Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions src/experimental/browser-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { assert, layer } from "@effect/vitest";
import { Chunk, Effect, Fiber, Stream } from "effect";
import { chromium } from "playwright-core";
import { Playwright } from "../index";
import * as BrowserUtils from "./browser-utils";

layer(Playwright.layer)("BrowserUtils", (it) => {
it.scoped("allPages should return all pages from all contexts", () =>
Effect.gen(function* () {
const playwright = yield* Playwright;
const browser = yield* playwright.launchScoped(chromium);

const context1 = yield* browser.newContext();
yield* context1.newPage;
yield* context1.newPage;

const context2 = yield* browser.newContext();
yield* context2.newPage;

const pages = yield* BrowserUtils.allPages(browser);
assert.strictEqual(pages.length, 3);
}),
);

it.scoped("allFrames should return all frames from all pages", () =>
Effect.gen(function* () {
const playwright = yield* Playwright;
const browser = yield* playwright.launchScoped(chromium);

yield* browser.newPage();
yield* browser.newPage();

// Retrieve frames
const frames = yield* BrowserUtils.allFrames(browser);

// Each page has at least one frame (the main frame)
assert.strictEqual(frames.length, 2);
}),
);

it.scoped(
"allFrameNavigatedEventStream should capture navigations from existing and new pages across multiple contexts",
() =>
Effect.gen(function* () {
const playwright = yield* Playwright;
const browser = yield* playwright.launchScoped(chromium);

// Setup contexts and an initial page
const context1 = yield* browser.newContext();
const context2 = yield* browser.newContext();
const page1 = yield* context1.newPage;

// Start the event stream
const stream = BrowserUtils.allFrameNavigatedEventStream(browser);
const eventFiber = yield* stream.pipe(Stream.runCollect, Effect.fork);

// 1. Navigate existing page
yield* page1.goto(
"data:text/html,<div>Page 1 (existing in context 1)</div>",
);

// 2. Create new page in context 1 and navigate
const page2 = yield* context1.newPage;
yield* page2.goto(
"data:text/html,<div>Page 2 (new in context 1)</div>",
);

// 3. Create new page in context 2 and navigate
const page3 = yield* context2.newPage;

yield* page3.goto(
"data:text/html,<div>Page 3 (new in context 2)</div>",
);

// 4. Create new page in context 2 and navigate
const page4 = yield* context2.newPage;

yield* page4.goto(
"data:text/html,<div>Page 4 (new in context 2)</div>",
);

yield* browser.close;

const events = yield* Fiber.join(eventFiber);
assert.strictEqual(Chunk.size(events), 4);
}),
);

it.scoped("page eventStream should capture framenavigated", () =>
Effect.gen(function* () {
const playwright = yield* Playwright;
const browser = yield* playwright.launchScoped(chromium);
const page = yield* browser.newPage();

const fiber = yield* BrowserUtils.allFrameNavigatedEventStream(
browser,
).pipe(Stream.take(1), Stream.runCollect, Effect.fork);

yield* page.goto("https://example.com");

const events = yield* Fiber.join(fiber);
assert.strictEqual(Chunk.size(events), 1);
}),
);
});
58 changes: 58 additions & 0 deletions src/experimental/browser-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Array, Effect, pipe, Stream } from "effect";
import type { PlaywrightBrowserService } from "../browser";

/**
* Returns all pages in the browser from all contexts.
* @category util
*/
export const allPages = (browser: PlaywrightBrowserService) =>
browser.contexts.pipe(
Effect.flatMap((contexts) =>
Effect.all(contexts.map((context) => context.pages)),
),
Effect.map(Array.flatten),
);

/**
* Returns all frames in the browser from all pages in all contexts.
* @category util
*/
export const allFrames = (browser: PlaywrightBrowserService) =>
allPages(browser).pipe(
Effect.flatMap((pages) => Effect.all(pages.map((page) => page.frames))),
);

/**
* Returns a stream of all framenavigated events for all current and future pages in the browser.
* In all current contexts (but not future contexts).
* @category util
*/
export const allFrameNavigatedEventStream = (
browser: PlaywrightBrowserService,
) =>
Effect.gen(function* () {
const contexts = yield* browser.contexts;
const pages = yield* pipe(
contexts.map((c) => c.pages),
Effect.all,
Effect.map(Array.flatten),
);

// listen for framenavigated for all current pages
const currentPages = pages.map((page) =>
page.eventStream("framenavigated"),
);

// and all future pages
const newPages = pipe(
contexts.map((c) => c.eventStream("page")),
Stream.mergeAll({ concurrency: "unbounded" }),
Stream.flatMap((page) => page.eventStream("framenavigated"), {
concurrency: "unbounded",
}),
);

return Stream.mergeAll([newPages, ...currentPages], {
concurrency: "unbounded",
});
}).pipe(Stream.unwrap);
1 change: 1 addition & 0 deletions src/experimental/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
* @packageDocumentation
*/

export * as BrowserUtils from "./browser-utils";
export * as PlaywrightEnvironment from "./environment";