@ricsam/isolate-playwright 0.1.2 → 0.1.4

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 CHANGED
@@ -1,48 +1,137 @@
1
1
  # @ricsam/isolate-playwright
2
2
 
3
- Playwright bridge for running browser tests in a V8 sandbox. Execute untrusted Playwright test code against a real browser page while keeping the test logic isolated.
3
+ Playwright bridge for running browser automation in a V8 sandbox. Execute untrusted Playwright code against a real browser page while keeping the logic isolated.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm add @ricsam/isolate-playwright playwright
9
+ ```
10
+
11
+ ## Usage with isolate-runtime (Recommended)
12
+
13
+ The easiest way to use this package is through `@ricsam/isolate-runtime`:
14
+
15
+ ### Script Mode (No Tests)
16
+
17
+ Run browser automation scripts without a test framework:
18
+
19
+ ```typescript
20
+ import { createRuntime } from "@ricsam/isolate-runtime";
21
+ import { chromium } from "playwright";
22
+
23
+ const browser = await chromium.launch({ headless: true });
24
+ const page = await browser.newPage();
25
+
26
+ const runtime = await createRuntime({
27
+ playwright: {
28
+ page,
29
+ baseUrl: "https://example.com",
30
+ console: true, // Print browser console logs to stdout
31
+ },
32
+ });
33
+
34
+ // Run a script - page is available, but expect is not
35
+ await runtime.eval(`
36
+ await page.goto("/");
37
+ const title = await page.title();
38
+ console.log("Page title:", title);
39
+ `);
40
+
41
+ // Get collected network data
42
+ const data = runtime.playwright.getCollectedData();
43
+ console.log("Network requests:", data.networkRequests.length);
44
+
45
+ await runtime.dispose();
46
+ await browser.close();
47
+ ```
48
+
49
+ ### Test Mode (With Test Framework)
50
+
51
+ For tests, enable `testEnvironment` which provides `describe`, `it`, and `expect`. Playwright extends `expect` with locator matchers:
52
+
53
+ ```typescript
54
+ import { createRuntime } from "@ricsam/isolate-runtime";
55
+ import { chromium } from "playwright";
56
+
57
+ const browser = await chromium.launch({ headless: true });
58
+ const page = await browser.newPage();
59
+
60
+ const runtime = await createRuntime({
61
+ testEnvironment: true, // Provides describe, it, expect
62
+ playwright: {
63
+ page,
64
+ baseUrl: "https://example.com",
65
+ onBrowserConsoleLog: (entry) => console.log("[browser]", entry.level, ...entry.args),
66
+ onNetworkRequest: (info) => console.log("Request:", info.url),
67
+ },
68
+ });
69
+
70
+ await runtime.eval(`
71
+ describe("homepage", () => {
72
+ it("loads correctly", async () => {
73
+ await page.goto("/");
74
+ const heading = page.getByRole("heading", { name: "Example Domain" });
75
+ await expect(heading).toBeVisible(); // Locator matcher from playwright
76
+ expect(await page.title()).toBe("Example Domain"); // Primitive matcher from test-environment
77
+ });
78
+ });
79
+ `);
80
+
81
+ // Run tests using test-environment
82
+ const results = await runtime.testEnvironment.runTests();
83
+ console.log(`${results.passed}/${results.total} tests passed`);
84
+
85
+ // Get collected browser data
86
+ const data = runtime.playwright.getCollectedData();
87
+ console.log("Browser console logs:", data.browserConsoleLogs);
88
+
89
+ await runtime.dispose();
90
+ await browser.close();
91
+ ```
92
+
93
+ ## Low-level Usage (Direct ivm)
94
+
95
+ For advanced use cases with direct isolated-vm access:
4
96
 
5
97
  ```typescript
6
98
  import ivm from "isolated-vm";
7
99
  import { chromium } from "playwright";
8
- import { setupPlaywright, runPlaywrightTests } from "@ricsam/isolate-playwright";
100
+ import { setupPlaywright } from "@ricsam/isolate-playwright";
101
+ import { setupTestEnvironment, runTests } from "@ricsam/isolate-test-environment";
9
102
 
