Files
cline/apps/vscode/scripts/interactive-playwright.ts
Dominic Cooney 159951f82d ci(vscode): make the e2e job spend its time on the tests (#14607)
* ci(vscode): make the e2e job spend its time on the tests

Windows e2e runs hit the 20-minute limit after their tests had passed:
the job was saving a multi-gigabyte bun cache that only that pull request
could read. The job also repeated the type check and lint that Quality
Checks already runs, and downloaded Playwright browsers the tests never
start.

- Restore the bun cache on every run and save it only from main, right
  after the install, so a later test failure keeps it.
- Build the extension with build:production and load it as the
  extension under development. The tests already ran that copy; the VSIX
  they also installed was never used, and packaging it ran
  vscode:prepublish's checks.
- Install only Playwright's ffmpeg, which it records test videos with.
  The tests launch VS Code through Electron, not a Playwright browser.
- Let runs on main finish instead of cancelling them on the next merge.
- Raise the timeout to 30 minutes for the run on main that installs
  without a cache and saves a new one.

* ci(vscode): make the e2e workflow comments match its behavior

A run on main that is still waiting to start is replaced by the next merge; only a run that has started always finishes. The comments now say so, and name the directory VS Code loads the extension from.

* ci(vscode): run Quality Checks when the Playwright config changes

The e2e build no longer runs the type check and lint, and
playwright.config.ts was the one lintable e2e input outside Quality
Checks' path filter. A change to only that file could pass CI unlinted.

* ci(vscode): stop caching Bun's install cache in e2e

Restoring the cache and installing took about as long as an uncached
`bun install` in ext-vscode-test's jobs on the same commits: 3:48-4:31
against 2:39-5:06 on Windows, and 25s against 23s on Ubuntu. With no
cache to save, a run on main can be cancelled by the next merge again
and the job fits main's 20-minute timeout, so concurrency and the
timeout go back to main's settings.

The e2e path filter also drops apps/vscode/.vscodeignore: test:e2e no
longer packages a VSIX, so nothing in the job reads that file.
2026-09-29 21:18:37 +02:00

112 lines
3.4 KiB
TypeScript

#!/usr/bin/env bun
/**
* Interactive Playwright launcher for the Cline VS Code extension.
*
* Overview:
* - Starts the mock Cline API server (from the e2e test fixtures).
* - Downloads a stable build of VS Code (via @vscode/test-electron).
* - Creates a temporary VS Code user profile directory.
* - Loads the Cline extension from apps/vscode as an extension under development.
* - Opens a test workspace and automatically reveals the Cline sidebar.
* - Records **all gRPC calls** during the session for later inspection.
* - Keeps VS Code running for manual interactive testing until the window is closed or Ctrl+C is pressed.
* - Cleans up all resources (mock server, temp profile, Electron process) on exit.
*
* Usage:
* 1. (Optional) Build the extension:
* bun run build:production
*
* 2. From the repo root, start the interactive session:
* bun run test:e2e:ui
*
* 3. VS Code will launch with the Cline extension loaded and gRPC recording enabled.
*
* 4. Interact with the extension manually.
*
* 5. Close the VS Code window or press Ctrl+C to end the session and trigger cleanup.
*/
import { downloadAndUnzipVSCode, SilentReporter } from "@vscode/test-electron"
import { mkdtempSync } from "fs"
import os from "os"
import path from "path"
import { _electron } from "playwright"
import { ClineApiServerMock } from "../src/test/e2e/fixtures/server"
import { E2ETestHelper } from "../src/test/e2e/utils/helpers"
async function main() {
await ClineApiServerMock.startGlobalServer()
const userDataDir = mkdtempSync(path.join(os.tmpdir(), "vsce-interactive"))
const executablePath = await downloadAndUnzipVSCode("stable", undefined, new SilentReporter())
// launch VSCode
const app = await _electron.launch({
executablePath,
env: {
...process.env,
TEMP_PROFILE: "true",
E2E_TEST: "true",
CLINE_ENVIRONMENT: "local",
GRPC_RECORDER_ENABLED: "true",
GRPC_RECORDER_TESTS_FILTERS_ENABLED: "true",
},
args: [
"--no-sandbox",
"--disable-updates",
"--disable-workspace-trust",
"--disable-extensions",
"--skip-welcome",
"--skip-release-notes",
`--user-data-dir=${userDataDir}`,
`--extensionDevelopmentPath=${E2ETestHelper.CODEBASE_ROOT_DIR}`,
path.join(E2ETestHelper.E2E_TESTS_DIR, "fixtures", "workspace"),
],
})
const page = await app.firstWindow()
await E2ETestHelper.openClineSidebar(page)
console.log("VSCode with Cline extension is now running!")
console.log(`Temporary data directory on: ${userDataDir}`)
console.log("You can manually interact with the extension.")
console.log("Press Ctrl+C to close when done.")
async function teardown() {
console.log("Cleaning up resources...")
try {
await app?.close()
await ClineApiServerMock.stopGlobalServer?.()
await E2ETestHelper.rmForRetries(userDataDir, { recursive: true })
} catch (e) {
console.log(`We could teardown interactive playwright properly, error:${e}`)
}
console.log("Finished cleaning up resources...")
}
process.on("SIGINT", async () => {
await teardown()
process.exit(0)
})
process.on("SIGTERM", async () => {
await teardown()
process.exit(0)
})
const win = await app.firstWindow()
win.on("close", async () => {
console.log("VS Code window closed.")
await teardown()
process.exit(0)
})
process.stdin.resume()
}
main().catch((err) => {
console.error("Failed to start:", err)
process.exit(1)
})