@ricsam/isolate-runtime 0.1.14 → 0.1.16

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
@@ -115,19 +115,16 @@ interface RuntimeOptions {
115
115
  cwd?: string;
116
116
  /** Enable test environment (describe, it, expect) */
117
117
  testEnvironment?: boolean | TestEnvironmentOptions;
118
- /** Playwright options - user provides page object */
118
+ /** Playwright options (handler-first public API) */
119
119
  playwright?: PlaywrightOptions;
120
120
  }
121
121
 
122
122
  interface PlaywrightOptions {
123
- page: import("playwright").Page;
123
+ handler: (op: PlaywrightOperation) => Promise<PlaywrightResult>;
124
124
  timeout?: number;
125
125
  /** Print browser console logs to stdout */
126
126
  console?: boolean;
127
- /** Browser console log callback (from the page, not sandbox) */
128
- onBrowserConsoleLog?: (entry: { level: string; stdout: string; timestamp: number }) => void;
129
- onNetworkRequest?: (info: { url: string; method: string; headers: Record<string, string>; timestamp: number }) => void;
130
- onNetworkResponse?: (info: { url: string; status: number; headers: Record<string, string>; timestamp: number }) => void;
127
+ onEvent?: (event: PlaywrightEvent) => void;
131
128
  }
132
129
  ```
133
130
 
@@ -309,20 +306,27 @@ type TestEvent =
309
306
 
310
307
  ## Playwright Integration
311
308
 
312
- Run browser automation with untrusted code. **You provide the Playwright page object**:
309
+ Run browser automation with untrusted code. Public API is handler-first:
310
+
311
+ ```typescript
312
+ import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
313
+
314
+ playwright: { handler: defaultPlaywrightHandler(page) }
315
+ ```
313
316
 
314
317
  ### Script Mode (No Tests)
315
318
 
316
319
  ```typescript
317
320
  import { createRuntime } from "@ricsam/isolate-runtime";
318
321
  import { chromium } from "playwright";
322
+ import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
319
323
 
320
324
  const browser = await chromium.launch({ headless: true });
321
325
  const page = await browser.newPage();
322
326
 
323
327
  const runtime = await createRuntime({
324
328
  playwright: {
325
- page,
329
+ handler: defaultPlaywrightHandler(page),
326
330
  console: true, // Print browser console to stdout
327
331
  },
328
332
  });
@@ -349,6 +353,7 @@ Combine `testEnvironment` and `playwright` for browser testing. Playwright exten
349
353
  ```typescript
350
354
  import { createRuntime } from "@ricsam/isolate-runtime";
351
355
  import { chromium } from "playwright";
356
+ import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
352
357
 
353
358
  const browser = await chromium.launch({ headless: true });
354
359
  const page = await browser.newPage();
@@ -356,8 +361,12 @@ const page = await browser.newPage();
356
361
  const runtime = await createRuntime({
357
362
  testEnvironment: true, // Provides describe, it, expect
358
363
  playwright: {
359
- page,
360
- onBrowserConsoleLog: (entry) => console.log("[browser]", entry.stdout),
364
+ handler: defaultPlaywrightHandler(page),
365
+ onEvent: (event) => {
366
+ if (event.type === "browserConsoleLog") {
367
+ console.log("[browser]", event.stdout);
368
+ }
369
+ },
361
370
  },
362
371
  });
363
372
 
@@ -383,6 +392,74 @@ await runtime.dispose();
383
392
  await browser.close();
384
393
  ```
385
394
 
395
+ ### Multi-Page Testing
396
+
397
+ For tests that need multiple pages or browser contexts, provide `createPage` and/or `createContext` callbacks:
398
+
399
+ ```typescript
400
+ import { createRuntime } from "@ricsam/isolate-runtime";
401
+ import { chromium } from "playwright";
402
+ import { defaultPlaywrightHandler } from "@ricsam/isolate-playwright/client";
403
+
404
+ const browser = await chromium.launch({ headless: true });
405
+ const browserContext = await browser.newContext();
406
+ const page = await browserContext.newPage();
407
+
408
+ const runtime = await createRuntime({
409
+ testEnvironment: true,
410
+ playwright: {
411
+ handler: defaultPlaywrightHandler(page, {
412
+ // Called when isolate code calls context.newPage()
413
+ createPage: async (context) => context.newPage(),
414
+ // Called when isolate code calls browser.newContext()
415
+ createContext: async (options) => browser.newContext(options),
416
+ }),
417
+ },
418
+ });
419
+
420
+ await runtime.eval(`
421
+ test('multi-page test', async () => {
422
+ // Create additional pages
423
+ const page2 = await context.newPage();
424
+
425
+ // Navigate independently
426
+ await page.goto('https://example.com/page1');
427
+ await page2.goto('https://example.com/page2');
428
+
429
+ // Work with multiple pages
430
+ await page.locator('#button').click();
431
+ await page2.locator('#input').fill('text');
432
+
433
+ await page2.close();
434
+ });
435
+
436
+ test('multi-context test', async () => {
437
+ // Create isolated context (separate cookies, storage)
438
+ const ctx2 = await browser.newContext();
439
+ const page2 = await ctx2.newPage();
440
+
441
+ // Cookies are isolated between contexts
442
+ await context.addCookies([{ name: 'test', value: '1', domain: 'example.com', path: '/' }]);
443
+ const ctx1Cookies = await context.cookies();
444
+ const ctx2Cookies = await ctx2.cookies();
445
+
446
+ expect(ctx1Cookies.some(c => c.name === 'test')).toBe(true);
447
+ expect(ctx2Cookies.some(c => c.name === 'test')).toBe(false);
448
+
449
+ await ctx2.close();
450
+ });
451
+ `);
452
+
453
+ const results = await runtime.testEnvironment.runTests();
454
+ await runtime.dispose();
455
+ await browser.close();
456
+ ```
457
+
458
+ **Behavior without lifecycle callbacks:**
459
+ - `context.newPage()` without `createPage`: Throws error
460
+ - `browser.newContext()` without `createContext`: Throws error
461
+ - `context.cookies()`, `context.addCookies()`, `context.clearCookies()`: Work without callbacks
462
+
386
463
  ## Included APIs
387
464
 
388
465
  - Core (Blob, File, streams, URL, TextEncoder/Decoder)
@@ -394,20 +471,7 @@ await browser.close();
394
471
  - Fetch API
395
472
  - File System (if handler provided)
396
473
  - Test Environment (if enabled)
397
- - Playwright (if page provided)
398
-
399
- ## Legacy API
400
-
401
- For backwards compatibility with code that needs direct isolate/context access:
402
-
403
- ```typescript
404
- import { createLegacyRuntime } from "@ricsam/isolate-runtime";
405
-
406
- const runtime = await createLegacyRuntime();
407
- // runtime.isolate and runtime.context are available
408
- await runtime.context.eval(`console.log("Hello")`);
409
- runtime.dispose(); // sync
410
- ```
474
+ - Playwright (if handler provided)
411
475
 
412
476
  ## License
413
477