diff --git a/packages/brow-2-brow/package.json b/packages/brow-2-brow/package.json index c05e3723b..6a0e4c1a5 100644 --- a/packages/brow-2-brow/package.json +++ b/packages/brow-2-brow/package.json @@ -13,7 +13,7 @@ "build:dev": "mkdir -p dist && ln -fs ../src/index.html dist/index.html", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/brow-2-brow", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/cli/package.json b/packages/cli/package.json index 93ba4196a..6980b30e2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -18,7 +18,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/cli", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/create-package/package.json b/packages/create-package/package.json index 7b5a354ad..7a8e89708 100644 --- a/packages/create-package/package.json +++ b/packages/create-package/package.json @@ -31,7 +31,7 @@ "scripts": { "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/create-package", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/extension/package.json b/packages/extension/package.json index cd58e78c4..5c0932696 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -21,7 +21,7 @@ "build:browser": "OPEN_BROWSER=true yarn build:dev --watch", "build:vite": "vite build --configLoader runner --config vite.config.ts", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/extension", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", @@ -63,7 +63,7 @@ "@ocap/cli": "workspace:^", "@ocap/kernel-test": "workspace:^", "@ocap/repo-tools": "workspace:^", - "@playwright/test": "^1.54.2", + "@playwright/test": "^1.57.0", "@testing-library/jest-dom": "^6.6.3", "@types/chrome": "^0.0.313", "@types/react": "^17.0.11", @@ -83,7 +83,7 @@ "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^7.2.1", "jsdom": "^27.4.0", - "playwright": "^1.54.2", + "playwright": "^1.57.0", "prettier": "^3.5.3", "rimraf": "^6.0.1", "tsx": "^4.20.6", diff --git a/packages/extension/src/background.ts b/packages/extension/src/background.ts index 967103779..9fafe2f24 100644 --- a/packages/extension/src/background.ts +++ b/packages/extension/src/background.ts @@ -4,6 +4,8 @@ import { makeCapTPNotification, isCapTPNotification, getCapTPMessage, + isConsoleForwardMessage, + handleConsoleForwardMessage, } from '@metamask/kernel-browser-runtime'; import type { KernelFacade, @@ -115,9 +117,11 @@ async function main(): Promise { logger.info(result); }; - // Handle incoming CapTP messages from the kernel + // Handle incoming messages from offscreen (CapTP and console-forward) const drainPromise = offscreenStream.drain((message) => { - if (isCapTPNotification(message)) { + if (isConsoleForwardMessage(message)) { + handleConsoleForwardMessage(message, '[offscreen]'); + } else if (isCapTPNotification(message)) { const captpMessage = getCapTPMessage(message); backgroundCapTP.dispatch(captpMessage); } else { diff --git a/packages/extension/src/offscreen.ts b/packages/extension/src/offscreen.ts index c09ec2772..aa11ac189 100644 --- a/packages/extension/src/offscreen.ts +++ b/packages/extension/src/offscreen.ts @@ -2,6 +2,7 @@ import { makeIframeVatWorker, PlatformServicesServer, createRelayQueryString, + setupConsoleForwarding, } from '@metamask/kernel-browser-runtime'; import { delay, isJsonRpcMessage } from '@metamask/kernel-utils'; import type { JsonRpcMessage } from '@metamask/kernel-utils'; @@ -31,6 +32,9 @@ async function main(): Promise { JsonRpcMessage >(chrome.runtime, 'offscreen', 'background', isJsonRpcMessage); + // Set up console forwarding to background for Playwright capture + setupConsoleForwarding(backgroundStream); + const kernelStream = await makeKernelWorker(); // Handle messages from the background script / kernel diff --git a/packages/kernel-agents-repl/package.json b/packages/kernel-agents-repl/package.json index 7e3551baf..68d7b6fe8 100644 --- a/packages/kernel-agents-repl/package.json +++ b/packages/kernel-agents-repl/package.json @@ -32,7 +32,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/kernel-agents-repl", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-agents/package.json b/packages/kernel-agents/package.json index 05e84a383..2a4b857e8 100644 --- a/packages/kernel-agents/package.json +++ b/packages/kernel-agents/package.json @@ -132,7 +132,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/kernel-agents", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-browser-runtime/package.json b/packages/kernel-browser-runtime/package.json index 5ea33b466..9cc2c7480 100644 --- a/packages/kernel-browser-runtime/package.json +++ b/packages/kernel-browser-runtime/package.json @@ -48,7 +48,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-browser-runtime", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-browser-runtime", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-browser-runtime/src/index.test.ts b/packages/kernel-browser-runtime/src/index.test.ts index f52b98667..0a4415bdc 100644 --- a/packages/kernel-browser-runtime/src/index.test.ts +++ b/packages/kernel-browser-runtime/src/index.test.ts @@ -11,7 +11,9 @@ describe('index', () => { 'createRelayQueryString', 'getCapTPMessage', 'getRelaysFromCurrentLocation', + 'handleConsoleForwardMessage', 'isCapTPNotification', + 'isConsoleForwardMessage', 'makeBackgroundCapTP', 'makeCapTPNotification', 'makeIframeVatWorker', @@ -19,6 +21,7 @@ describe('index', () => { 'receiveInternalConnections', 'rpcHandlers', 'rpcMethodSpecs', + 'setupConsoleForwarding', ]); }); }); diff --git a/packages/kernel-browser-runtime/src/utils/console-forwarding.ts b/packages/kernel-browser-runtime/src/utils/console-forwarding.ts new file mode 100644 index 000000000..8df0d4be4 --- /dev/null +++ b/packages/kernel-browser-runtime/src/utils/console-forwarding.ts @@ -0,0 +1,94 @@ +import type { JsonRpcMessage } from '@metamask/kernel-utils'; +import type { DuplexStream } from '@metamask/streams'; +import type { JsonRpcNotification } from '@metamask/utils'; + +/** + * Message type for forwarding console output from one context to another. + * Used to capture console logs from offscreen documents in Playwright tests. + */ +export type ConsoleForwardMessage = JsonRpcNotification & { + method: 'console-forward'; + params: { + method: 'log' | 'debug' | 'info' | 'warn' | 'error'; + args: string[]; + }; +}; + +/** + * Type guard for console-forward messages. + * + * @param value - The value to check. + * @returns Whether the value is a ConsoleForwardMessage. + */ +export const isConsoleForwardMessage = ( + value: unknown, +): value is ConsoleForwardMessage => + typeof value === 'object' && + value !== null && + 'method' in value && + (value as { method: unknown }).method === 'console-forward'; + +/** + * Wraps console methods to forward messages to background via a stream. + * This enables capturing console output from contexts that Playwright cannot + * directly access (like offscreen documents). + * + * Call this early after the stream is created. After setup, console output + * will be forwarded to the stream recipient where it can be replayed. + * + * @param stream - The stream to write console messages to. + */ +export function setupConsoleForwarding( + stream: DuplexStream, +): void { + const originalConsole = { ...console }; + const consoleMethods = ['log', 'debug', 'info', 'warn', 'error'] as const; + + consoleMethods.forEach((consoleMethod) => { + // eslint-disable-next-line no-console + console[consoleMethod] = (...args: unknown[]) => { + // Call original console method + originalConsole[consoleMethod](...args); + + // Forward to background via stream + const message: ConsoleForwardMessage = { + jsonrpc: '2.0', + method: 'console-forward', + params: { + method: consoleMethod, + args: args.map((arg) => { + if (typeof arg === 'string') { + return arg; + } + if (typeof arg === 'number' || typeof arg === 'boolean') { + return String(arg); + } + // Objects, arrays, null, undefined, functions, symbols, etc. + return JSON.stringify(arg); + }), + }, + }; + stream.write(message).catch(() => { + // Ignore errors if stream isn't ready + }); + }; + }); + + harden(globalThis.console); +} + +/** + * Handles a console-forward message by replaying it to the local console. + * Use this in the stream handler to replay forwarded console output. + * + * @param message - The console-forward message to handle. + * @param prefix - Optional prefix to add to the message (e.g., '[offscreen]'). + */ +export function handleConsoleForwardMessage( + message: ConsoleForwardMessage, + prefix?: string, +): void { + const { method, args } = message.params; + // eslint-disable-next-line no-console + console[method](...(prefix ? [prefix, ...args] : args)); +} diff --git a/packages/kernel-browser-runtime/src/utils/index.test.ts b/packages/kernel-browser-runtime/src/utils/index.test.ts index 1defa9bf6..9d50bd8d2 100644 --- a/packages/kernel-browser-runtime/src/utils/index.test.ts +++ b/packages/kernel-browser-runtime/src/utils/index.test.ts @@ -7,7 +7,10 @@ describe('index', () => { expect(Object.keys(indexModule).sort()).toStrictEqual([ 'createRelayQueryString', 'getRelaysFromCurrentLocation', + 'handleConsoleForwardMessage', + 'isConsoleForwardMessage', 'parseRelayQueryString', + 'setupConsoleForwarding', ]); }); }); diff --git a/packages/kernel-browser-runtime/src/utils/index.ts b/packages/kernel-browser-runtime/src/utils/index.ts index c77e8b124..4e189403c 100644 --- a/packages/kernel-browser-runtime/src/utils/index.ts +++ b/packages/kernel-browser-runtime/src/utils/index.ts @@ -1 +1,2 @@ +export * from './console-forwarding.ts'; export * from './relay-query-string.ts'; diff --git a/packages/kernel-errors/package.json b/packages/kernel-errors/package.json index 32e836fa5..0569bac81 100644 --- a/packages/kernel-errors/package.json +++ b/packages/kernel-errors/package.json @@ -42,7 +42,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-errors", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-errors", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-language-model-service/package.json b/packages/kernel-language-model-service/package.json index cf1f66ed6..ecf260897 100644 --- a/packages/kernel-language-model-service/package.json +++ b/packages/kernel-language-model-service/package.json @@ -52,7 +52,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/kernel-language-model-service", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-platforms/package.json b/packages/kernel-platforms/package.json index d6ac09791..dee5d1571 100644 --- a/packages/kernel-platforms/package.json +++ b/packages/kernel-platforms/package.json @@ -52,7 +52,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/kernel-platforms", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-rpc-methods/package.json b/packages/kernel-rpc-methods/package.json index aff89af64..179af4746 100644 --- a/packages/kernel-rpc-methods/package.json +++ b/packages/kernel-rpc-methods/package.json @@ -42,7 +42,7 @@ "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-rpc-methods", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-rpc-methods", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-shims/package.json b/packages/kernel-shims/package.json index eed7d3e65..728d34836 100644 --- a/packages/kernel-shims/package.json +++ b/packages/kernel-shims/package.json @@ -34,7 +34,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-shims", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-shims", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-store/package.json b/packages/kernel-store/package.json index cca4438be..ca457f833 100644 --- a/packages/kernel-store/package.json +++ b/packages/kernel-store/package.json @@ -63,7 +63,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-store", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-store", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-test-local/package.json b/packages/kernel-test-local/package.json index 45e21eea0..ff6e9c617 100644 --- a/packages/kernel-test-local/package.json +++ b/packages/kernel-test-local/package.json @@ -13,7 +13,7 @@ }, "type": "module", "scripts": { - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-test/package.json b/packages/kernel-test/package.json index c4acfa7e2..2e38acea4 100644 --- a/packages/kernel-test/package.json +++ b/packages/kernel-test/package.json @@ -31,7 +31,7 @@ ], "scripts": { "build": "ocap bundle src/vats", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo './src/**/*.bundle'", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo './src/**/*.bundle' ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-ui/package.json b/packages/kernel-ui/package.json index 88abd5192..33eea5a7b 100644 --- a/packages/kernel-ui/package.json +++ b/packages/kernel-ui/package.json @@ -45,7 +45,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-ui", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-ui", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/kernel-utils/package.json b/packages/kernel-utils/package.json index 37813fea5..fed41a03b 100644 --- a/packages/kernel-utils/package.json +++ b/packages/kernel-utils/package.json @@ -62,7 +62,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-utils", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-utils", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/logger/package.json b/packages/logger/package.json index 2ab934ffc..acf2d6802 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -42,7 +42,7 @@ "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/logger", "changelog:update": "../../scripts/update-changelog.sh @metamask/logger", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/nodejs-test-workers/package.json b/packages/nodejs-test-workers/package.json index 5f6c0d67d..891ca82c3 100644 --- a/packages/nodejs-test-workers/package.json +++ b/packages/nodejs-test-workers/package.json @@ -32,7 +32,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/nodejs-test-workers", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/nodejs/package.json b/packages/nodejs/package.json index c095d65cb..6238d4ade 100644 --- a/packages/nodejs/package.json +++ b/packages/nodejs/package.json @@ -34,7 +34,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/nodejs", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/ocap-kernel/package.json b/packages/ocap-kernel/package.json index 10f251a7b..e60c78672 100644 --- a/packages/ocap-kernel/package.json +++ b/packages/ocap-kernel/package.json @@ -53,7 +53,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/ocap-kernel", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/ocap-kernel", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/omnium-gatherum/package.json b/packages/omnium-gatherum/package.json index 9b33af1dd..c40d8ea88 100644 --- a/packages/omnium-gatherum/package.json +++ b/packages/omnium-gatherum/package.json @@ -22,7 +22,7 @@ "build:browser": "OPEN_BROWSER=true yarn build:dev --watch", "build:vite": "vite build --configLoader runner --config vite.config.ts", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/omnium-gatherum", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", @@ -63,7 +63,7 @@ "@metamask/eslint-config-typescript": "^15.0.0", "@ocap/cli": "workspace:^", "@ocap/repo-tools": "workspace:^", - "@playwright/test": "^1.54.2", + "@playwright/test": "^1.57.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^12.1.5", "@types/chrome": "^0.0.313", @@ -85,7 +85,7 @@ "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^7.2.1", "jsdom": "^27.4.0", - "playwright": "^1.54.2", + "playwright": "^1.57.0", "prettier": "^3.5.3", "rimraf": "^6.0.1", "tsx": "^4.20.6", diff --git a/packages/remote-iterables/package.json b/packages/remote-iterables/package.json index 0ccadfde7..4ae85fc59 100644 --- a/packages/remote-iterables/package.json +++ b/packages/remote-iterables/package.json @@ -32,7 +32,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/remote-iterables", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 1a217dfa6..f0ba1be36 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -28,7 +28,7 @@ "dist/" ], "scripts": { - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", @@ -46,7 +46,7 @@ "@metamask/eslint-config-nodejs": "^15.0.0", "@metamask/eslint-config-typescript": "^15.0.0", "@metamask/superstruct": "^3.2.1", - "@playwright/test": "^1.54.2", + "@playwright/test": "^1.57.0", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", "@typescript-eslint/utils": "^8.29.0", diff --git a/packages/repo-tools/src/test-utils/extension.ts b/packages/repo-tools/src/test-utils/extension.ts index 065107f52..380ec767e 100644 --- a/packages/repo-tools/src/test-utils/extension.ts +++ b/packages/repo-tools/src/test-utils/extension.ts @@ -1,11 +1,20 @@ -import { chromium } from '@playwright/test'; -import type { BrowserContext, Page } from '@playwright/test'; +import { chromium, test } from '@playwright/test'; +import type { BrowserContext, ConsoleMessage, Page } from '@playwright/test'; +import { appendFileSync } from 'node:fs'; +import { mkdir } from 'node:fs/promises'; import { rm } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; export const sessionPath = path.resolve(os.tmpdir(), 'ocap-test'); +// Run ID is generated once per Playwright invocation (per worker process) +// This allows associating all test log files from the same run +const runId = new Date() + .toISOString() + .slice(0, -5) // Remove ".123Z" + .replace(/[:.]/gu, '-'); // Make filename-safe + type Options = { contextId?: string | undefined; extensionPath: string; @@ -21,7 +30,7 @@ type Options = { * @param options.extensionPath - The path to the extension dist folder. * @param options.onPageLoad - Optional callback to run after the extension is loaded. Useful for * e.g. waiting for components to be visible before proceeding with a test. - * @returns The extension context, extension ID, and popup page + * @returns The extension context, extension ID, popup page, and log file path */ export const makeLoadExtension = async ({ contextId, @@ -31,6 +40,7 @@ export const makeLoadExtension = async ({ browserContext: BrowserContext; extensionId: string; popupPage: Page; + logFilePath: string; }> => { const workerIndex = process.env.TEST_WORKER_INDEX ?? '0'; // Use provided contextId or fall back to workerIndex for separate user data dirs @@ -38,6 +48,33 @@ export const makeLoadExtension = async ({ const userDataDir = path.join(sessionPath, effectiveContextId); await rm(userDataDir, { recursive: true, force: true }); + // Set up log file for capturing console output from extension contexts + const packageRoot = path.dirname(extensionPath); // extensionPath is /dist + const logsDir = path.join(packageRoot, 'logs'); + await mkdir(logsDir, { recursive: true }); + const testTitle = test + .info() + .titlePath.join('-') + .replace(/[^a-zA-Z0-9-]/gu, '_'); // Make filename-safe + const logFilePath = path.join(logsDir, `${runId}-${testTitle}.log`); + + // Attach log file path to test results (viewable in Playwright HTML report) + await test.info().attach('console-logs', { + body: logFilePath, + contentType: 'text/plain', + }); + + const writeLog = (source: string, consoleMessage: ConsoleMessage): void => { + const logTimestamp = new Date().toISOString().slice(0, -5); + const text = consoleMessage.text(); + const type = consoleMessage.type(); + // eslint-disable-next-line n/no-sync + appendFileSync( + logFilePath, + `[${logTimestamp}] [${source}] [${type}] ${text}\n`, + ); + }; + const browserArgs = [ `--disable-features=ExtensionDisableUnsupportedDeveloper`, `--disable-extensions-except=${extensionPath}`, @@ -56,6 +93,23 @@ export const makeLoadExtension = async ({ args: browserArgs, }); + // Capture background service worker console logs + browserContext.on('serviceworker', (worker) => { + worker.on('console', (consoleMessage) => + writeLog('background', consoleMessage), + ); + }); + + // Capture console logs from extension pages (offscreen document, etc.) + // Note: Pages may start at about:blank, so we attach the listener and check URL in the handler + browserContext.on('page', (page) => { + page.on('console', (consoleMessage) => { + if (page.url().includes('offscreen.html')) { + writeLog('offscreen', consoleMessage); + } + }); + }); + // Wait for the extension to be loaded await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -70,8 +124,11 @@ export const makeLoadExtension = async ({ } const popupPage = await browserContext.newPage(); + popupPage.on('console', (consoleMessage) => + writeLog('popup', consoleMessage), + ); await popupPage.goto(`chrome-extension://${extensionId}/popup.html`); await onPageLoad(popupPage); - return { browserContext, extensionId, popupPage }; + return { browserContext, extensionId, popupPage, logFilePath }; }; diff --git a/packages/streams/package.json b/packages/streams/package.json index 712313989..f5a58948b 100644 --- a/packages/streams/package.json +++ b/packages/streams/package.json @@ -53,7 +53,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/streams", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/streams", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", @@ -102,7 +102,7 @@ "eslint-plugin-n": "^17.17.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^7.2.1", - "playwright": "^1.54.2", + "playwright": "^1.57.0", "prettier": "^3.5.3", "rimraf": "^6.0.1", "ses": "^1.14.0", diff --git a/packages/template-package/package.json b/packages/template-package/package.json index 79f86f7f0..ae6ab27fc 100644 --- a/packages/template-package/package.json +++ b/packages/template-package/package.json @@ -32,7 +32,7 @@ "build": "ts-bridge --project tsconfig.build.json --no-references --clean", "build:docs": "typedoc", "changelog:validate": "../../scripts/validate-changelog.sh @ocap/template-package", - "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo", + "clean": "rimraf --glob './*.tsbuildinfo' ./.eslintcache ./coverage ./dist ./.turbo ./logs", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies", "lint:dependencies": "depcheck --quiet", "lint:eslint": "eslint . --cache", diff --git a/yarn.config.cjs b/yarn.config.cjs index 8077c49fa..e1b6d68b1 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -204,8 +204,12 @@ module.exports = defineConfig({ expectWorkspaceField(workspace, 'scripts.build:docs', 'typedoc'); } - // All packages except the root must have a "clean" script. - expectWorkspaceField(workspace, 'scripts.clean'); + // All packages except the root must have a "clean" script that includes ./logs. + expectWorkspaceField(workspace, 'scripts.clean', (currentValue) => + typeof currentValue === 'string' && !currentValue.includes('./logs') + ? `${currentValue} ./logs` + : currentValue, + ); // No non-root packages may have a "prepack" script. workspace.unset('scripts.prepack'); diff --git a/yarn.lock b/yarn.lock index 8fdc13dfa..6c6bccb3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2924,7 +2924,7 @@ __metadata: eslint-plugin-n: "npm:^17.17.0" eslint-plugin-prettier: "npm:^5.2.6" eslint-plugin-promise: "npm:^7.2.1" - playwright: "npm:^1.54.2" + playwright: "npm:^1.57.0" prettier: "npm:^3.5.3" rimraf: "npm:^6.0.1" ses: "npm:^1.14.0" @@ -3471,7 +3471,7 @@ __metadata: "@ocap/cli": "workspace:^" "@ocap/kernel-test": "workspace:^" "@ocap/repo-tools": "workspace:^" - "@playwright/test": "npm:^1.54.2" + "@playwright/test": "npm:^1.57.0" "@testing-library/jest-dom": "npm:^6.6.3" "@types/chrome": "npm:^0.0.313" "@types/react": "npm:^17.0.11" @@ -3491,7 +3491,7 @@ __metadata: eslint-plugin-prettier: "npm:^5.2.6" eslint-plugin-promise: "npm:^7.2.1" jsdom: "npm:^27.4.0" - playwright: "npm:^1.54.2" + playwright: "npm:^1.57.0" prettier: "npm:^3.5.3" react: "npm:^17.0.2" react-dom: "npm:^17.0.2" @@ -3931,7 +3931,7 @@ __metadata: "@metamask/streams": "workspace:^" "@ocap/cli": "workspace:^" "@ocap/repo-tools": "workspace:^" - "@playwright/test": "npm:^1.54.2" + "@playwright/test": "npm:^1.57.0" "@testing-library/jest-dom": "npm:^6.6.3" "@testing-library/react": "npm:^12.1.5" "@types/chrome": "npm:^0.0.313" @@ -3953,7 +3953,7 @@ __metadata: eslint-plugin-prettier: "npm:^5.2.6" eslint-plugin-promise: "npm:^7.2.1" jsdom: "npm:^27.4.0" - playwright: "npm:^1.54.2" + playwright: "npm:^1.57.0" prettier: "npm:^3.5.3" react: "npm:^17.0.2" react-dom: "npm:^17.0.2" @@ -4020,7 +4020,7 @@ __metadata: "@metamask/eslint-config-nodejs": "npm:^15.0.0" "@metamask/eslint-config-typescript": "npm:^15.0.0" "@metamask/superstruct": "npm:^3.2.1" - "@playwright/test": "npm:^1.54.2" + "@playwright/test": "npm:^1.57.0" "@typescript-eslint/eslint-plugin": "npm:^8.29.0" "@typescript-eslint/parser": "npm:^8.29.0" "@typescript-eslint/utils": "npm:^8.29.0" @@ -4540,14 +4540,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:^1.54.2": - version: 1.54.2 - resolution: "@playwright/test@npm:1.54.2" +"@playwright/test@npm:^1.57.0": + version: 1.57.0 + resolution: "@playwright/test@npm:1.57.0" dependencies: - playwright: "npm:1.54.2" + playwright: "npm:1.57.0" bin: playwright: cli.js - checksum: 10/33d6c0780ef6cca12c008cae166ba06978ae975138a1654e14cd8691ad7826dbbc03e492ed42c65889c44ca893d0fd47d8ae4dc8d824020d7f767260b395dc6b + checksum: 10/07f5ba4841b2db1dea70d821004c5156b692488e13523c096ce3487d30f95f34ccf30ba6467ece60c86faac27ae382213b7eacab48a695550981b2e811e5e579 languageName: node linkType: hard @@ -12682,27 +12682,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.54.2": - version: 1.54.2 - resolution: "playwright-core@npm:1.54.2" +"playwright-core@npm:1.57.0": + version: 1.57.0 + resolution: "playwright-core@npm:1.57.0" bin: playwright-core: cli.js - checksum: 10/8eb8b37d7b02c2394f85567c5703464ee7c85929e1de7118946548a0277cacd41a6f247b8f7eb50c921433346f785356eff8132a9d3c6c2dcf3402c6a83e4f18 + checksum: 10/ec066602f0196f036006caee14a30d0a57533a76673bb9a0c609ef56e21decf018f0e8d402ba2fb18251393be6a1c9e193c83266f1670fe50838c5340e220de0 languageName: node linkType: hard -"playwright@npm:1.54.2, playwright@npm:^1.54.2": - version: 1.54.2 - resolution: "playwright@npm:1.54.2" +"playwright@npm:1.57.0, playwright@npm:^1.57.0": + version: 1.57.0 + resolution: "playwright@npm:1.57.0" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.54.2" + playwright-core: "npm:1.57.0" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10/3f4fedc1e2290b6da6960eedf0d78b9097251848b499b0627186ca85e94178fe6be1590c4926246ff4dad5729fc80ab63fee6554fe736d29c0a1808ec483467f + checksum: 10/241559210f98ef11b6bd6413f2d29da7ef67c7865b72053192f0d164fab9e0d3bd47913b3351d5de6433a8aff2d8424d4b8bd668df420bf4dda7ae9fcd37b942 languageName: node linkType: hard