testronaut 1.0.22 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -13,6 +13,10 @@ import { exec as execCmd } from 'child_process';
13
13
  import { promisify } from 'util';
14
14
  const exec = promisify(execCmd);
15
15
  import url from 'url';
16
+ import { ensureBrowsers } from '../tools/playwrightSetup.js';
17
+
18
+ // Keep PW browsers inside the project to avoid global cache skew
19
+ process.env.PLAYWRIGHT_BROWSERS_PATH = process.env.PLAYWRIGHT_BROWSERS_PATH || '0';
16
20
 
17
21
 
18
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -55,7 +59,26 @@ if (args.includes('--init')) {
55
59
  await initializeTestronautProject();
56
60
  await createWelcomeMission();
57
61
 
58
- console.log(`
62
+ const { default: inquirer } = await import('inquirer');
63
+ const { doPw } = await inquirer.prompt([
64
+ { type: 'confirm', name: 'doPw', message: 'Install Playwright browsers now?', default: true }
65
+ ]);
66
+
67
+ if (doPw) {
68
+ // ensure @playwright/test exists so the installer can pin to its version
69
+ const present = await hasAny(['@playwright/test', 'playwright']);
70
+ if (!present) {
71
+ console.log('📦 Installing @playwright/test …');
72
+ const pm = pkgManagerForCwd();
73
+ await installDev(pm, '@playwright/test');
74
+ console.log('✅ @playwright/test installed.');
75
+ }
76
+
77
+ // install browsers pinned to local version, project-local cache
78
+ await ensureBrowsers({ browser: 'chromium', withDeps: true });
79
+ }
80
+
81
+ console.log(`
59
82
  ✅ Project initialized!
60
83
 
61
84
  Next steps:
@@ -67,7 +90,6 @@ Next steps:
67
90
  📚 Docs: https://docs.testronaut.app/docs/guides/cli-auth
68
91
  `);
69
92
 
70
-
71
93
  process.exit(0);
72
94
  }
73
95
 
@@ -180,6 +202,94 @@ async function parseJsonSafe(res, urlLabel) {
180
202
  return data;
181
203
  }
182
204
 
205
+ function pkgManagerForCwd(cwd = process.cwd()) {
206
+ const has = (f) => fs.existsSync(path.join(cwd, f));
207
+ if (has('pnpm-lock.yaml')) return 'pnpm';
208
+ if (has('yarn.lock')) return 'yarn';
209
+ if (has('bun.lockb')) return 'bun';
210
+ return 'npm';
211
+ }
212
+
213
+ async function hasAny(modNames) {
214
+ try {
215
+ for (const m of modNames) {
216
+ // Use createRequire so ESM can resolve CJS packages relative to the project
217
+ const { createRequire } = await import('module');
218
+ const req = createRequire(path.join(process.cwd(), 'noop.js'));
219
+ req.resolve(m);
220
+ return m;
221
+ }
222
+ } catch {}
223
+ return null;
224
+ }
225
+
226
+ async function installDev(pkgMgr, pkg) {
227
+ const cmd =
228
+ pkgMgr === 'pnpm' ? `pnpm add -D ${pkg}` :
229
+ pkgMgr === 'yarn' ? `yarn add -D ${pkg}` :
230
+ pkgMgr === 'bun' ? `bun add -d ${pkg}` :
231
+ `npm i -D ${pkg}`;
232
+ await exec(cmd, { stdio: 'inherit' });
233
+ }
234
+
235
+ export async function ensurePlaywrightInstalled() {
236
+ // 1) Is playwright already present?
237
+ const present = await hasAny(['@playwright/test', 'playwright']);
238
+ if (!present) {
239
+ console.log('📦 @playwright/test not found. Installing…');
240
+ const pm = pkgManagerForCwd();
241
+ try {
242
+ await installDev(pm, '@playwright/test');
243
+ console.log('✅ @playwright/test installed.');
244
+ } catch (err) {
245
+ console.error('❌ Failed to install @playwright/test:', err?.message || err);
246
+ console.error(' Try installing manually and re-run init.');
247
+ return false;
248
+ }
249
+ }
250
+
251
+ // 2) Install browsers (skip in CI if desired)
252
+ if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1') {
253
+ console.log('⏭️ Skipping browser download (PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1).');
254
+ return true;
255
+ }
256
+
257
+ console.log('🧭 Ensuring Playwright browsers are installed…');
258
+
259
+ // Use npx with explicit package + -y to avoid prompts and PATH issues
260
+ // (works even if the local bin is not on PATH yet)
261
+ const installCmd = `npx -y playwright@latest install --with-deps`;
262
+
263
+ try {
264
+ await exec(installCmd, { stdio: 'inherit', env: process.env });
265
+ console.log('✅ Playwright browsers installed.');
266
+ return true;
267
+ } catch (err) {
268
+ // Some shells can’t find `playwright` even via npx. Fall back to package-runner:
269
+ // npm 7+: `npm exec`, yarn: `yarn playwright`, pnpm: `pnpm exec`
270
+ const pm = pkgManagerForCwd();
271
+ const fallback =
272
+ pm === 'pnpm' ? `pnpm exec playwright install --with-deps` :
273
+ pm === 'yarn' ? `yarn playwright install --with-deps` :
274
+ pm === 'bun' ? `bunx playwright install --with-deps` :
275
+ `npm exec --yes playwright@latest install --with-deps`;
276
+
277
+ console.log('⚠️ npx fallback:', err?.message || err);
278
+ console.log(`↩️ Retrying with: ${fallback}`);
279
+ try {
280
+ await exec(fallback, { stdio: 'inherit', env: process.env });
281
+ console.log('✅ Playwright browsers installed on retry.');
282
+ return true;
283
+ } catch (err2) {
284
+ console.error('❌ Failed to install Playwright browsers:', err2?.message || err2);
285
+ console.error(' Manual fix:');
286
+ console.error(' 1) npm i -D @playwright/test');
287
+ console.error(' 2) npx -y playwright@latest install --with-deps');
288
+ return false;
289
+ }
290
+ }
291
+ }
292
+
183
293
 
184
294
  // Function to handle login and store session token
185
295
  async function handleLogin() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testronaut",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "main": "./runner/testronaut.js",
5
5
  "bin": {
6
6
  "testronaut": "./bin/cli.js"
@@ -1,4 +1,15 @@
1
- import { chromium } from 'playwright';
1
+ // Keep browsers in project-local cache; matches ensureBrowsers()
2
+ process.env.PLAYWRIGHT_BROWSERS_PATH = process.env.PLAYWRIGHT_BROWSERS_PATH || '0';
3
+
4
+ // import { chromium } from 'playwright';
5
+ import { createRequire } from 'module';
6
+ const req = createRequire(process.cwd() + '/noop.js'); // resolve from user project
7
+ async function loadPlaywright() {
8
+ try { return req('playwright'); } catch {}
9
+ try { return req('@playwright/test'); } catch {}
10
+ try { return req('playwright-core'); } catch {}
11
+ throw new Error('Playwright is not installed in this project. Run: npm i -D @playwright/test');
12
+ }
2
13
  import * as cheerio from 'cheerio';
3
14
  import {
4
15
  chunkBySection,
@@ -8,6 +19,7 @@ import {
8
19
  removeObfuscatedClassNames,
9
20
  } from './domControl.js';
10
21
  import fs from 'fs';
22
+ import { ensureBrowsers } from '../tools/playwrightSetup.js';
11
23
 
12
24
  export class ChromeBrowser {
13
25
  constructor() {
@@ -68,7 +80,27 @@ export class ChromeBrowser {
68
80
  }
69
81
 
70
82
  async start() {
71
- this.browser = await chromium.launch({ headless: true });
83
+ try {
84
+ const pw = await loadPlaywright();
85
+ this.browser = await pw.chromium.launch({ headless: true });
86
+ } catch (err) {
87
+ const msg = String(err?.message || err);
88
+ const needsInstall =
89
+ msg.includes('Executable doesn\'t exist') ||
90
+ msg.includes('was just installed or updated') ||
91
+ msg.includes('looks like Playwright') ||
92
+ msg.includes('browserType.launch');
93
+
94
+ if (!needsInstall) throw err;
95
+
96
+ // Download the right browsers and retry once
97
+ console.log('🧩 Playwright browsers missing or stale. Installing…');
98
+ await ensureBrowsers({ browser: 'chromium', withDeps: true });
99
+
100
+ // Retry
101
+ const pw = await loadPlaywright();
102
+ return await pw.chromium.launch({ headless: true });
103
+ }
72
104
  this.context = await this.browser.newContext();
73
105
  const first = await this.context.newPage();
74
106
  const id = this._addAndFocus(first);
@@ -0,0 +1,70 @@
1
+ import { createRequire } from 'module';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { promisify } from 'util';
5
+ import { exec as execCb } from 'child_process';
6
+
7
+ const exec = promisify(execCb);
8
+
9
+ // Resolve from the **user project** (cwd)
10
+ const req = createRequire(path.join(process.cwd(), 'noop.js'));
11
+
12
+ // Prefer 'playwright' → '@playwright/test' → 'playwright-core' from the USER PROJECT (cwd)
13
+ function getPlaywrightVersion() {
14
+ console.log('🔍 Detecting installed Playwright version (from project)…');
15
+ const tryPkg = (name) => {
16
+ try {
17
+ const p = req.resolve(`${name}/package.json`);
18
+ const v = JSON.parse(fs.readFileSync(p, 'utf8')).version;
19
+ console.log(` • found ${name}@${v} → ${p}`);
20
+ return v;
21
+ } catch {
22
+ return null;
23
+ }
24
+ };
25
+ return (
26
+ tryPkg('playwright') ||
27
+ tryPkg('@playwright/test') ||
28
+ tryPkg('playwright-core') ||
29
+ 'latest'
30
+ );
31
+ }
32
+
33
+ function pkgManagerForCwd(cwd = process.cwd()) {
34
+ const has = (f) => fs.existsSync(path.join(cwd, f));
35
+ if (has('pnpm-lock.yaml')) return 'pnpm';
36
+ if (has('yarn.lock')) return 'yarn';
37
+ if (has('bun.lockb')) return 'bun';
38
+ return 'npm';
39
+ }
40
+
41
+ async function run(cmd, extraEnv = {}) {
42
+ console.log(`\n> ${cmd}`);
43
+ await exec(cmd, { stdio: 'inherit', env: { ...process.env, ...extraEnv } });
44
+ }
45
+
46
+ export async function ensureBrowsers({ browser = 'chromium', withDeps = true } = {}) {
47
+ if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1') return;
48
+
49
+ const version = getPlaywrightVersion();
50
+ const withDepsFlag = withDeps ? ' --with-deps' : '';
51
+ const localCacheEnv = { PLAYWRIGHT_BROWSERS_PATH: '0' }; // project-local cache
52
+
53
+ const npxCmd = `npx -y playwright@${version} install ${browser}${withDepsFlag}`;
54
+ try {
55
+ await run(npxCmd, localCacheEnv);
56
+ } catch {
57
+ const pm =
58
+ fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml')) ? 'pnpm' :
59
+ fs.existsSync(path.join(process.cwd(), 'yarn.lock')) ? 'yarn' :
60
+ fs.existsSync(path.join(process.cwd(), 'bun.lockb')) ? 'bun' : 'npm';
61
+
62
+ const fallback =
63
+ pm === 'pnpm' ? `pnpm exec playwright install ${browser}${withDepsFlag}` :
64
+ pm === 'yarn' ? `yarn playwright install ${browser}${withDepsFlag}` :
65
+ pm === 'bun' ? `bunx playwright install ${browser}${withDepsFlag}` :
66
+ `npm exec --yes playwright@${version} install ${browser}${withDepsFlag}`;
67
+
68
+ await run(fallback, localCacheEnv);
69
+ }
70
+ }