@playwright-repl/runner 0.21.2
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/README.md +116 -0
- package/dist/args.d.ts +16 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +48 -0
- package/dist/args.js.map +1 -0
- package/dist/bridge-utils.cjs +197 -0
- package/dist/bridge-utils.cjs.map +1 -0
- package/dist/bridge-utils.d.cts +30 -0
- package/dist/bridge-utils.d.cts.map +1 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +40 -0
- package/dist/browser.js.map +1 -0
- package/dist/bundler.d.ts +6 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +32 -0
- package/dist/bundler.js.map +1 -0
- package/dist/cdpPreload.cjs +44 -0
- package/dist/cdpPreload.cjs.map +1 -0
- package/dist/cdpPreload.d.cts +11 -0
- package/dist/cdpPreload.d.cts.map +1 -0
- package/dist/chrome-extension/background.js +233485 -0
- package/dist/chrome-extension/background.js.map +1 -0
- package/dist/chrome-extension/content/picker.js +279 -0
- package/dist/chrome-extension/content/recorder.js +475 -0
- package/dist/chrome-extension/devtools/console.html +17 -0
- package/dist/chrome-extension/devtools/console.js +28 -0
- package/dist/chrome-extension/devtools/console.js.map +1 -0
- package/dist/chrome-extension/devtools/devtools.html +8 -0
- package/dist/chrome-extension/devtools/devtools.js +7 -0
- package/dist/chrome-extension/devtools/devtools.js.map +1 -0
- package/dist/chrome-extension/icons/dramaturg_icon_128.png +0 -0
- package/dist/chrome-extension/icons/dramaturg_icon_16.png +0 -0
- package/dist/chrome-extension/icons/dramaturg_icon_32.png +0 -0
- package/dist/chrome-extension/icons/dramaturg_icon_48.png +0 -0
- package/dist/chrome-extension/index.css +1333 -0
- package/dist/chrome-extension/index.js +12462 -0
- package/dist/chrome-extension/index.js.map +1 -0
- package/dist/chrome-extension/index2.js +27327 -0
- package/dist/chrome-extension/index2.js.map +1 -0
- package/dist/chrome-extension/manifest.json +46 -0
- package/dist/chrome-extension/modulepreload-polyfill.js +30 -0
- package/dist/chrome-extension/modulepreload-polyfill.js.map +1 -0
- package/dist/chrome-extension/newtab/newtab.html +202 -0
- package/dist/chrome-extension/offscreen/offscreen.html +6 -0
- package/dist/chrome-extension/offscreen/offscreen.js +54 -0
- package/dist/chrome-extension/offscreen/offscreen.js.map +1 -0
- package/dist/chrome-extension/panel/panel.html +16 -0
- package/dist/chrome-extension/panel/panel.js +2210 -0
- package/dist/chrome-extension/panel/panel.js.map +1 -0
- package/dist/chrome-extension/preferences/preferences.html +14 -0
- package/dist/chrome-extension/preferences/preferences.js +102 -0
- package/dist/chrome-extension/preferences/preferences.js.map +1 -0
- package/dist/chrome-extension/settings.js +13 -0
- package/dist/chrome-extension/settings.js.map +1 -0
- package/dist/chrome-extension/sw-debugger-core.js +1127 -0
- package/dist/chrome-extension/sw-debugger-core.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +43 -0
- package/dist/cli.js.map +1 -0
- package/dist/compiler/classify.d.ts +13 -0
- package/dist/compiler/classify.d.ts.map +1 -0
- package/dist/compiler/classify.js +143 -0
- package/dist/compiler/classify.js.map +1 -0
- package/dist/compiler/index.d.ts +24 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +41 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/compiler/parser.d.ts +24 -0
- package/dist/compiler/parser.d.ts.map +1 -0
- package/dist/compiler/parser.js +77 -0
- package/dist/compiler/parser.js.map +1 -0
- package/dist/compiler/transform.d.ts +20 -0
- package/dist/compiler/transform.d.ts.map +1 -0
- package/dist/compiler/transform.js +36 -0
- package/dist/compiler/transform.js.map +1 -0
- package/dist/compiler.d.ts +24 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +129 -0
- package/dist/compiler.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -0
- package/dist/discover.d.ts +5 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +46 -0
- package/dist/discover.js.map +1 -0
- package/dist/execute.d.ts +13 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +281 -0
- package/dist/execute.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/mode-detect.d.ts +21 -0
- package/dist/mode-detect.d.ts.map +1 -0
- package/dist/mode-detect.js +67 -0
- package/dist/mode-detect.js.map +1 -0
- package/dist/proxy-page.d.ts +32 -0
- package/dist/proxy-page.d.ts.map +1 -0
- package/dist/proxy-page.js +192 -0
- package/dist/proxy-page.js.map +1 -0
- package/dist/pw-cli.d.ts +12 -0
- package/dist/pw-cli.d.ts.map +1 -0
- package/dist/pw-cli.js +73 -0
- package/dist/pw-cli.js.map +1 -0
- package/dist/pw-launch.d.ts +15 -0
- package/dist/pw-launch.d.ts.map +1 -0
- package/dist/pw-launch.js +97 -0
- package/dist/pw-launch.js.map +1 -0
- package/dist/pw-preload.cjs +164 -0
- package/dist/pw-preload.cjs.map +1 -0
- package/dist/pw-preload.d.cts +15 -0
- package/dist/pw-preload.d.cts.map +1 -0
- package/dist/pw-repl-extension.d.ts +12 -0
- package/dist/pw-repl-extension.d.ts.map +1 -0
- package/dist/pw-repl-extension.js +100 -0
- package/dist/pw-repl-extension.js.map +1 -0
- package/dist/pw-repl.d.ts +12 -0
- package/dist/pw-repl.d.ts.map +1 -0
- package/dist/pw-repl.js +111 -0
- package/dist/pw-repl.js.map +1 -0
- package/dist/pw-worker.cjs +216 -0
- package/dist/pw-worker.cjs.map +1 -0
- package/dist/pw-worker.d.cts +13 -0
- package/dist/pw-worker.d.cts.map +1 -0
- package/dist/pw-worker.d.ts +11 -0
- package/dist/pw-worker.d.ts.map +1 -0
- package/dist/pw-worker.js +51 -0
- package/dist/pw-worker.js.map +1 -0
- package/dist/run-test.d.ts +22 -0
- package/dist/run-test.d.ts.map +1 -0
- package/dist/run-test.js +358 -0
- package/dist/run-test.js.map +1 -0
- package/dist/runner.d.ts +10 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +82 -0
- package/dist/runner.js.map +1 -0
- package/dist/shim/alias.d.ts +11 -0
- package/dist/shim/alias.d.ts.map +1 -0
- package/dist/shim/alias.js +24 -0
- package/dist/shim/alias.js.map +1 -0
- package/dist/shim/framework.d.ts +11 -0
- package/dist/shim/framework.d.ts.map +1 -0
- package/dist/shim/framework.js +11 -0
- package/dist/shim/framework.js.map +1 -0
- package/dist/shim/test-runner-node.d.ts +49 -0
- package/dist/shim/test-runner-node.d.ts.map +1 -0
- package/dist/shim/test-runner-node.js +191 -0
- package/dist/shim/test-runner-node.js.map +1 -0
- package/dist/shim/test-runner.d.ts +44 -0
- package/dist/shim/test-runner.d.ts.map +1 -0
- package/dist/shim/test-runner.js +181 -0
- package/dist/shim/test-runner.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/dist/execute.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute a test file via two paths:
|
|
3
|
+
*
|
|
4
|
+
* 1. Browser path (default): compile → send to bridge → runs in service worker
|
|
5
|
+
* where page/expect are real Playwright objects. Zero bridge round-trips.
|
|
6
|
+
*
|
|
7
|
+
* 2. Node.js path: compile → run locally → page calls go through Proxy → bridge.
|
|
8
|
+
* Used when test imports Node.js APIs (fs, path, http, etc.)
|
|
9
|
+
*/
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { expect as pwExpect } from '@playwright/test';
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
// ─── Alias path (resolved once) ────────────────────────────────────────────
|
|
17
|
+
let _aliasPath = null;
|
|
18
|
+
function getAliasPath() {
|
|
19
|
+
if (_aliasPath)
|
|
20
|
+
return _aliasPath;
|
|
21
|
+
_aliasPath = path.resolve(path.dirname(__filename), 'shim/alias.ts');
|
|
22
|
+
if (!fs.existsSync(_aliasPath))
|
|
23
|
+
_aliasPath = _aliasPath.replace('.ts', '.js');
|
|
24
|
+
return _aliasPath;
|
|
25
|
+
}
|
|
26
|
+
// Browser framework is pre-loaded in the extension's service worker
|
|
27
|
+
// (packages/extension/src/test-framework.ts). No setup needed here.
|
|
28
|
+
// ─── Execute (auto-detect path) ────────────────────────────────────────────
|
|
29
|
+
export async function executeTestFile(testFilePath, bridge, opts, nodePage, cdpPage) {
|
|
30
|
+
const needsNode = opts.forceNode || await detectNodeAPIs(testFilePath);
|
|
31
|
+
console.log(` [path] ${path.basename(testFilePath)} → ${needsNode ? 'NODE' : 'BROWSER'}`);
|
|
32
|
+
if (needsNode) {
|
|
33
|
+
return executeNode(testFilePath, bridge, opts.grep, nodePage, cdpPage);
|
|
34
|
+
}
|
|
35
|
+
return executeBrowser(testFilePath, bridge, opts.grep);
|
|
36
|
+
}
|
|
37
|
+
// ─── Browser Path ──────────────────────────────────────────────────────────
|
|
38
|
+
async function executeBrowser(testFilePath, bridge, grep) {
|
|
39
|
+
// Set grep in browser framework
|
|
40
|
+
if (grep)
|
|
41
|
+
await bridge.run(`globalThis.__setGrep(${JSON.stringify(grep)})`);
|
|
42
|
+
else
|
|
43
|
+
await bridge.run(`globalThis.__setGrep(null)`);
|
|
44
|
+
const compiled = await compileBrowser(testFilePath);
|
|
45
|
+
// Send compiled test to bridge — runs in SW with real page/expect
|
|
46
|
+
const r = await bridge.run(compiled);
|
|
47
|
+
if (r.isError)
|
|
48
|
+
throw new Error(r.text || 'Bridge error');
|
|
49
|
+
return parseResults(r.text || '', testFilePath);
|
|
50
|
+
}
|
|
51
|
+
async function compileBrowser(testFilePath) {
|
|
52
|
+
const esbuild = await import('esbuild');
|
|
53
|
+
const testDir = path.dirname(testFilePath);
|
|
54
|
+
const testFileName = path.basename(testFilePath);
|
|
55
|
+
const plugin = {
|
|
56
|
+
name: 'pw-browser',
|
|
57
|
+
setup(build) {
|
|
58
|
+
build.onResolve({ filter: /^__entry__$/ }, () => ({ path: '__entry__', namespace: 'entry' }));
|
|
59
|
+
build.onLoad({ filter: /.*/, namespace: 'entry' }, () => ({
|
|
60
|
+
contents: `
|
|
61
|
+
import './${testFileName}';
|
|
62
|
+
`,
|
|
63
|
+
resolveDir: testDir,
|
|
64
|
+
loader: 'ts',
|
|
65
|
+
}));
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const result = await esbuild.build({
|
|
69
|
+
entryPoints: ['__entry__'],
|
|
70
|
+
bundle: true, write: false, format: 'iife', platform: 'neutral',
|
|
71
|
+
plugins: [plugin],
|
|
72
|
+
alias: { '@playwright/test': getAliasPath() },
|
|
73
|
+
});
|
|
74
|
+
// Wrap: reset state → run test registration → run tests → return result
|
|
75
|
+
const testCode = result.outputFiles[0].text;
|
|
76
|
+
return `
|
|
77
|
+
globalThis.__resetTestState();
|
|
78
|
+
${testCode}
|
|
79
|
+
await globalThis.__runTests();
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
// ─── Node.js Path ──────────────────────────────────────────────────────────
|
|
83
|
+
async function executeNode(testFilePath, bridge, grep, nodePage, cdpPage) {
|
|
84
|
+
console.log(` [node] DIRECT mode`);
|
|
85
|
+
const page = cdpPage || nodePage;
|
|
86
|
+
const expect = pwExpect;
|
|
87
|
+
// Collect registered tests
|
|
88
|
+
const tests = [];
|
|
89
|
+
const hooks = {
|
|
90
|
+
beforeEach: [], afterEach: [], beforeAll: [], afterAll: [],
|
|
91
|
+
};
|
|
92
|
+
// Provide test/expect on globalThis for the compiled test file
|
|
93
|
+
const grepRe = grep ? new RegExp(grep, 'i') : null;
|
|
94
|
+
const testFn = (name, fn) => { tests.push({ name, fn, skip: false }); };
|
|
95
|
+
testFn.only = testFn;
|
|
96
|
+
testFn.skip = (nameOrCond, fn) => {
|
|
97
|
+
if (typeof nameOrCond === 'string')
|
|
98
|
+
tests.push({ name: nameOrCond, fn, skip: true });
|
|
99
|
+
};
|
|
100
|
+
testFn.describe = (name, fn) => {
|
|
101
|
+
const prefix = name;
|
|
102
|
+
const origTest = globalThis.__test;
|
|
103
|
+
const wrappedTest = (n, f) => { tests.push({ name: `${prefix} > ${n}`, fn: f, skip: false }); };
|
|
104
|
+
wrappedTest.skip = (nOrC, f) => {
|
|
105
|
+
if (typeof nOrC === 'string')
|
|
106
|
+
tests.push({ name: `${prefix} > ${nOrC}`, fn: f, skip: true });
|
|
107
|
+
};
|
|
108
|
+
wrappedTest.only = wrappedTest;
|
|
109
|
+
globalThis.__test = wrappedTest;
|
|
110
|
+
fn();
|
|
111
|
+
globalThis.__test = origTest;
|
|
112
|
+
};
|
|
113
|
+
testFn.describe.configure = () => { };
|
|
114
|
+
testFn.beforeEach = (fn) => { hooks.beforeEach.push(fn); };
|
|
115
|
+
testFn.afterEach = (fn) => { hooks.afterEach.push(fn); };
|
|
116
|
+
testFn.beforeAll = (fn) => { hooks.beforeAll.push(fn); };
|
|
117
|
+
testFn.afterAll = (fn) => { hooks.afterAll.push(fn); };
|
|
118
|
+
testFn.fixme = (condOrName, fn) => {
|
|
119
|
+
if (typeof condOrName === 'string')
|
|
120
|
+
tests.push({ name: condOrName, fn, skip: true });
|
|
121
|
+
};
|
|
122
|
+
testFn.slow = () => { };
|
|
123
|
+
testFn.info = () => ({ annotations: [] });
|
|
124
|
+
testFn.extend = (fixtures) => {
|
|
125
|
+
// Return a test function that applies extended fixtures
|
|
126
|
+
const extended = (name, fn) => {
|
|
127
|
+
testFn(name, async (baseFixtures) => {
|
|
128
|
+
const ext = { ...baseFixtures };
|
|
129
|
+
for (const [key, fixtureFn] of Object.entries(fixtures)) {
|
|
130
|
+
if (typeof fixtureFn === 'function') {
|
|
131
|
+
await new Promise((resolve, reject) => {
|
|
132
|
+
Promise.resolve(fixtureFn({ ...ext }, async (value) => { ext[key] = value; resolve(); })).catch(reject);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
await fn(ext);
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
// Copy methods
|
|
140
|
+
for (const k of Object.keys(testFn))
|
|
141
|
+
extended[k] = testFn[k];
|
|
142
|
+
return extended;
|
|
143
|
+
};
|
|
144
|
+
globalThis.__test = testFn;
|
|
145
|
+
globalThis.__expect = expect;
|
|
146
|
+
// Compile and import (registers tests, doesn't run them)
|
|
147
|
+
const compiled = await compileNode(testFilePath);
|
|
148
|
+
const tmpFile = path.join(os.tmpdir(), `pw-test-${Date.now()}.mjs`);
|
|
149
|
+
try {
|
|
150
|
+
fs.writeFileSync(tmpFile, compiled);
|
|
151
|
+
await import(`file://${tmpFile.replace(/\\/g, '/')}`);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
try {
|
|
155
|
+
fs.unlinkSync(tmpFile);
|
|
156
|
+
}
|
|
157
|
+
catch { /* ignore */ }
|
|
158
|
+
}
|
|
159
|
+
// Run tests from Node side with real page
|
|
160
|
+
let bridgeCallCount = 0;
|
|
161
|
+
const origRun = bridge.run.bind(bridge);
|
|
162
|
+
bridge.run = async (cmd, opts) => { bridgeCallCount++; return origRun(cmd, opts); };
|
|
163
|
+
const results = [];
|
|
164
|
+
const fixtures = { page, context: page.context(), expect };
|
|
165
|
+
for (const hook of hooks.beforeAll)
|
|
166
|
+
await hook(fixtures);
|
|
167
|
+
for (const t of tests) {
|
|
168
|
+
if (t.skip || (grepRe && !grepRe.test(t.name))) {
|
|
169
|
+
results.push({ name: t.name, file: testFilePath, passed: true, skipped: true, duration: 0 });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const start = Date.now();
|
|
173
|
+
try {
|
|
174
|
+
for (const fn of hooks.beforeEach)
|
|
175
|
+
await fn(fixtures);
|
|
176
|
+
await t.fn(fixtures);
|
|
177
|
+
for (const fn of hooks.afterEach)
|
|
178
|
+
await fn(fixtures);
|
|
179
|
+
results.push({ name: t.name, file: testFilePath, passed: true, skipped: false, duration: Date.now() - start });
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
const msg = err.message || String(err);
|
|
183
|
+
console.error(` [FAIL] ${t.name}\n ${msg.split('\n').slice(0, 3).join('\n ')}`);
|
|
184
|
+
results.push({ name: t.name, file: testFilePath, passed: false, skipped: false, error: msg, duration: Date.now() - start });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (const hook of hooks.afterAll)
|
|
188
|
+
await hook(fixtures);
|
|
189
|
+
console.log(` [node] bridge calls: ${bridgeCallCount}`);
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
async function compileNode(testFilePath) {
|
|
193
|
+
const esbuild = await import('esbuild');
|
|
194
|
+
const testDir = path.dirname(testFilePath);
|
|
195
|
+
const testFileName = path.basename(testFilePath);
|
|
196
|
+
// Node path: no compiler, no bridge. Just bundle TS → JS.
|
|
197
|
+
const plugin = {
|
|
198
|
+
name: 'pw-node',
|
|
199
|
+
setup(build) {
|
|
200
|
+
build.onResolve({ filter: /^__entry__$/ }, () => ({ path: '__entry__', namespace: 'entry' }));
|
|
201
|
+
build.onLoad({ filter: /.*/, namespace: 'entry' }, () => ({
|
|
202
|
+
contents: `import './${testFileName}';`,
|
|
203
|
+
resolveDir: testDir,
|
|
204
|
+
loader: 'ts',
|
|
205
|
+
}));
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
const result = await esbuild.build({
|
|
209
|
+
entryPoints: ['__entry__'],
|
|
210
|
+
bundle: true, write: false, format: 'esm', platform: 'node',
|
|
211
|
+
plugins: [plugin],
|
|
212
|
+
alias: { '@playwright/test': getAliasPath() },
|
|
213
|
+
external: [
|
|
214
|
+
'fs', 'path', 'child_process', 'os', 'crypto', 'util',
|
|
215
|
+
'stream', 'events', 'net', 'http', 'https', 'url',
|
|
216
|
+
'worker_threads', 'node:*',
|
|
217
|
+
],
|
|
218
|
+
});
|
|
219
|
+
return result.outputFiles[0].text;
|
|
220
|
+
}
|
|
221
|
+
// ─── Detection ─────────────────────────────────────────────────────────────
|
|
222
|
+
const NODE_MODULES = new Set([
|
|
223
|
+
'fs', 'path', 'child_process', 'os', 'crypto', 'util',
|
|
224
|
+
'stream', 'events', 'net', 'http', 'https', 'url',
|
|
225
|
+
'worker_threads',
|
|
226
|
+
]);
|
|
227
|
+
// Patterns that need Node.js path (context-level routing)
|
|
228
|
+
const NODE_PATTERNS = [
|
|
229
|
+
/\.route\s*\(/, // page.route() with callbacks
|
|
230
|
+
/\.routeFromHAR\s*\(/, // file path access
|
|
231
|
+
/\.waitForEvent\s*\(/, // non-serializable return objects
|
|
232
|
+
/\bserver\b/, // server fixture (http.createServer in Node.js)
|
|
233
|
+
];
|
|
234
|
+
async function detectNodeAPIs(testFilePath) {
|
|
235
|
+
const esbuild = await import('esbuild');
|
|
236
|
+
const result = await esbuild.build({
|
|
237
|
+
entryPoints: [testFilePath],
|
|
238
|
+
bundle: true, write: false, metafile: true, format: 'esm', platform: 'node',
|
|
239
|
+
alias: { '@playwright/test': getAliasPath() },
|
|
240
|
+
external: [...NODE_MODULES, 'node:*'],
|
|
241
|
+
});
|
|
242
|
+
// Check if any Node.js modules were imported
|
|
243
|
+
for (const input of Object.values(result.metafile.inputs)) {
|
|
244
|
+
for (const imp of input.imports) {
|
|
245
|
+
const mod = imp.path.replace(/^node:/, '');
|
|
246
|
+
if (NODE_MODULES.has(mod))
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Check if test source uses patterns that need Node.js path
|
|
251
|
+
const src = fs.readFileSync(testFilePath, 'utf-8');
|
|
252
|
+
for (const pattern of NODE_PATTERNS) {
|
|
253
|
+
if (pattern.test(src))
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
// ─── Parse Results ─────────────────────────────────────────────────────────
|
|
259
|
+
function parseResults(output, file) {
|
|
260
|
+
const results = [];
|
|
261
|
+
const lines = output.split('\n');
|
|
262
|
+
for (let i = 0; i < lines.length; i++) {
|
|
263
|
+
const passMatch = lines[i].match(/^\s*[✓✔]\s+(.+?)\s+\((\d+)ms\)/);
|
|
264
|
+
if (passMatch) {
|
|
265
|
+
results.push({ name: passMatch[1], file, passed: true, skipped: false, duration: parseInt(passMatch[2]) });
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const failMatch = lines[i].match(/^\s*[✗✘]\s+(.+?)\s+\((\d+)ms\)/);
|
|
269
|
+
if (failMatch) {
|
|
270
|
+
const error = lines[i + 1]?.trim() || 'Test failed';
|
|
271
|
+
results.push({ name: failMatch[1], file, passed: false, skipped: false, error, duration: parseInt(failMatch[2]) });
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const skipMatch = lines[i].match(/^\s*-\s+(.+?)\s+\(skipped\)/);
|
|
275
|
+
if (skipMatch) {
|
|
276
|
+
results.push({ name: skipMatch[1], file, passed: true, skipped: true, duration: 0 });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return results;
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../src/execute.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode Detection
|
|
3
|
+
*
|
|
4
|
+
* Determines test execution mode by analyzing the full dependency tree via esbuild:
|
|
5
|
+
* - 'browser': pure browser test → run in browser via shim (fastest, 0ms overhead)
|
|
6
|
+
* - 'compiler': uses Node.js APIs → run in Node.js, page/expect → bridge (~2ms per call)
|
|
7
|
+
*
|
|
8
|
+
* Uses esbuild's metafile to check ALL resolved imports (not just the test file),
|
|
9
|
+
* so Node.js usage in helpers/utilities is detected correctly.
|
|
10
|
+
*
|
|
11
|
+
* Detection is per-file. If any dependency uses Node.js APIs, the entire file
|
|
12
|
+
* uses compiler mode.
|
|
13
|
+
*/
|
|
14
|
+
export type TestMode = 'browser' | 'compiler';
|
|
15
|
+
/**
|
|
16
|
+
* Detect test mode by attempting an esbuild bundle with platform: 'browser'.
|
|
17
|
+
* If esbuild encounters Node.js built-in imports, they'll appear as externals
|
|
18
|
+
* in the metafile — indicating the test needs Node.js.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectTestMode(testFilePath: string): Promise<TestMode>;
|
|
21
|
+
//# sourceMappingURL=mode-detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-detect.d.ts","sourceRoot":"","sources":["../src/mode-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE9C;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuC5E"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode Detection
|
|
3
|
+
*
|
|
4
|
+
* Determines test execution mode by analyzing the full dependency tree via esbuild:
|
|
5
|
+
* - 'browser': pure browser test → run in browser via shim (fastest, 0ms overhead)
|
|
6
|
+
* - 'compiler': uses Node.js APIs → run in Node.js, page/expect → bridge (~2ms per call)
|
|
7
|
+
*
|
|
8
|
+
* Uses esbuild's metafile to check ALL resolved imports (not just the test file),
|
|
9
|
+
* so Node.js usage in helpers/utilities is detected correctly.
|
|
10
|
+
*
|
|
11
|
+
* Detection is per-file. If any dependency uses Node.js APIs, the entire file
|
|
12
|
+
* uses compiler mode.
|
|
13
|
+
*/
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const NODE_BUILTINS = new Set([
|
|
18
|
+
'fs', 'path', 'child_process', 'os', 'crypto', 'util',
|
|
19
|
+
'stream', 'events', 'net', 'http', 'https', 'url',
|
|
20
|
+
'worker_threads', 'cluster', 'dgram', 'dns', 'tls',
|
|
21
|
+
'readline', 'zlib', 'buffer', 'assert', 'vm', 'perf_hooks',
|
|
22
|
+
'async_hooks', 'string_decoder', 'querystring', 'punycode',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Detect test mode by attempting an esbuild bundle with platform: 'browser'.
|
|
26
|
+
* If esbuild encounters Node.js built-in imports, they'll appear as externals
|
|
27
|
+
* in the metafile — indicating the test needs Node.js.
|
|
28
|
+
*/
|
|
29
|
+
export async function detectTestMode(testFilePath) {
|
|
30
|
+
const esbuild = await import('esbuild');
|
|
31
|
+
const shimPath = path.resolve(path.dirname(__filename), '../src/shim/test-runner.ts');
|
|
32
|
+
try {
|
|
33
|
+
const result = await esbuild.build({
|
|
34
|
+
entryPoints: [testFilePath],
|
|
35
|
+
bundle: true,
|
|
36
|
+
write: false,
|
|
37
|
+
format: 'iife',
|
|
38
|
+
platform: 'browser',
|
|
39
|
+
metafile: true,
|
|
40
|
+
alias: { '@playwright/test': shimPath },
|
|
41
|
+
// Mark Node.js built-ins as external — if they appear in metafile, test needs Node.js
|
|
42
|
+
external: [...NODE_BUILTINS].flatMap(m => [m, `node:${m}`]),
|
|
43
|
+
logLevel: 'silent',
|
|
44
|
+
});
|
|
45
|
+
// Check if any resolved import is a Node.js built-in
|
|
46
|
+
for (const inputPath of Object.keys(result.metafile.inputs)) {
|
|
47
|
+
// esbuild marks externals with a prefix like <define:fs> or the bare module name
|
|
48
|
+
for (const imp of result.metafile.inputs[inputPath].imports) {
|
|
49
|
+
const modName = imp.path.replace(/^node:/, '');
|
|
50
|
+
if (NODE_BUILTINS.has(modName))
|
|
51
|
+
return 'compiler';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Also check the source for process.env / __dirname (not import-based)
|
|
55
|
+
const fs = await import('fs');
|
|
56
|
+
const source = fs.default.readFileSync(testFilePath, 'utf-8');
|
|
57
|
+
if (/process\.env\b|process\.cwd\b|process\.argv\b|__dirname\b|__filename\b/.test(source)) {
|
|
58
|
+
return 'compiler';
|
|
59
|
+
}
|
|
60
|
+
return 'browser';
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// If esbuild fails, fall back to compiler mode (safer)
|
|
64
|
+
return 'compiler';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=mode-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-detect.js","sourceRoot":"","sources":["../src/mode-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAElD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM;IACrD,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;IACjD,gBAAgB,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK;IAClD,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;IAC1D,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU;CAC3D,CAAC,CAAC;AAIH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,YAAoB;IACvD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,4BAA4B,CAAC,CAAC;IAEtF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;YACjC,WAAW,EAAE,CAAC,YAAY,CAAC;YAC3B,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE;YACvC,sFAAsF;YACtF,QAAQ,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC3D,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;QAEH,qDAAqD;QACrD,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,iFAAiF;YACjF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC7D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAC/C,IAAI,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;oBAAE,OAAO,UAAU,CAAC;YACpD,CAAC;QACH,CAAC;QAED,uEAAuE;QACvE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC9D,IAAI,wEAAwE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1F,OAAO,UAAU,CAAC;QACpB,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;QACvD,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy Page
|
|
3
|
+
*
|
|
4
|
+
* Creates a Proxy that looks like Playwright's Page object.
|
|
5
|
+
* Records method calls as a string chain. Nothing executes until `await`.
|
|
6
|
+
* `await` triggers `then()` which sends the chain to the bridge.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* page.* → Proxy (builds chain string)
|
|
10
|
+
* await page.* → bridge.run(chain) → executes in browser
|
|
11
|
+
* expect(proxy) → builds expect chain → bridge.run on await
|
|
12
|
+
* expect(value) → native assertion in Node.js
|
|
13
|
+
*/
|
|
14
|
+
type BridgeRun = (command: string) => Promise<{
|
|
15
|
+
text?: string;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
declare const PROXY_FLAG: unique symbol;
|
|
19
|
+
declare const CHAIN_KEY: unique symbol;
|
|
20
|
+
/**
|
|
21
|
+
* Create a proxy page that routes most calls through the bridge.
|
|
22
|
+
* Methods in NODE_PAGE_METHODS delegate to the real Node page (CDP) instead.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createPageProxy(bridge: BridgeRun, nodePage?: any, cdpPage?: any): any;
|
|
25
|
+
/**
|
|
26
|
+
* Create a smart expect function.
|
|
27
|
+
* - expect(proxy) → bridge (async Playwright assertions)
|
|
28
|
+
* - expect(value) → native Node.js assertion
|
|
29
|
+
*/
|
|
30
|
+
export declare function createExpect(bridge: BridgeRun): (target: any) => any;
|
|
31
|
+
export { PROXY_FLAG, CHAIN_KEY };
|
|
32
|
+
//# sourceMappingURL=proxy-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-page.d.ts","sourceRoot":"","sources":["../src/proxy-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,KAAK,SAAS,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAYpF,QAAA,MAAM,UAAU,eAAoB,CAAC;AACrC,QAAA,MAAM,SAAS,eAAkB,CAAC;AA0HlC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,CAErF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,IAepC,QAAQ,GAAG,SAQpB;AAED,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy Page
|
|
3
|
+
*
|
|
4
|
+
* Creates a Proxy that looks like Playwright's Page object.
|
|
5
|
+
* Records method calls as a string chain. Nothing executes until `await`.
|
|
6
|
+
* `await` triggers `then()` which sends the chain to the bridge.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* page.* → Proxy (builds chain string)
|
|
10
|
+
* await page.* → bridge.run(chain) → executes in browser
|
|
11
|
+
* expect(proxy) → builds expect chain → bridge.run on await
|
|
12
|
+
* expect(value) → native assertion in Node.js
|
|
13
|
+
*/
|
|
14
|
+
const BRIDGE_TIMEOUT = 30000;
|
|
15
|
+
const BRIDGE_DEBUG = !!process.env.PW_DEBUG;
|
|
16
|
+
function withTimeout(promise, ms, label) {
|
|
17
|
+
let timer;
|
|
18
|
+
return Promise.race([
|
|
19
|
+
promise,
|
|
20
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(`Timeout ${ms}ms: ${label}`)), ms); }),
|
|
21
|
+
]).finally(() => clearTimeout(timer));
|
|
22
|
+
}
|
|
23
|
+
const PROXY_FLAG = Symbol('isProxy');
|
|
24
|
+
const CHAIN_KEY = Symbol('chain');
|
|
25
|
+
// Methods delegated to Node.js context (context-level routing).
|
|
26
|
+
const CONTEXT_METHODS = new Set([
|
|
27
|
+
'route', 'unroute', 'routeFromHAR', 'unrouteAll',
|
|
28
|
+
]);
|
|
29
|
+
// Methods delegated to Node page (same tab, returns non-serializable objects or needs concurrency).
|
|
30
|
+
const CDP_PAGE_METHODS = new Set([
|
|
31
|
+
'waitForEvent',
|
|
32
|
+
'$', '$$', '$eval', '$$eval',
|
|
33
|
+
'frames', 'mainFrame',
|
|
34
|
+
'evaluate', // needs concurrency with pending bridge actions
|
|
35
|
+
'evaluateHandle', // returns JSHandle — non-serializable
|
|
36
|
+
'on', 'off', 'once', // event listeners need real page
|
|
37
|
+
]);
|
|
38
|
+
function makeProxy(chain, bridge, nodePage, cdpPage) {
|
|
39
|
+
const handler = {
|
|
40
|
+
get(_target, prop) {
|
|
41
|
+
// Internal: check if this is a proxy
|
|
42
|
+
if (prop === PROXY_FLAG)
|
|
43
|
+
return true;
|
|
44
|
+
if (prop === CHAIN_KEY)
|
|
45
|
+
return chain;
|
|
46
|
+
// await triggers execution
|
|
47
|
+
if (prop === 'then') {
|
|
48
|
+
return (resolve, reject) => {
|
|
49
|
+
const cmd = `await ${chain}`;
|
|
50
|
+
if (BRIDGE_DEBUG)
|
|
51
|
+
console.log(` [bridge →] ${cmd.substring(0, 120)}`);
|
|
52
|
+
withTimeout(bridge(cmd), BRIDGE_TIMEOUT, chain).then(r => {
|
|
53
|
+
if (BRIDGE_DEBUG)
|
|
54
|
+
console.log(` [bridge ←] ${r.isError ? 'ERR' : 'OK'} ${(r.text || '').substring(0, 80)}`);
|
|
55
|
+
if (r.isError)
|
|
56
|
+
reject(new Error(r.text || 'Bridge error'));
|
|
57
|
+
else {
|
|
58
|
+
const text = r.text;
|
|
59
|
+
if (text === undefined || text === '')
|
|
60
|
+
resolve(undefined);
|
|
61
|
+
else {
|
|
62
|
+
try {
|
|
63
|
+
resolve(JSON.parse(text));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
resolve(text);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}).catch(reject);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Symbol.toPrimitive — for string concatenation etc.
|
|
74
|
+
if (prop === Symbol.toPrimitive) {
|
|
75
|
+
return () => chain;
|
|
76
|
+
}
|
|
77
|
+
// Context methods — delegate to Node.js context (route/unroute).
|
|
78
|
+
if (nodePage && chain === 'page' && CONTEXT_METHODS.has(String(prop))) {
|
|
79
|
+
return (...args) => nodePage[String(prop)](...args);
|
|
80
|
+
}
|
|
81
|
+
// CDP page methods — delegate to real page via CDP (waitForEvent).
|
|
82
|
+
if (cdpPage && chain === 'page' && CDP_PAGE_METHODS.has(String(prop))) {
|
|
83
|
+
return (...args) => cdpPage[String(prop)](...args);
|
|
84
|
+
}
|
|
85
|
+
// Property access → extend chain (returns callable proxy)
|
|
86
|
+
return makeCallableProxy(`${chain}.${String(prop)}`, bridge, nodePage, cdpPage);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
return new Proxy({}, handler);
|
|
90
|
+
}
|
|
91
|
+
function makeCallableProxy(chain, bridge, nodePage, cdpPage) {
|
|
92
|
+
// A proxy that can be both called as a function AND have properties accessed
|
|
93
|
+
const fn = function () { };
|
|
94
|
+
return new Proxy(fn, {
|
|
95
|
+
get(_target, prop) {
|
|
96
|
+
if (prop === PROXY_FLAG)
|
|
97
|
+
return true;
|
|
98
|
+
if (prop === CHAIN_KEY)
|
|
99
|
+
return chain;
|
|
100
|
+
if (prop === 'then') {
|
|
101
|
+
return (resolve, reject) => {
|
|
102
|
+
withTimeout(bridge(`await ${chain}`), BRIDGE_TIMEOUT, chain).then(r => {
|
|
103
|
+
if (r.isError)
|
|
104
|
+
reject(new Error(r.text || 'Bridge error'));
|
|
105
|
+
else {
|
|
106
|
+
const text = r.text;
|
|
107
|
+
if (text === undefined || text === '')
|
|
108
|
+
resolve(undefined);
|
|
109
|
+
else {
|
|
110
|
+
try {
|
|
111
|
+
resolve(JSON.parse(text));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
resolve(text);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}).catch(reject);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (prop === Symbol.toPrimitive) {
|
|
122
|
+
return () => chain;
|
|
123
|
+
}
|
|
124
|
+
return makeCallableProxy(`${chain}.${String(prop)}`, bridge, nodePage, cdpPage);
|
|
125
|
+
},
|
|
126
|
+
apply(_target, _thisArg, args) {
|
|
127
|
+
const argsStr = args.map(serializeArg).join(', ');
|
|
128
|
+
return makeProxy(`${chain}(${argsStr})`, bridge, nodePage, cdpPage);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function serializeArg(arg) {
|
|
133
|
+
// Proxy argument → use its chain
|
|
134
|
+
if (arg && typeof arg === 'object' && arg[PROXY_FLAG]) {
|
|
135
|
+
return arg[CHAIN_KEY];
|
|
136
|
+
}
|
|
137
|
+
// RegExp → serialize as regex literal
|
|
138
|
+
if (arg instanceof RegExp) {
|
|
139
|
+
return arg.toString();
|
|
140
|
+
}
|
|
141
|
+
// Function → serialize as string
|
|
142
|
+
if (typeof arg === 'function') {
|
|
143
|
+
return arg.toString();
|
|
144
|
+
}
|
|
145
|
+
// Everything else → JSON
|
|
146
|
+
return JSON.stringify(arg);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create a proxy page that routes most calls through the bridge.
|
|
150
|
+
* Methods in NODE_PAGE_METHODS delegate to the real Node page (CDP) instead.
|
|
151
|
+
*/
|
|
152
|
+
export function createPageProxy(bridge, nodePage, cdpPage) {
|
|
153
|
+
return makeProxy('page', bridge, nodePage, cdpPage);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Create a smart expect function.
|
|
157
|
+
* - expect(proxy) → bridge (async Playwright assertions)
|
|
158
|
+
* - expect(value) → native Node.js assertion
|
|
159
|
+
*/
|
|
160
|
+
export function createExpect(bridge) {
|
|
161
|
+
// Simple native expect for non-proxy values
|
|
162
|
+
const nativeExpect = (actual) => ({
|
|
163
|
+
toBe: (expected) => { if (actual !== expected)
|
|
164
|
+
throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`); },
|
|
165
|
+
toEqual: (expected) => { if (JSON.stringify(actual) !== JSON.stringify(expected))
|
|
166
|
+
throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`); },
|
|
167
|
+
toBeTruthy: () => { if (!actual)
|
|
168
|
+
throw new Error(`Expected truthy but got ${JSON.stringify(actual)}`); },
|
|
169
|
+
toBeFalsy: () => { if (actual)
|
|
170
|
+
throw new Error(`Expected falsy but got ${JSON.stringify(actual)}`); },
|
|
171
|
+
toContain: (expected) => { if (!String(actual).includes(expected))
|
|
172
|
+
throw new Error(`Expected to contain ${JSON.stringify(expected)}`); },
|
|
173
|
+
toMatch: (pattern) => { if (!pattern.test(String(actual)))
|
|
174
|
+
throw new Error(`Expected to match ${pattern}`); },
|
|
175
|
+
not: {
|
|
176
|
+
toBe: (expected) => { if (actual === expected)
|
|
177
|
+
throw new Error(`Expected not ${JSON.stringify(expected)}`); },
|
|
178
|
+
toContain: (expected) => { if (String(actual).includes(expected))
|
|
179
|
+
throw new Error(`Expected not to contain ${JSON.stringify(expected)}`); },
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
return (target) => {
|
|
183
|
+
if (target && target[PROXY_FLAG]) {
|
|
184
|
+
// Proxy → route to bridge
|
|
185
|
+
return makeCallableProxy(`expect(${target[CHAIN_KEY]})`, bridge);
|
|
186
|
+
}
|
|
187
|
+
// Regular value → native assertion
|
|
188
|
+
return nativeExpect(target);
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export { PROXY_FLAG, CHAIN_KEY };
|
|
192
|
+
//# sourceMappingURL=proxy-page.js.map
|