@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 +170 -45
- package/dist/cjs/index.cjs +431 -530
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +431 -530
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +49 -18
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,48 +1,137 @@
|
|
|
1
1
|
# @ricsam/isolate-playwright
|
|
2
2
|
|
|
3
|
-
Playwright bridge for running browser
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
45
|
-
const results = await
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
- `
|
|
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
|
-
|
|
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
|
-
- `
|
|
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
|
-
|
|
221
|
+
## Setup Options
|
|
103
222
|
|
|
104
223
|
```typescript
|
|
105
|
-
interface
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|