pi-lean-dimension 0.2.3 → 0.3.0
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 +1 -1
- package/node_modules/pi-lean-portal/AGENTS.md +15 -23
- package/node_modules/pi-lean-portal/README.md +31 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +12 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +263 -1102
- package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +6 -32
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +1 -1
- package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +1 -2
- package/node_modules/pi-lean-portal/__tests__/helpers/setup-temp-dir.ts +26 -0
- package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-contract.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +65 -69
- package/node_modules/pi-lean-portal/__tests__/router-dispatch.test.ts +1 -14
- package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +31 -16
- package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/web-guides.test.ts +146 -19
- package/node_modules/pi-lean-portal/backends/chromium-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/firefox-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +1 -1
- package/node_modules/pi-lean-portal/backends/python-adapter.ts +3 -3
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +2 -4
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +572 -81
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_py_bridges.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +13 -43
- package/node_modules/pi-lean-portal/browser-profile.ts +2 -3
- package/node_modules/pi-lean-portal/browser-toggle.ts +168 -380
- package/node_modules/pi-lean-portal/contributed/CHOOSING.md +2 -2
- package/node_modules/pi-lean-portal/contributed/README.md +65 -6
- package/node_modules/pi-lean-portal/contributed/camoufox-py/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +108 -14
- package/node_modules/pi-lean-portal/contributed/camoufox-py/pin.json +4 -0
- package/node_modules/pi-lean-portal/contributed/invisible-py/bridge.py +1 -1
- package/node_modules/pi-lean-portal/core/fetch-backend.ts +10 -30
- package/node_modules/pi-lean-portal/core/guides.ts +14 -18
- package/node_modules/pi-lean-portal/core/router.ts +2 -20
- package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +19 -30
- package/node_modules/pi-lean-portal/core/shared/browser-data.ts +4 -4
- package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +2 -2
- package/node_modules/pi-lean-portal/core/shared/paths.ts +16 -2
- package/node_modules/pi-lean-portal/core/shared/settings-reader.ts +2 -7
- package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +1 -1
- package/node_modules/pi-lean-portal/index.ts +8 -1
- package/node_modules/pi-lean-portal/package.json +2 -1
- package/node_modules/pi-lean-portal/tools/web-learn.ts +1 -4
- package/node_modules/pi-lean-search/__tests__/web-search.test.ts +87 -0
- package/node_modules/pi-lean-search/index.ts +134 -64
- package/node_modules/pi-lean-search/package.json +2 -1
- package/node_modules/playwright/README.md +3 -3
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +2 -2
- package/node_modules/playwright/lib/cli/reportActions.js +2 -2
- package/node_modules/playwright/lib/common/index.js +77 -52
- package/node_modules/playwright/lib/common/index.js.txt +10 -11
- package/node_modules/playwright/lib/errorContext.js +2 -3
- package/node_modules/playwright/lib/index.js +30 -8
- package/node_modules/playwright/lib/matchers/expect.js +4811 -4379
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +87 -140
- package/node_modules/playwright/lib/matchers/expect.js.txt +30 -35
- package/node_modules/playwright/lib/program.js +19 -0
- package/node_modules/playwright/lib/runner/index.js +309 -153
- package/node_modules/playwright/lib/runner/index.js.txt +13 -12
- package/node_modules/playwright/lib/transform/babelBundle.js +12899 -11654
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +379 -354
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +39 -39
- package/node_modules/playwright/lib/transform/esmLoader.js +3129 -1116
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +81 -61
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +21 -18
- package/node_modules/playwright/lib/util.js +2 -29
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +14 -32
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +7 -8
- package/node_modules/playwright/package.json +3 -6
- package/node_modules/playwright/types/test.d.ts +391 -4
- package/node_modules/playwright/types/testReporter.d.ts +79 -4
- package/node_modules/playwright-core/browsers.json +12 -18
- package/node_modules/playwright-core/lib/bootstrap.js +1 -1
- package/node_modules/playwright-core/lib/coreBundle.js +11344 -9900
- package/node_modules/playwright-core/lib/server/electron/loader.js +5 -2
- package/node_modules/playwright-core/lib/serverRegistry.js +5 -1
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +21 -6
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +1 -1
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +4 -0
- package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/SKILL.md +21 -5
- package/node_modules/playwright-core/lib/tools/{cli-client/skill/references/spec-driven-testing.md → skills/playwright-cli/references/test-generation.md} +135 -7
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +31 -8
- package/node_modules/playwright-core/lib/utilsBundle.js +37698 -33670
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +391 -268
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -1
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +15 -55
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +3 -4
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +338 -137
- package/node_modules/playwright-core/package.json +2 -2
- package/node_modules/playwright-core/types/protocol.d.ts +249 -36
- package/node_modules/playwright-core/types/structs.d.ts +3 -1
- package/node_modules/playwright-core/types/types.d.ts +2009 -266
- package/package.json +3 -3
- package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +0 -188
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/patch_playwright.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +0 -525
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_chromium_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_firefox_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/core/shared/url-safety.ts +0 -164
- package/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json +0 -2739
- package/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +0 -134
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-BY2S1tHT.css +0 -1
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-C_5TMfeg.js +0 -52
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DeBYQozu.js +0 -32
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-4ZiSSCmn.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Bq-mQf8S.js +0 -193
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-LEHpjmcn.js +0 -32
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-BNmKHKpQ.js +0 -264
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-BYQrIQwR.js +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +0 -9
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.CjdS-WJx.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.CzXZzn5A.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.DMMX1gXU.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.v8KI4P3m.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.BZQ54Kgt.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Ut8wwJNp.js +0 -6
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.DYP7pi_n.css +0 -32
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/element-attributes.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/playwright-tests.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/request-mocking.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/running-code.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/session-management.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/storage-state.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/tracing.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/video-recording.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{trace → skills/playwright-trace}/SKILL.md +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for fetch-backend.ts — webFetch() and supporting utilities.
|
|
3
3
|
*
|
|
4
|
-
* Uses vi.spyOn(global, 'fetch') to mock HTTP responses,
|
|
5
|
-
* URL safety restrictions (127.0.0.1 is blocked) and providing
|
|
4
|
+
* Uses vi.spyOn(global, 'fetch') to mock HTTP responses, providing
|
|
6
5
|
* deterministic control over response timing.
|
|
7
6
|
*/
|
|
8
7
|
|
|
@@ -187,40 +186,14 @@ describe("webFetch — core fetch", () => {
|
|
|
187
186
|
});
|
|
188
187
|
});
|
|
189
188
|
|
|
190
|
-
// ─── URL
|
|
189
|
+
// ─── URL Validation ───────────────────────────────────────────
|
|
191
190
|
|
|
192
|
-
describe("webFetch — URL
|
|
191
|
+
describe("webFetch — URL validation", () => {
|
|
193
192
|
it("rejects invalid URLs before fetching", async () => {
|
|
194
193
|
const result = await webFetch({ url: "not a url" });
|
|
195
194
|
expect(result.success).toBe(false);
|
|
196
195
|
expect(result.error).toBe("Invalid URL");
|
|
197
196
|
});
|
|
198
|
-
|
|
199
|
-
it("rejects SSRF (localhost) before fetching", async () => {
|
|
200
|
-
const result = await webFetch({ url: "http://localhost/admin" });
|
|
201
|
-
expect(result.success).toBe(false);
|
|
202
|
-
expect(result.error).toMatch(/URL blocked/i);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("rejects SSRF (private IP) before fetching", async () => {
|
|
206
|
-
const result = await webFetch({ url: "http://127.0.0.1/secret" });
|
|
207
|
-
expect(result.success).toBe(false);
|
|
208
|
-
expect(result.error).toMatch(/URL blocked/i);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("rejects dangerous schemes before fetching", async () => {
|
|
212
|
-
const result = await webFetch({ url: "file:///etc/passwd" });
|
|
213
|
-
expect(result.success).toBe(false);
|
|
214
|
-
expect(result.error).toMatch(/URL blocked/i);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("rejects URLs containing secrets before fetching", async () => {
|
|
218
|
-
const result = await webFetch({
|
|
219
|
-
url: "http://example.com/api?api_key=secret123",
|
|
220
|
-
});
|
|
221
|
-
expect(result.success).toBe(false);
|
|
222
|
-
expect(result.error).toMatch(/URL blocked/i);
|
|
223
|
-
});
|
|
224
197
|
});
|
|
225
198
|
|
|
226
199
|
// ─── JavaScript Detection ─────────────────────────────────────
|
|
@@ -314,11 +287,12 @@ describe("webFetch — content capping", () => {
|
|
|
314
287
|
});
|
|
315
288
|
|
|
316
289
|
it("preserves both temp files when two large fetches share a taskId", async () => {
|
|
290
|
+
const tid = "preserve-shared";
|
|
317
291
|
mockFetch({ body: `<p>${longText(6000)}</p>` });
|
|
318
|
-
const a = await webFetch({ url: "http://example.com/a", taskId:
|
|
292
|
+
const a = await webFetch({ url: "http://example.com/a", taskId: tid });
|
|
319
293
|
|
|
320
294
|
mockFetch({ body: `<p>${"B".repeat(6000)}</p>` });
|
|
321
|
-
const b = await webFetch({ url: "http://example.com/b", taskId:
|
|
295
|
+
const b = await webFetch({ url: "http://example.com/b", taskId: tid });
|
|
322
296
|
|
|
323
297
|
expect(a.filePath).toBeTruthy();
|
|
324
298
|
expect(b.filePath).toBeTruthy();
|
|
@@ -37,7 +37,7 @@ def main() -> None:
|
|
|
37
37
|
write_response(req_id, result="pong")
|
|
38
38
|
|
|
39
39
|
elif method == "browser.init":
|
|
40
|
-
#
|
|
40
|
+
# Accept plugin config forwarded from the adapter after ping.
|
|
41
41
|
write_response(req_id, result={"ok": True})
|
|
42
42
|
|
|
43
43
|
elif method == "shutdown":
|
|
@@ -43,8 +43,7 @@ import { isAbsolute, join } from "node:path";
|
|
|
43
43
|
* and non-empty; otherwise defaults to
|
|
44
44
|
* `~/.pi/agent/pi-lean-portal/user-backends/`.
|
|
45
45
|
*
|
|
46
|
-
* Vendored locally —
|
|
47
|
-
* generic runner reuses this same helper for consistency.
|
|
46
|
+
* Vendored locally — reused by the contributed test runner for consistency.
|
|
48
47
|
*/
|
|
49
48
|
export function userBackendsDir(): string {
|
|
50
49
|
const env = process.env.PI_USER_BACKENDS_DIR;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test setup: isolate each worker's temp directory using process.pid.
|
|
3
|
+
*
|
|
4
|
+
* vitest's default `forks` pool runs each test file in a separate Node
|
|
5
|
+
* process with a unique PID. Keying PI_BROWSER_TEMP_DIR on the PID
|
|
6
|
+
* gives each worker a disjoint filesystem namespace, preventing
|
|
7
|
+
* cross-worker `removeAllSnapshotFiles()` races that cause flaky
|
|
8
|
+
* ENOENT / null-result failures in snapshot-cache.test.ts.
|
|
9
|
+
*
|
|
10
|
+
* This file must be listed in vitest's `setupFiles` so it runs *before*
|
|
11
|
+
* any test file import, including the module-level `const BROWSER_TEMP_DIR`
|
|
12
|
+
* evaluation in `core/shared/paths.ts`.
|
|
13
|
+
*/
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { rmSync } from "node:fs";
|
|
16
|
+
|
|
17
|
+
const dir = `${tmpdir()}/pi-lean-portal-test-${process.pid}`;
|
|
18
|
+
process.env.PI_BROWSER_TEMP_DIR = dir;
|
|
19
|
+
|
|
20
|
+
process.on("exit", () => {
|
|
21
|
+
try {
|
|
22
|
+
rmSync(dir, { recursive: true, force: true });
|
|
23
|
+
} catch {
|
|
24
|
+
/* best-effort cleanup */
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -190,7 +190,7 @@ describe("loadFullConfig().browser", () => {
|
|
|
190
190
|
});
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
// ─── Default plugin fallback
|
|
193
|
+
// ─── Default plugin fallback ───────────────────────────────────────
|
|
194
194
|
|
|
195
195
|
describe("loadFullConfig().plugins default fallback", () => {
|
|
196
196
|
it("returns the four shipped backends when browser.plugins is absent", () => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* test harness itself works correctly against a known-good fixture.
|
|
4
4
|
*
|
|
5
5
|
* These tests exercise the structural (non-browser) contract only.
|
|
6
|
-
* Real-browser behavioral tests run in
|
|
6
|
+
* Real-browser behavioral tests run in the per-backend contract test files.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { runContractTests } from "./helpers/plugin-contract.js";
|
|
@@ -259,7 +259,7 @@ describe("DEFAULT_BACKENDS_ROOT", () => {
|
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
261
|
|
|
262
|
-
// ─── Multi-root discovery
|
|
262
|
+
// ─── Multi-root discovery ────────────────────────────────────────────
|
|
263
263
|
|
|
264
264
|
describe("detectPluginType — multi-root", () => {
|
|
265
265
|
let packageRoot: string;
|
|
@@ -814,12 +814,12 @@ describeStderr("stderr capture", () => {
|
|
|
814
814
|
// ═════════════════════════════════════════════════════════════════════
|
|
815
815
|
|
|
816
816
|
// ══════════════════════════════════════════════════════════════════════
|
|
817
|
-
// browser.init RPC
|
|
817
|
+
// browser.init RPC
|
|
818
818
|
// ══════════════════════════════════════════════════════════════════════
|
|
819
819
|
|
|
820
820
|
const describeBrowserInit = PYTHON_AVAILABLE ? describe : describe.skip;
|
|
821
821
|
|
|
822
|
-
describeBrowserInit("browser.init RPC
|
|
822
|
+
describeBrowserInit("browser.init RPC", () => {
|
|
823
823
|
/**
|
|
824
824
|
* Build a bridge that records every method received (in order) and
|
|
825
825
|
* optionally answers `browser.init` with success or an error.
|
|
@@ -1087,7 +1087,7 @@ describeBrowserInit("browser.init RPC (Phase 0)", () => {
|
|
|
1087
1087
|
});
|
|
1088
1088
|
|
|
1089
1089
|
// ═════════════════════════════════════════════════════════════════════
|
|
1090
|
-
// PYTHONPATH injection
|
|
1090
|
+
// PYTHONPATH injection
|
|
1091
1091
|
// ═════════════════════════════════════════════════════════════════════
|
|
1092
1092
|
|
|
1093
1093
|
/**
|
|
@@ -1095,7 +1095,7 @@ describeBrowserInit("browser.init RPC (Phase 0)", () => {
|
|
|
1095
1095
|
* module-level `DEFAULT_BACKENDS_ROOT` only, never `this`), so it can be
|
|
1096
1096
|
* invoked without binding. These unit tests don't need a subprocess.
|
|
1097
1097
|
*/
|
|
1098
|
-
describe("_buildPythonPath — PYTHONPATH injection
|
|
1098
|
+
describe("_buildPythonPath — PYTHONPATH injection", () => {
|
|
1099
1099
|
it("includes the package python-base directory when PYTHONPATH is unset", () => {
|
|
1100
1100
|
const adapter = createAdapter();
|
|
1101
1101
|
const saved = process.env.PYTHONPATH;
|
|
@@ -1131,73 +1131,69 @@ describe("_buildPythonPath — PYTHONPATH injection (Phase 0b)", () => {
|
|
|
1131
1131
|
|
|
1132
1132
|
const describePythonPathEnv = PYTHON_AVAILABLE ? describe : describe.skip;
|
|
1133
1133
|
|
|
1134
|
-
describePythonPathEnv(
|
|
1135
|
-
"
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
});
|
|
1134
|
+
describePythonPathEnv("PYTHONPATH reaches the spawned bridge", () => {
|
|
1135
|
+
it("the bridge process env includes the package python-base path", async () => {
|
|
1136
|
+
const dir = mkdtempSync(join(tmpdir(), "pi-lean-portal-pypath-"));
|
|
1137
|
+
const bridgePath = join(dir, "bridge.py");
|
|
1138
|
+
const lines: string[] = [
|
|
1139
|
+
"import json, sys, os",
|
|
1140
|
+
"sys.stderr.write('PYPATH:' + (os.environ.get('PYTHONPATH') or '') + '\\n')",
|
|
1141
|
+
"sys.stderr.flush()",
|
|
1142
|
+
"for _line in sys.stdin:",
|
|
1143
|
+
" _line = _line.strip()",
|
|
1144
|
+
" if not _line: continue",
|
|
1145
|
+
" _req = json.loads(_line)",
|
|
1146
|
+
" _m = _req.get('method','')",
|
|
1147
|
+
" _rid = _req.get('id')",
|
|
1148
|
+
" if _m == 'ping':",
|
|
1149
|
+
" sys.stdout.write(json.dumps({'jsonrpc':'2.0','id':_rid,'result':'pong'}) + '\\n')",
|
|
1150
|
+
" sys.stdout.flush()",
|
|
1151
|
+
" elif _m == 'browser.init':",
|
|
1152
|
+
" sys.stdout.write(json.dumps({'jsonrpc':'2.0','id':_rid,'result':{'ok':True}}) + '\\n')",
|
|
1153
|
+
" sys.stdout.flush()",
|
|
1154
|
+
" elif _m == 'shutdown':",
|
|
1155
|
+
" sys.stdout.write(json.dumps({'jsonrpc':'2.0','id':_rid,'result':'bye'}) + '\\n')",
|
|
1156
|
+
" sys.stdout.flush()",
|
|
1157
|
+
" break",
|
|
1158
|
+
" elif _m == 'browser.navigate':",
|
|
1159
|
+
" sys.stdout.write(json.dumps({'jsonrpc':'2.0','id':_rid,'result':{'success':True,'url':_req.get('params',{}).get('url',''),'title':'P','snapshot':'','elementCount':0}}) + '\\n')",
|
|
1160
|
+
" sys.stdout.flush()",
|
|
1161
|
+
" else:",
|
|
1162
|
+
" sys.stdout.write(json.dumps({'jsonrpc':'2.0','id':_rid,'error':{'code':-32601,'message':'unknown'}}) + '\\n')",
|
|
1163
|
+
" sys.stdout.flush()",
|
|
1164
|
+
];
|
|
1165
|
+
writeFileSync(bridgePath, lines.join("\n"), "utf-8");
|
|
1166
|
+
const adapter = new PythonPluginAdapter("pypath-test", {
|
|
1167
|
+
bridgeScript: bridgePath,
|
|
1168
|
+
pythonPath: "python3",
|
|
1169
|
+
});
|
|
1170
|
+
try {
|
|
1171
|
+
await adapter.init({});
|
|
1172
|
+
const r = await adapter.navigate(
|
|
1173
|
+
"https://example.com",
|
|
1174
|
+
"pypath-t1",
|
|
1175
|
+
30_000,
|
|
1176
|
+
);
|
|
1177
|
+
expect(r.success).toBe(true);
|
|
1178
|
+
const stderr =
|
|
1179
|
+
(adapter as unknown as Record<string, string>)["_stderrAccumulated"] ??
|
|
1180
|
+
"";
|
|
1181
|
+
const pypathLine = stderr
|
|
1182
|
+
.split("\n")
|
|
1183
|
+
.find((l) => l.startsWith("PYPATH:"));
|
|
1184
|
+
expect(pypathLine).toBeDefined();
|
|
1185
|
+
const pypath = (pypathLine ?? "").slice("PYPATH:".length);
|
|
1186
|
+
expect(pypath).toContain(join(DEFAULT_BACKENDS_ROOT, "python-base"));
|
|
1187
|
+
} finally {
|
|
1188
|
+
await adapter.cleanupAll().catch(() => {});
|
|
1172
1189
|
try {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
"pypath-t1",
|
|
1177
|
-
30_000,
|
|
1178
|
-
);
|
|
1179
|
-
expect(r.success).toBe(true);
|
|
1180
|
-
const stderr =
|
|
1181
|
-
(adapter as unknown as Record<string, string>)[
|
|
1182
|
-
"_stderrAccumulated"
|
|
1183
|
-
] ?? "";
|
|
1184
|
-
const pypathLine = stderr
|
|
1185
|
-
.split("\n")
|
|
1186
|
-
.find((l) => l.startsWith("PYPATH:"));
|
|
1187
|
-
expect(pypathLine).toBeDefined();
|
|
1188
|
-
const pypath = (pypathLine ?? "").slice("PYPATH:".length);
|
|
1189
|
-
expect(pypath).toContain(join(DEFAULT_BACKENDS_ROOT, "python-base"));
|
|
1190
|
-
} finally {
|
|
1191
|
-
await adapter.cleanupAll().catch(() => {});
|
|
1192
|
-
try {
|
|
1193
|
-
rmSync(dir, { recursive: true, force: true });
|
|
1194
|
-
} catch {
|
|
1195
|
-
/* ignore */
|
|
1196
|
-
}
|
|
1190
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1191
|
+
} catch {
|
|
1192
|
+
/* ignore */
|
|
1197
1193
|
}
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
);
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1201
1197
|
|
|
1202
1198
|
const describePersistence = PYTHON_AVAILABLE ? describe : describe.skip;
|
|
1203
1199
|
|
|
@@ -64,19 +64,6 @@ describe("Router dispatch", () => {
|
|
|
64
64
|
expect(mock.calls.get("navigate")).toBeUndefined();
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it("blocks SSRF URLs without calling the plugin", async () => {
|
|
68
|
-
const result = await router.navigate("http://localhost:8080/");
|
|
69
|
-
expect(result.success).toBe(false);
|
|
70
|
-
expect(result.error).toMatch(/blocked/i);
|
|
71
|
-
expect(mock.calls.get("navigate")).toBeUndefined();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("blocks private IP ranges", async () => {
|
|
75
|
-
const result = await router.navigate("http://192.168.1.1/");
|
|
76
|
-
expect(result.success).toBe(false);
|
|
77
|
-
expect(result.error).toMatch(/blocked/i);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
67
|
it("returns error for unknown strategy", async () => {
|
|
81
68
|
const result = await router.navigate("https://example.com", {
|
|
82
69
|
strategy: "nonexistent",
|
|
@@ -836,7 +823,7 @@ describe("bot detection UX", () => {
|
|
|
836
823
|
});
|
|
837
824
|
});
|
|
838
825
|
|
|
839
|
-
// ─── Profile-aware auto-recovery
|
|
826
|
+
// ─── Profile-aware auto-recovery ────────────────────────────────
|
|
840
827
|
describe("profile-aware auto-recovery", () => {
|
|
841
828
|
it("restores profileName from lastNav on auto-creation", async () => {
|
|
842
829
|
await sessionManager.removeAll();
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
* invisible-py's lifecycle/deadlock tests) keep a hand-authored
|
|
12
12
|
* file under ``__tests__/contributed/<name>-py/``.
|
|
13
13
|
*
|
|
14
|
-
* Gated by `CONTRIB_RUN === "1"` (the same env var
|
|
15
|
-
* for the contributed MiniWoB parity helper). No-op in bare CI (no
|
|
14
|
+
* Gated by `CONTRIB_RUN === "1"` (the same env var the contributed MiniWoB parity helper established). No-op in bare CI (no
|
|
16
15
|
* user-backends installed → discovered list empty → registers nothing)
|
|
17
16
|
* and also no-op when `CONTRIB_RUN` is unset, so `npm run test:ci`
|
|
18
17
|
* stays green with the file present even on a machine that has
|
|
@@ -20,8 +19,8 @@
|
|
|
20
19
|
*
|
|
21
20
|
* No stealth engine is **named** in this file. Discovery is purely
|
|
22
21
|
* runtime — whatever the user has installed under `user-backends/` is
|
|
23
|
-
* what runs. The shared suites own parametrized `it` titles
|
|
24
|
-
*
|
|
22
|
+
* what runs. The shared suites own parametrized `it` titles so a helper
|
|
23
|
+
* regression is attributable per-backend.
|
|
25
24
|
*
|
|
26
25
|
* @module
|
|
27
26
|
*/
|
|
@@ -188,6 +187,21 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
188
187
|
// absent or has no matching entry, cfg stays {} (synthetic fallback).
|
|
189
188
|
const entry = pluginCfg?.plugins.find((p) => p.name === name);
|
|
190
189
|
const cfg = entry?.config ?? {};
|
|
190
|
+
// Camoufox ``152.0.4-beta.27+`` humanized-click motion makes
|
|
191
|
+
// ``locator.click(timeout=5s)`` flake/timeout and eats MiniWoB
|
|
192
|
+
// task budgets (~1.5s/click vs 10s task timers). The contributed
|
|
193
|
+
// suite exercises the backend *contract*, not human-emulation
|
|
194
|
+
// stealth, so force ``launch.humanize=false`` for every discovered
|
|
195
|
+
// backend. Real users keep the humanize=True default for
|
|
196
|
+
// evasion-sensitive browsing.
|
|
197
|
+
const launchOverride = {
|
|
198
|
+
...((cfg.launch as Record<string, unknown> | undefined) ?? {}),
|
|
199
|
+
humanize: false,
|
|
200
|
+
};
|
|
201
|
+
const testCfg: Record<string, unknown> = {
|
|
202
|
+
...cfg,
|
|
203
|
+
launch: launchOverride,
|
|
204
|
+
};
|
|
191
205
|
if (!entry) {
|
|
192
206
|
console.warn(
|
|
193
207
|
`[contributed-runner] ${name}: no settings entry — synthetic config`,
|
|
@@ -202,12 +216,12 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
202
216
|
new PythonPluginAdapter(`${name}-${idSuffix}`, {
|
|
203
217
|
bridgeScript: probe.bridgePath,
|
|
204
218
|
pythonPath:
|
|
205
|
-
typeof
|
|
206
|
-
?
|
|
219
|
+
typeof testCfg.pythonPath === "string"
|
|
220
|
+
? testCfg.pythonPath
|
|
207
221
|
: probe.venvPython,
|
|
208
|
-
capabilities: mergeCapabilities(
|
|
209
|
-
...(typeof
|
|
210
|
-
? { transportTimeoutMs:
|
|
222
|
+
capabilities: mergeCapabilities(testCfg.capabilities),
|
|
223
|
+
...(typeof testCfg.transportTimeoutMs === "number"
|
|
224
|
+
? { transportTimeoutMs: testCfg.transportTimeoutMs }
|
|
211
225
|
: {}),
|
|
212
226
|
});
|
|
213
227
|
|
|
@@ -232,7 +246,7 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
232
246
|
getServerUrl: () => serverUrl,
|
|
233
247
|
createPlugin: async () => {
|
|
234
248
|
const p = buildAdapter("persist");
|
|
235
|
-
await p.init(
|
|
249
|
+
await p.init(testCfg);
|
|
236
250
|
return p;
|
|
237
251
|
},
|
|
238
252
|
});
|
|
@@ -247,12 +261,13 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
247
261
|
registerContributedParitySuite({
|
|
248
262
|
name,
|
|
249
263
|
probe,
|
|
250
|
-
...(
|
|
264
|
+
...(testCfg.capabilities !== undefined
|
|
251
265
|
? {
|
|
252
|
-
capabilities:
|
|
266
|
+
capabilities:
|
|
267
|
+
testCfg.capabilities as Partial<PluginCapabilities>,
|
|
253
268
|
}
|
|
254
269
|
: {}),
|
|
255
|
-
config:
|
|
270
|
+
config: testCfg,
|
|
256
271
|
getBaseUrl: async () => miniwobBaseUrl,
|
|
257
272
|
});
|
|
258
273
|
|
|
@@ -269,7 +284,7 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
269
284
|
describe("quirks", () => {
|
|
270
285
|
beforeAll(async () => {
|
|
271
286
|
const p = buildAdapter("quirks");
|
|
272
|
-
await p.init(
|
|
287
|
+
await p.init(testCfg);
|
|
273
288
|
quirks_ = await p.describeQuirks();
|
|
274
289
|
await p.cleanupAll().catch(() => {});
|
|
275
290
|
});
|
|
@@ -277,7 +292,7 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
277
292
|
it("eval_prefix: evaluate('1 + 1') === 2", async () => {
|
|
278
293
|
if (!quirks_.eval_prefix) return;
|
|
279
294
|
const p = buildAdapter("quirks-eval");
|
|
280
|
-
await p.init(
|
|
295
|
+
await p.init(testCfg);
|
|
281
296
|
const nav = await p.navigate(
|
|
282
297
|
`${serverUrl}/simple`,
|
|
283
298
|
"quirks-eval",
|
|
@@ -293,7 +308,7 @@ ${Array.from({ length: 80 }, (_, i) => `<p>Paragraph ${i + 1}: content to make t
|
|
|
293
308
|
it("scroll_via_wheel: scroll moves the page", async () => {
|
|
294
309
|
if (!quirks_.scroll_via_wheel) return;
|
|
295
310
|
const p = buildAdapter("quirks-scroll");
|
|
296
|
-
await p.init(
|
|
311
|
+
await p.init(testCfg);
|
|
297
312
|
const nav = await p.navigate(
|
|
298
313
|
`${serverUrl}/scroll`,
|
|
299
314
|
"quirks-scroll",
|