10
- // Create browser and page
11
103
  const browser = await chromium.launch();
12
104
  const page = await browser.newPage();
13
105
 
14
- // Create isolate and context
15
106
  const isolate = new ivm.Isolate();
16
107
  const context = await isolate.createContext();
17
108
 
18
- // Setup playwright bridge
109
+ // Setup test-environment first (provides describe, it, expect)
110
+ await setupTestEnvironment(context);
111
+
112
+ // Then setup playwright (extends expect with locator matchers)
19
113
  const handle = await setupPlaywright(context, {
20
114
  page,
21
115
  timeout: 30000,
22
116
  baseUrl: "https://example.com",
23
117
  onNetworkRequest: (info) => console.log("Request:", info.url),
24
118
  onNetworkResponse: (info) => console.log("Response:", info.status),
25
- onConsoleLog: (level, ...args) => console.log(`[${level}]`, ...args),
119
+ onBrowserConsoleLog: (entry) => console.log(`[${entry.level}]`, ...entry.args),
26
120
  });
27
121
 
28
122
  // Load and run untrusted test code
29
123
  await context.eval(`
30
- test("homepage loads correctly", async () => {
31
- await page.goto("/");
32
- const heading = page.getByRole("heading", { name: "Example Domain" });
33
- await expect(heading).toBeVisible();
34
- });
35
-
36
- test("can interact with elements", async () => {
37
- const link = page.locator("a");
38
- await expect(link).toBeVisible();
39
- const text = await link.textContent();
40
- expect(text).toContain("More information");
124
+ describe("homepage", () => {
125
+ it("loads correctly", async () => {
126
+ await page.goto("/");
127
+ const heading = page.getByRole("heading", { name: "Example Domain" });
128
+ await expect(heading).toBeVisible();
129
+ });
41
130
  });
42
131
  `);
43
132
 
44
- // Run tests and get results
45
- const results = await runPlaywrightTests(context);
133
+ // Run tests
134
+ const results = await runTests(context);
46
135
  console.log(`${results.passed}/${results.total} tests passed`);
47
136
 
48
137
  // Cleanup
@@ -52,18 +141,44 @@ isolate.dispose();
52
141
  await browser.close();
