# Chrome E2E Testing Skill

Run end-to-end tests through the user's real Chrome browser session with all their existing logins (cookies, OAuth sessions, etc.).

## Prerequisites

- `puppeteer-core` installed in workspace (`npm install puppeteer-core` in `~/.skykoi/workspace`)
- Chrome installed at `C:\Program Files\Google\Chrome\Application\chrome.exe` (Windows) or `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` (macOS)
- User must be logged into the target sites in their normal Chrome session

## How It Works

1. **Kill existing Chrome** (saves/restores session via `--restore-last-session`)
2. **Relaunch Chrome** with `--remote-debugging-port=9222` using the user's real profile
3. **Connect via puppeteer-core** using CDP websocket
4. **Drive the browser** — navigate, click, type, screenshot, assert
5. **User's cookies/sessions are intact** — no login needed

## Launch Chrome

```powershell
# Windows
taskkill /F /IM chrome.exe 2>$null
Start-Sleep 3
Start-Process "C:\Program Files\Google\Chrome\Application\chrome.exe" -ArgumentList "--remote-debugging-port=9222","--user-data-dir=$env:LOCALAPPDATA\Google\Chrome\User Data","--profile-directory=Default","--restore-last-session"
Start-Sleep 5
```

```bash
# macOS
pkill -f "Google Chrome" || true
sleep 3
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --remote-debugging-port=9222 \
  --user-data-dir="$HOME/Library/Application Support/Google/Chrome" \
  --profile-directory=Default \
  --restore-last-session &
sleep 5
```

## Verify Debug Port

```powershell
(Invoke-WebRequest -Uri "http://127.0.0.1:9222/json/version" -UseBasicParsing -TimeoutSec 3).Content
```

## Connect with Puppeteer

```javascript
import puppeteer from "puppeteer-core";

const resp = await fetch("http://127.0.0.1:9222/json/version");
const { webSocketDebuggerUrl } = await resp.json();
const browser = await puppeteer.connect({
  browserWSEndpoint: webSocketDebuggerUrl,
  defaultViewport: null,
});
const pages = await browser.pages();
// Find your target tab
let page = pages.find((p) => p.url().includes("example.com"));
```

## Typing into React Inputs

React controlled inputs ignore `page.type()` and `Input.insertText`. Use the React fiber trick:

```javascript
await page.evaluate(
  (selector, value) => {
    const input = document.querySelector(selector);
    const propsKey = Object.keys(input).find((k) => k.startsWith("__reactProps$"));
    if (propsKey) {
      const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
      nativeSetter.call(input, value);
      input.dispatchEvent(new Event("input", { bubbles: true }));
      input[propsKey].onChange({ target: input, currentTarget: input });
    }
  },
  "#myInput",
  "my value",
);
```

## Clerk Auth Notes

- If user is already logged in via Chrome cookies, Clerk session persists — no login needed
- If session expired, user must log in manually once (OAuth is easiest)
- Clerk's `signIn.create()` API can be called from console but 2FA blocks automation
- Test accounts with 2FA cannot be automated without TOTP secret

## Anti-False-Positive Testing

When verifying responses, use a **nonce** to prevent matching stale content:

```javascript
const nonce = `NONCE_${Date.now()}`;
const testMsg = `Say exactly: e2e_ok_${nonce}`;
// ... send message ...
// Wait for response containing the nonce — guarantees it's fresh
const hasNonce = await page.evaluate((n) => document.body.innerText.includes("e2e_ok_" + n), nonce);
```

## Retrieving Environment Variables

### Vercel

```javascript
// Navigate to project settings > env vars
await page.goto("https://vercel.com/<team>/<project>/settings/environment-variables");
// Extract all env var names and values (values need "Show" click)
```

### Generic Pattern

Use the authenticated Chrome session to visit any dashboard and scrape values — AWS Console, Clerk Dashboard, etc.

## Screenshots

```javascript
await page.screenshot({ path: "step-name.png", fullPage: false });
```

Note: The gateway's `read` tool may fail on PNGs if `sharp` is missing. Convert to base64 or use `.jpg` via System.Drawing on Windows.

## Important Caveats

- **Chrome must be killed first** — only one instance can use a profile at a time
- **User loses their Chrome while test runs** — warn them, restore after
- **Sessions may expire** — if auth fails, user needs to log in once manually
- **Don't leave debug port open** — security risk. Kill Chrome after test or relaunch without `--remote-debugging-port`
- **Always disconnect puppeteer** (`browser.disconnect()`) — don't close the browser

## File Locations

- Workspace scripts: `~/.skykoi/workspace/e2e-*.mjs`
- Screenshots: `~/.skykoi/workspace/e2e-shots/`
