diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 25b32dc831..8d9be580d9 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -13,17 +13,14 @@ import { IntakeRegistry } from './intakeRegistry' import { flushEvents } from './flushEvents' import type { Servers } from './httpServers' import { getTestServers, waitForServersIdle } from './httpServers' -import type { SetupFactory, SetupOptions } from './pageSetups' -import { html, DEFAULT_SETUPS, npmSetup, reactSetup } from './pageSetups' +import type { SetupFactory, SetupOptions, WorkerOptions } from './pageSetups' +import { buildWorkerScript, html, DEFAULT_SETUPS, npmSetup, reactSetup } from './pageSetups' import { createIntakeServerApp } from './serverApps/intake' import { createMockServerApp } from './serverApps/mock' import type { Extension } from './createExtension' import { isBrowserStack } from './environment' -interface LogsWorkerOptions { - importScript?: boolean - nativeLog?: boolean -} +import '../types/global' export function createTest(title: string) { return new TestBuilder(title) @@ -42,7 +39,6 @@ interface TestContext { flushEvents: () => Promise deleteAllCookies: () => Promise sendXhr: (url: string, headers?: string[][]) => Promise - interactWithWorker: (cb: (worker: ServiceWorker) => void) => Promise } type TestRunner = (testContext: TestContext) => Promise | void @@ -62,8 +58,8 @@ class TestBuilder { rumConfiguration?: RumInitConfiguration logsConfiguration?: LogsInitConfiguration } = {} - private useServiceWorker: boolean = false private hostName?: string + private workerOptions?: WorkerOptions constructor(private title: string) {} @@ -135,37 +131,20 @@ class TestBuilder { return this } - withWorker({ importScript = false, nativeLog = false }: LogsWorkerOptions = {}) { - if (!this.useServiceWorker) { - this.useServiceWorker = true - - const isModule = !importScript - - const params = [] - if (importScript) { - params.push('importScripts=true') - } - if (nativeLog) { - params.push('nativeLog=true') - } - - const query = params.length > 0 ? `?${params.join('&')}` : '' - const url = `/sw.js${query}` - - const options = isModule ? '{ type: "module" }' : '{}' - - // Service workers require HTTPS or localhost due to browser security restrictions - this.hostName = 'localhost' - this.withBody(html` - - `) - } + withWorker(testCase: WorkerOptions['testCase'], registrationOptions: RegistrationOptions = {}) { + this.workerOptions = { ...registrationOptions, testCase } + + // Service workers require HTTPS or localhost due to browser security restrictions + this.withHostName('localhost') + this.withBody(html` + + `) return this } @@ -194,6 +173,7 @@ class TestBuilder { testFixture: this.testFixture, extension: this.extension, hostName: this.hostName, + workerOptions: this.workerOptions, } if (this.alsoRunWithRumSlim) { @@ -270,7 +250,12 @@ function declareTest(title: string, setupOptions: SetupOptions, factory: SetupFa servers.intake.bindServerApp(createIntakeServerApp(testContext.intakeRegistry)) const setup = factory(setupOptions, servers) - servers.base.bindServerApp(createMockServerApp(servers, setup, setupOptions.remoteConfiguration)) + servers.base.bindServerApp( + createMockServerApp(servers, setup, { + remoteConfiguration: setupOptions.remoteConfiguration, + workerScript: buildWorkerScript(setupOptions, servers), + }) + ) servers.crossOrigin.bindServerApp(createMockServerApp(servers, setup)) await setUpTest(browserLogs, testContext) @@ -312,9 +297,6 @@ function createTestContext( browserLogsManager.clear() } }, - interactWithWorker: async (cb: (worker: ServiceWorker) => void) => { - await page.evaluate(`(${cb.toString()})(window.myServiceWorker.active)`) - }, flushBrowserLogs: () => browserLogsManager.clear(), flushEvents: () => flushEvents(page), deleteAllCookies: () => deleteAllCookies(browserContext), diff --git a/test/e2e/lib/framework/index.ts b/test/e2e/lib/framework/index.ts index 8baeb9662f..94381b7e6b 100644 --- a/test/e2e/lib/framework/index.ts +++ b/test/e2e/lib/framework/index.ts @@ -1,7 +1,15 @@ export { createTest } from './createTest' export { DEFAULT_RUM_CONFIGURATION, DEFAULT_LOGS_CONFIGURATION } from '../helpers/configuration' export { createExtension } from './createExtension' -export { bundleSetup, html, npmSetup, reactSetup, formatConfiguration, createCrossOriginScriptUrls } from './pageSetups' +export { + bundleSetup, + html, + js, + npmSetup, + reactSetup, + formatConfiguration, + createCrossOriginScriptUrls, +} from './pageSetups' export { IntakeRegistry } from './intakeRegistry' export { getTestServers, waitForServersIdle } from './httpServers' export { flushEvents } from './flushEvents' diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index 3a47a8ac51..8e06c34f2f 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -1,11 +1,14 @@ import { generateUUID, INTAKE_URL_PARAMETERS } from '@datadog/browser-core' -import type { LogsInitConfiguration } from '@datadog/browser-logs' +import type { LogsInitConfiguration, DatadogLogs } from '@datadog/browser-logs' import type { RumInitConfiguration, RemoteConfiguration } from '@datadog/browser-rum-core' import type test from '@playwright/test' import { DEFAULT_LOGS_CONFIGURATION } from '../helpers/configuration' import { isBrowserStack, isContinuousIntegration } from './environment' import type { Servers } from './httpServers' +export interface WorkerOptions extends RegistrationOptions { + testCase: (self: WorkerGlobalScope & { DD_LOGS?: DatadogLogs }) => void +} export interface SetupOptions { rum?: RumInitConfiguration useRumSlim: boolean @@ -27,11 +30,7 @@ export interface SetupOptions { logsConfiguration?: LogsInitConfiguration } hostName?: string -} - -export interface WorkerOptions { - importScripts?: boolean - nativeLog?: boolean + workerOptions?: WorkerOptions } export type SetupFactory = (options: SetupOptions, servers: Servers) => string @@ -209,20 +208,25 @@ export function reactSetup(options: SetupOptions, servers: Servers, appName: str }) } -export function workerSetup(options: WorkerOptions, servers: Servers) { - return js` - ${options.importScripts ? js`importScripts('/datadog-logs.js');` : js`import '/datadog-logs.js';`} +export function buildWorkerScript(options: SetupOptions, servers: Servers) { + let script = '' + + if (!options.workerOptions) { + return script + } + + if (options.logs) { + script += js` + ${options.workerOptions.type === 'module' ? js`import '/datadog-logs.js';` : js`importScripts('/datadog-logs.js');`} // Initialize DD_LOGS in service worker - DD_LOGS.init(${formatConfiguration({ ...DEFAULT_LOGS_CONFIGURATION, forwardConsoleLogs: 'all', forwardErrorsToLogs: true }, servers)}) - - // Handle messages from main thread - self.addEventListener('message', (event) => { - const message = event.data; - - ${options.nativeLog ? js`console.log(message);` : js`DD_LOGS.logger.log(message);`} - }); + DD_LOGS.init(${formatConfiguration({ ...DEFAULT_LOGS_CONFIGURATION, ...options.logs }, servers)}) ` + } + + script += `;(${options.workerOptions.testCase.toString()})(self);` + + return script } export function basePage({ header, body }: { header?: string; body?: string }) { @@ -244,9 +248,7 @@ export function html(parts: readonly string[], ...vars: string[]) { return parts.reduce((full, part, index) => full + vars[index - 1] + part) } -function js(parts: readonly string[], ...vars: string[]) { - return parts.reduce((full, part, index) => full + vars[index - 1] + part) -} +export const js = html function setupEventBridge(servers: Servers) { const baseHostname = new URL(servers.base.origin).hostname diff --git a/test/e2e/lib/framework/serverApps/mock.ts b/test/e2e/lib/framework/serverApps/mock.ts index 88c684809f..59c04f51e1 100644 --- a/test/e2e/lib/framework/serverApps/mock.ts +++ b/test/e2e/lib/framework/serverApps/mock.ts @@ -6,14 +6,17 @@ import type { RemoteConfiguration } from '@datadog/browser-rum-core' import { getSdkBundlePath, getTestAppBundlePath } from '../sdkBuilds' import type { MockServerApp, Servers } from '../httpServers' import { DEV_SERVER_BASE_URL } from '../../helpers/playwright' -import { workerSetup } from '../pageSetups' export const LARGE_RESPONSE_MIN_BYTE_SIZE = 100_000 +interface MockServerOptions { + remoteConfiguration?: RemoteConfiguration + workerScript?: string +} export function createMockServerApp( servers: Servers, setup: string, - remoteConfiguration?: RemoteConfiguration + { remoteConfiguration, workerScript }: MockServerOptions = {} ): MockServerApp { const app = express() let largeResponseBytesWritten = 0 @@ -46,11 +49,7 @@ export function createMockServerApp( }) app.get('/sw.js', (_req, res) => { - const query = _req.query - - res - .contentType('application/javascript') - .send(workerSetup({ importScripts: Boolean(query.importScripts), nativeLog: Boolean(query.nativeLog) }, servers)) + res.contentType('application/javascript').send(workerScript) }) function generateLargeResponse(res: ServerResponse, chunkText: string) { diff --git a/test/e2e/lib/types/global.ts b/test/e2e/lib/types/global.ts index 5dc2a6d45f..b124342f4a 100644 --- a/test/e2e/lib/types/global.ts +++ b/test/e2e/lib/types/global.ts @@ -1,9 +1,9 @@ -import type { LogsGlobal } from '@datadog/browser-logs' -import type { RumGlobal } from '@datadog/browser-rum' +import type { DatadogLogs } from '@datadog/browser-logs' +import type { DatadogRum } from '@datadog/browser-rum' declare global { interface Window { - DD_LOGS?: LogsGlobal - DD_RUM?: RumGlobal + DD_LOGS?: DatadogLogs + DD_RUM?: DatadogRum } } diff --git a/test/e2e/scenario/logs.scenario.ts b/test/e2e/scenario/logs.scenario.ts index 61610e1e97..52299d668c 100644 --- a/test/e2e/scenario/logs.scenario.ts +++ b/test/e2e/scenario/logs.scenario.ts @@ -13,14 +13,16 @@ declare global { test.describe('logs', () => { createTest('service worker with worker logs - esm') - .withWorker() - .run(async ({ flushEvents, intakeRegistry, browserName, interactWithWorker }) => { + .withLogs() + .withWorker( + function (self) { + self.DD_LOGS!.logger.log('Some message') + }, + { type: 'module' } + ) + .run(async ({ flushEvents, intakeRegistry, browserName }) => { test.skip(browserName !== 'chromium', 'Non-Chromium browsers do not support ES modules in Service Workers') - await interactWithWorker((worker) => { - worker.postMessage('Some message') - }) - await flushEvents() expect(intakeRegistry.logsRequests).toHaveLength(1) @@ -28,17 +30,15 @@ test.describe('logs', () => { }) createTest('service worker with worker logs - importScripts') - .withWorker({ importScript: true }) - .run(async ({ flushEvents, intakeRegistry, browserName, interactWithWorker }) => { + .withLogs() + .withWorker(function (self) { + self.DD_LOGS!.logger.log('Other message') + }) + .run(async ({ flushEvents, intakeRegistry, browserName }) => { test.skip( browserName === 'webkit', 'BrowserStack overrides the localhost URL with bs-local.com and cannot be used to install a Service Worker' ) - - await interactWithWorker((worker) => { - worker.postMessage('Other message') - }) - await flushEvents() expect(intakeRegistry.logsRequests).toHaveLength(1) @@ -46,17 +46,16 @@ test.describe('logs', () => { }) createTest('service worker console forwarding') - .withWorker({ importScript: true, nativeLog: true }) - .run(async ({ flushEvents, intakeRegistry, interactWithWorker, browserName }) => { + .withLogs({ forwardConsoleLogs: 'all', forwardErrorsToLogs: true }) + .withWorker(function () { + console.log('SW console log test') + }) + .run(async ({ flushEvents, intakeRegistry, browserName }) => { test.skip( browserName === 'webkit', 'BrowserStack overrides the localhost URL with bs-local.com and cannot be used to install a Service Worker' ) - await interactWithWorker((worker) => { - worker.postMessage('SW console log test') - }) - await flushEvents() // Expect logs for console, error, and report events from service worker diff --git a/test/e2e/tsconfig.json b/test/e2e/tsconfig.json index 887a50d94b..9170cb5bea 100644 --- a/test/e2e/tsconfig.json +++ b/test/e2e/tsconfig.json @@ -10,6 +10,7 @@ "target": "ES2022", "module": "ES2020", "types": ["node", "ajv"], + "lib": ["ESNext", "DOM", "WebWorker"], "allowJs": true, "noEmit": true }