53
142
  ```
54
143
 
55
- **Injected Globals (in isolate):**
144
+ ## Handler-based API (for Remote Execution)
145
+
146
+ For daemon/client architectures where the browser runs on the client:
147
+
148
+ ```typescript
149
+ import { createPlaywrightHandler, setupPlaywright, type PlaywrightCallback } from "@ricsam/isolate-playwright";
150
+ import { chromium } from "playwright";
151
+
152
+ // On the client: create handler from page
153
+ const browser = await chromium.launch();
154
+ const page = await browser.newPage();
155
+ const handler: PlaywrightCallback = createPlaywrightHandler(page, {
156
+ timeout: 30000,
157
+ baseUrl: "https://example.com",
158
+ });
159
+
160
+ // On the daemon: setup playwright with handler (instead of page)
161
+ const handle = await setupPlaywright(context, {
162
+ handler, // Handler callback instead of direct page
163
+ onBrowserConsoleLog: (entry) => sendToClient("browserConsoleLog", entry),
164
+ });
165
+ ```
166
+
167
+ ## Injected Globals (in isolate)
168
+
56
169
  - `page` - Page object with navigation and locator methods
57
- - `test(name, fn)` - Register a test
58
- - `expect(actual)` - Assertion helper for locators and primitives
59
170
  - `Locator` - Locator class for element interactions
171
+ - `expect` - Extended with locator matchers (only if test-environment is loaded first)
172
+
173
+ ## Page Methods
60
174
 
61
- **Page Methods:**
62
175
  - `page.goto(url, options?)` - Navigate to URL
63
176
  - `page.reload()` - Reload page
64
177
  - `page.url()` - Get current URL (sync)
65
178
  - `page.title()` - Get page title
66
179
  - `page.content()` - Get page HTML
180
+ - `page.click(selector)` - Click element (shorthand)
181
+ - `page.fill(selector, value)` - Fill input (shorthand)
67
182
  - `page.waitForSelector(selector, options?)` - Wait for element
68
183
  - `page.waitForTimeout(ms)` - Wait for milliseconds
69
184
  - `page.waitForLoadState(state?)` - Wait for load state
@@ -74,43 +189,53 @@ await browser.close();
74
189
  - `page.getByLabel(label)` - Get locator by label
75
190
  - `page.getByPlaceholder(text)` - Get locator by placeholder
76
191
  - `page.getByTestId(id)` - Get locator by test ID
192
+ - `page.request.get(url)` - HTTP GET request with page cookies
193
+ - `page.request.post(url, options?)` - HTTP POST request with page cookies
194
+
195
+ ## Locator Methods
77
196
 
78
- **Locator Methods:**
79
197
  - `click()`, `dblclick()`, `hover()`, `focus()`
80
198
  - `fill(text)`, `type(text)`, `clear()`, `press(key)`
81
199
  - `check()`, `uncheck()`, `selectOption(value)`
82
200
  - `textContent()`, `inputValue()`
83
201
  - `isVisible()`, `isEnabled()`, `isChecked()`, `count()`
202
+ - `nth(index)` - Get nth matching element
203
+
204
+ ## Expect Matchers (for Locators)
205
+
206
+ These matchers are available when using playwright with test-environment:
84
207
 
85
- **Expect Matchers (for Locators):**
86
- - `toBeVisible()`, `toBeEnabled()`, `toBeChecked()`
87
- - `toContainText(text)`, `toHaveValue(value)`
208
+ - `toBeVisible(options?)`, `toBeEnabled(options?)`, `toBeChecked(options?)`
209
+ - `toContainText(text, options?)`, `toHaveValue(value, options?)`
88
210
  - All matchers support `.not` modifier
211
+ - All matchers support `{ timeout: number }` option
89
212
 
90
- **Expect Matchers (for Primitives):**
91
- - `toBe(expected)`, `toEqual(expected)`
92
- - `toBeTruthy()`, `toBeFalsy()`
93
- - `toContain(item)`
213
+ ## Handle Methods
94
214
 
95
- **Handle Methods:**
96
215
  - `dispose()` - Clean up event listeners
97
- - `getConsoleLogs()` - Get captured browser console logs
216
+ - `getBrowserConsoleLogs()` - Get captured browser console logs
98
217
  - `getNetworkRequests()` - Get captured network requests
99
218
  - `getNetworkResponses()` - Get captured network responses
100
219
  - `clearCollected()` - Clear all collected data
101
220
 
102
- **Test Results:**
221
+ ## Setup Options
103
222
 
104
223
  ```typescript
105
- interface PlaywrightExecutionResult {
106
- passed: number;
107
- failed: number;
108
- total: number;
109
- results: Array<{
110
- name: string;
111
- passed: boolean;
112
- error?: string;
113
- duration: number;
114
- }>;
224
+ interface PlaywrightSetupOptions {
225
+ page?: Page; // Direct page object (for local use)
226
+ handler?: PlaywrightCallback; // Handler callback (for remote use)
227
+ timeout?: number; // Default timeout for operations
228
+ baseUrl?: string; // Base URL for relative navigation
229
+ console?: boolean; // Route browser console logs through console handler
230
+ onEvent?: (event: PlaywrightEvent) => void; // Unified event callback
115
231
  }
116
- ```
232
+
233
+ type PlaywrightEvent =
234
+ | { type: "browserConsoleLog"; level: string; args: unknown[]; timestamp: number }
235
+ | { type: "networkRequest"; url: string; method: string; headers: Record<string, string>; ... }
236
+ | { type: "networkResponse"; url: string; status: number; headers: Record<string, string>; ... };
237
+ ```
238
+
239
+ ## License
240
+
241
+ MIT