interactive-playwright.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env npx tsx
  2. /**
  3. * Interactive Playwright launcher for the Cline VS Code extension.
  4. *
  5. * Overview:
  6. * - Starts the mock Cline API server (from the e2e test fixtures).
  7. * - Downloads a stable build of VS Code (via @vscode/test-electron).
  8. * - Creates a temporary VS Code user profile directory.
  9. * - Installs and links the Cline extension (from dist/e2e.vsix and the dev path).
  10. * - Opens a test workspace and automatically reveals the Cline sidebar.
  11. * - Records **all gRPC calls** during the session for later inspection.
  12. * - Keeps VS Code running for manual interactive testing until the window is closed or Ctrl+C is pressed.
  13. * - Cleans up all resources (mock server, temp profile, Electron process) on exit.
  14. *
  15. * Usage:
  16. * 1. (Optional) Build and install the e2e extension:
  17. * npm run test:e2e:build
  18. *
  19. * 2. From the repo root, start the interactive session:
  20. * npm run test:playwright:interactive
  21. *
  22. * 3. VS Code will launch with the Cline extension loaded and gRPC recording enabled.
  23. *
  24. * 4. Interact with the extension manually.
  25. *
  26. * 5. Close the VS Code window or press Ctrl+C to end the session and trigger cleanup.
  27. */
  28. import { downloadAndUnzipVSCode, SilentReporter } from "@vscode/test-electron"
  29. import { mkdtempSync } from "fs"
  30. import os from "os"
  31. import path from "path"
  32. import { _electron } from "playwright"
  33. import { ClineApiServerMock } from "../src/test/e2e/fixtures/server"
  34. import { E2ETestHelper } from "../src/test/e2e/utils/helpers"
  35. async function main() {
  36. await ClineApiServerMock.startGlobalServer()
  37. const userDataDir = mkdtempSync(path.join(os.tmpdir(), "vsce-interactive"))
  38. const executablePath = await downloadAndUnzipVSCode("stable", undefined, new SilentReporter())
  39. // launch VSCode
  40. const app = await _electron.launch({
  41. executablePath,
  42. env: {
  43. ...process.env,
  44. TEMP_PROFILE: "true",
  45. E2E_TEST: "true",
  46. CLINE_ENVIRONMENT: "local",
  47. GRPC_RECORDER_ENABLED: "true",
  48. GRPC_RECORDER_TESTS_FILTERS_ENABLED: "true",
  49. },
  50. args: [
  51. "--no-sandbox",
  52. "--disable-updates",
  53. "--disable-workspace-trust",
  54. "--disable-extensions",
  55. "--skip-welcome",
  56. "--skip-release-notes",
  57. `--user-data-dir=${userDataDir}`,
  58. `--install-extension=${path.join(E2ETestHelper.CODEBASE_ROOT_DIR, "dist", "e2e.vsix")}`,
  59. `--extensionDevelopmentPath=${E2ETestHelper.CODEBASE_ROOT_DIR}`,
  60. path.join(E2ETestHelper.E2E_TESTS_DIR, "fixtures", "workspace"),
  61. ],
  62. })
  63. const page = await app.firstWindow()
  64. await E2ETestHelper.openClineSidebar(page)
  65. console.log("VSCode with Cline extension is now running!")
  66. console.log(`Temporary data directory on: ${userDataDir}`)
  67. console.log("You can manually interact with the extension.")
  68. console.log("Press Ctrl+C to close when done.")
  69. async function teardown() {
  70. console.log("Cleaning up resources...")
  71. try {
  72. await app?.close()
  73. await ClineApiServerMock.stopGlobalServer?.()
  74. await E2ETestHelper.rmForRetries(userDataDir, { recursive: true })
  75. } catch (e) {
  76. console.log(`We could teardown interactive playwright properly, error:${e}`)
  77. }
  78. console.log("Finished cleaning up resources...")
  79. }
  80. process.on("SIGINT", async () => {
  81. await teardown()
  82. process.exit(0)
  83. })
  84. process.on("SIGTERM", async () => {
  85. await teardown()
  86. process.exit(0)
  87. })
  88. const win = await app.firstWindow()
  89. win.on("close", async () => {
  90. console.log("VS Code window closed.")
  91. await teardown()
  92. process.exit(0)
  93. })
  94. process.stdin.resume()
  95. }
  96. main().catch((err) => {
  97. console.error("Failed to start:", err)
  98. process.exit(1)
  99. })