diff --git a/scripts/build/build-test-apps.ts b/scripts/build/build-test-apps.ts index 5c672ae25b..575871c37d 100644 --- a/scripts/build/build-test-apps.ts +++ b/scripts/build/build-test-apps.ts @@ -5,14 +5,9 @@ import { printLog, runMain } from '../lib/executionUtils.ts' import { command } from '../lib/command.ts' import { modifyFile } from '../lib/filesUtils.ts' -interface ExtensionConfig { - name: string - initParameter: string -} - -const EXTRA_EXTENSIONS: ExtensionConfig[] = [ - { name: 'allowed-tracking-origin', initParameter: 'allowedTrackingOrigins: [/^chrome-extension:\\/\\//],' }, - { name: 'invalid-tracking-origin', initParameter: "allowedTrackingOrigins: ['https://app.example.com']," }, +const OTHER_EXTENSIONS: Array<{ name: string; options?: { runAt?: string } }> = [ + { name: 'cdn' }, + { name: 'appendChild', options: { runAt: 'document_start' } }, ] runMain(async () => { @@ -66,17 +61,20 @@ async function buildExtensions(): Promise { buildApp(baseExtDir) - for (const { name, initParameter } of EXTRA_EXTENSIONS) { - const targetDir = path.join('test/apps', name) + for (const { name, options } of OTHER_EXTENSIONS) { + const targetDir = path.join('test/apps', `${name}-extension`) fs.rmSync(targetDir, { recursive: true, force: true }) fs.cpSync(baseExtDir, targetDir, { recursive: true }) - const contentScriptPath = path.join(targetDir, 'src/contentScript.ts') - await modifyFile(contentScriptPath, (content: string) => - content.replace(/\/\* EXTENSION_INIT_PARAMETER \*\//g, initParameter) - ) + const manifestPath = path.join(targetDir, 'manifest.json') + await modifyFile(manifestPath, (originalContent: string) => { + let content = originalContent.replace('dist/base.js', `dist/${name}.js`) - buildApp(targetDir) + if (options?.runAt) { + content = content.replace('document_end', options.runAt) + } + return content + }) } } diff --git a/test/apps/.gitignore b/test/apps/.gitignore index 4790c30db0..3c6fddc975 100644 --- a/test/apps/.gitignore +++ b/test/apps/.gitignore @@ -1,3 +1,5 @@ allowed-tracking-origin/ invalid-tracking-origin/ react-router-v7-app/ +cdn-extension/ +appendChild-extension/ diff --git a/test/apps/base-extension/manifest.json b/test/apps/base-extension/manifest.json index a2c31eba57..fd6deac35e 100644 --- a/test/apps/base-extension/manifest.json +++ b/test/apps/base-extension/manifest.json @@ -5,19 +5,12 @@ "description": "Injects Datadog RUM into every page to inspect data being sent.", "permissions": ["scripting", "activeTab"], "host_permissions": [""], - "background": { - "service_worker": "dist/background.js", - "type": "module" - }, "content_scripts": [ { "matches": [""], - "js": ["dist/content-script.js"], - "run_at": "document_start" + "js": ["dist/base.js"], + "run_at": "document_end", + "world": "MAIN" } - ], - "action": { - "default_title": "Datadog RUM Testing Extension", - "default_popup": "src/popup.html" - } + ] } diff --git a/test/apps/base-extension/src/appendChild.ts b/test/apps/base-extension/src/appendChild.ts new file mode 100644 index 0000000000..e7ebb609e8 --- /dev/null +++ b/test/apps/base-extension/src/appendChild.ts @@ -0,0 +1,5 @@ +const originalAppendChild = Node.prototype.appendChild // eslint-disable-line @typescript-eslint/unbound-method + +Node.prototype.appendChild = function (node: T): T { + return originalAppendChild.call(this, node) as T +} diff --git a/test/apps/base-extension/src/background.ts b/test/apps/base-extension/src/background.ts deleted file mode 100644 index 168831b690..0000000000 --- a/test/apps/base-extension/src/background.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Minimal background service worker to help with extension ID detection -console.log('Background service worker initialized') diff --git a/test/apps/base-extension/src/base.ts b/test/apps/base-extension/src/base.ts new file mode 100644 index 0000000000..be6d6d15d4 --- /dev/null +++ b/test/apps/base-extension/src/base.ts @@ -0,0 +1,30 @@ +import { datadogRum } from '@datadog/browser-rum' +import { datadogLogs } from '@datadog/browser-logs' +import type { RumInitConfiguration } from '@datadog/browser-rum-core' +import type { LogsInitConfiguration } from '@datadog/browser-logs' +import type { Context } from '@datadog/browser-core' + +declare global { + interface Window { + EXT_RUM_CONFIGURATION?: RumInitConfiguration + RUM_CONTEXT?: Context + EXT_LOGS_CONFIGURATION?: LogsInitConfiguration + LOGS_CONTEXT?: Context + } +} + +if (window.EXT_RUM_CONFIGURATION) { + datadogRum.init(window.EXT_RUM_CONFIGURATION) + + if (window.RUM_CONTEXT) { + datadogRum.setGlobalContext(window.RUM_CONTEXT) + } +} + +if (window.EXT_LOGS_CONFIGURATION) { + datadogLogs.init(window.EXT_LOGS_CONFIGURATION) + + if (window.LOGS_CONTEXT) { + datadogLogs.setGlobalContext(window.LOGS_CONTEXT) + } +} diff --git a/test/apps/base-extension/src/cdn.ts b/test/apps/base-extension/src/cdn.ts new file mode 100644 index 0000000000..b722b1a054 --- /dev/null +++ b/test/apps/base-extension/src/cdn.ts @@ -0,0 +1,47 @@ +import type { RumInitConfiguration, RumPublicApi } from '@datadog/browser-rum-core' +import type { LogsInitConfiguration, LogsGlobal } from '@datadog/browser-logs' +import type { Context } from '@datadog/browser-core' + +declare global { + interface Window { + RUM_BUNDLE_URL?: string + LOGS_BUNDLE_URL?: string + EXT_RUM_CONFIGURATION?: RumInitConfiguration + RUM_CONTEXT?: Context + EXT_LOGS_CONFIGURATION?: LogsInitConfiguration + LOGS_CONTEXT?: Context + DD_RUM?: RumPublicApi + DD_LOGS?: LogsGlobal + } +} + +function load( + sdk: T, + url: string, + initConfig: T extends 'DD_RUM' ? RumInitConfiguration : LogsInitConfiguration, + globalContext?: Context +) { + const script = document.createElement('script') + script.src = url + script.onload = () => { + if (!window[sdk]) { + console.error(`${sdk} is not loaded`) + return + } + + window[sdk].init(initConfig as any) + if (globalContext) { + window[sdk].setGlobalContext(globalContext) + } + } + + document.documentElement.appendChild(script) +} + +if (window.RUM_BUNDLE_URL && window.EXT_RUM_CONFIGURATION) { + load('DD_RUM', window.RUM_BUNDLE_URL, window.EXT_RUM_CONFIGURATION, window.RUM_CONTEXT) +} + +if (window.LOGS_BUNDLE_URL && window.EXT_LOGS_CONFIGURATION) { + load('DD_LOGS', window.LOGS_BUNDLE_URL, window.EXT_LOGS_CONFIGURATION, window.LOGS_CONTEXT) +} diff --git a/test/apps/base-extension/src/contentScript.ts b/test/apps/base-extension/src/contentScript.ts deleted file mode 100644 index f6782c5f1b..0000000000 --- a/test/apps/base-extension/src/contentScript.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { datadogRum } from '@datadog/browser-rum' -import { datadogLogs } from '@datadog/browser-logs' - -// NOTE: RUM and Logs data produced during E2E tests are not sent to the E2E intake, because it's -// not using the E2E init configuration including the `proxy` configuration. -// This could be changed in the future. - -datadogRum.init({ - applicationId: '1234', - clientToken: 'abcd', - /* EXTENSION_INIT_PARAMETER */ -}) - -datadogLogs.init({ - clientToken: 'abcd', - /* EXTENSION_INIT_PARAMETER */ -}) diff --git a/test/apps/base-extension/src/form.html b/test/apps/base-extension/src/form.html deleted file mode 100644 index 1db21092a9..0000000000 --- a/test/apps/base-extension/src/form.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Extension Test Page - - - - - diff --git a/test/apps/base-extension/src/popup.html b/test/apps/base-extension/src/popup.html deleted file mode 100644 index 0496063dd3..0000000000 --- a/test/apps/base-extension/src/popup.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - Extension Popup - - - diff --git a/test/apps/base-extension/webpack.config.js b/test/apps/base-extension/webpack.config.js index c8697b443e..0b6893f665 100644 --- a/test/apps/base-extension/webpack.config.js +++ b/test/apps/base-extension/webpack.config.js @@ -4,8 +4,18 @@ module.exports = { mode: 'production', target: ['web', 'es2018'], entry: { - 'content-script': './src/contentScript.ts', - background: './src/background.ts', + base: './src/base.ts', + cdn: './src/cdn.ts', + appendChild: './src/appendChild.ts', + }, + module: { + rules: [ + { + test: /\.ts$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], }, output: { filename: '[name].js', diff --git a/test/apps/base-extension/yarn.lock b/test/apps/base-extension/yarn.lock index 0077081d90..6d46027e50 100644 --- a/test/apps/base-extension/yarn.lock +++ b/test/apps/base-extension/yarn.lock @@ -7,14 +7,14 @@ __metadata: "@datadog/browser-core@file:../../../packages/core/package.tgz::locator=rum-testing-extension%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c8e335&locator=rum-testing-extension%40workspace%3A." - checksum: 10c0/e98c1c4d1dc900848961a233c9cc28fb442219bd45bae226f9a48f6b7282c6ed743a6be9a14477c12affb948184db28a9f9d5c7cd8ce4cf08f6141cbd8bc4679 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=3d1932&locator=rum-testing-extension%40workspace%3A." + checksum: 10c0/994366172286b77f3f9fba0e134c87b205a1b8bd55d7f2fa01b71a1151d696f4147d9885c3d10e2dea847adb15e70784115595730cf1126f2fe4c899aabb95db languageName: node linkType: hard "@datadog/browser-logs@file:../../../packages/logs/package.tgz::locator=rum-testing-extension%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-logs@file:../../../packages/logs/package.tgz#../../../packages/logs/package.tgz::hash=471336&locator=rum-testing-extension%40workspace%3A." + resolution: "@datadog/browser-logs@file:../../../packages/logs/package.tgz#../../../packages/logs/package.tgz::hash=5e7803&locator=rum-testing-extension%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" peerDependencies: @@ -22,22 +22,22 @@ __metadata: peerDependenciesMeta: "@datadog/browser-rum": optional: true - checksum: 10c0/8266d763df3a6fdc22b3dbd2855fea5799d0f27a476ade83850499a8ee2bc890fd09fa1f534c2ad79d8dce75007d908fc4928f77987bb6f74c1c1e4b11115a7e + checksum: 10c0/542438b155eba1f576c00ec24b36f5a191634c2a1912f4723159e934a02f5b485fa549bc8d8b230941ac68b4b3fe07aad38e40a7fc9407c15e2d6d4961a0d2ce languageName: node linkType: hard "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=rum-testing-extension%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c25ad6&locator=rum-testing-extension%40workspace%3A." + resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c95a64&locator=rum-testing-extension%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" - checksum: 10c0/0b46745d5dd37acb24a715b013622ed35ab01e482a0dfed56aca95cde24cfb907cb1f9deba304f8c7f49885519b2238cdc59bf3a5df0eb90e2d756aea02d346d + checksum: 10c0/272794feb33c274a6517d2bafb1107a77fabd1574f36be3ed9c4735fa8355e286d072355e7d55fafc705fea6b96ce3925a748635b9709771093a013330f3ca6f languageName: node linkType: hard "@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=rum-testing-extension%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=603883&locator=rum-testing-extension%40workspace%3A." + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=ee0f6e&locator=rum-testing-extension%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" "@datadog/browser-rum-core": "npm:6.19.0" @@ -46,7 +46,7 @@ __metadata: peerDependenciesMeta: "@datadog/browser-logs": optional: true - checksum: 10c0/0d05abf96a68f5c709c21e04b6cce6708f1a5e63d146efe4d42276b36d64abdeaccee39858bc0279a5011ab529d4d0d4430b344e34d0a01eed9b913649acb072 + checksum: 10c0/6690b8782df911bfbc4298914ed42bb1b6d1360a5ba43da082423cfa7e083f36e503505e2353ab20df6da1e0d094203257d6a8ae62d5a69cc27c9885fea2e394 languageName: node linkType: hard diff --git a/test/apps/react-router-v6-app/yarn.lock b/test/apps/react-router-v6-app/yarn.lock index cf927c8648..38e0f6cf90 100644 --- a/test/apps/react-router-v6-app/yarn.lock +++ b/test/apps/react-router-v6-app/yarn.lock @@ -7,17 +7,17 @@ __metadata: "@datadog/browser-core@file:../../../packages/core/package.tgz::locator=react-router-v6-app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c8e335&locator=react-router-v6-app%40workspace%3A." - checksum: 10c0/e98c1c4d1dc900848961a233c9cc28fb442219bd45bae226f9a48f6b7282c6ed743a6be9a14477c12affb948184db28a9f9d5c7cd8ce4cf08f6141cbd8bc4679 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=3d1932&locator=react-router-v6-app%40workspace%3A." + checksum: 10c0/994366172286b77f3f9fba0e134c87b205a1b8bd55d7f2fa01b71a1151d696f4147d9885c3d10e2dea847adb15e70784115595730cf1126f2fe4c899aabb95db languageName: node linkType: hard "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=react-router-v6-app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c25ad6&locator=react-router-v6-app%40workspace%3A." + resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c95a64&locator=react-router-v6-app%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" - checksum: 10c0/0b46745d5dd37acb24a715b013622ed35ab01e482a0dfed56aca95cde24cfb907cb1f9deba304f8c7f49885519b2238cdc59bf3a5df0eb90e2d756aea02d346d + checksum: 10c0/272794feb33c274a6517d2bafb1107a77fabd1574f36be3ed9c4735fa8355e286d072355e7d55fafc705fea6b96ce3925a748635b9709771093a013330f3ca6f languageName: node linkType: hard @@ -48,7 +48,7 @@ __metadata: "@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=react-router-v6-app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=603883&locator=react-router-v6-app%40workspace%3A." + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=ee0f6e&locator=react-router-v6-app%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" "@datadog/browser-rum-core": "npm:6.19.0" @@ -57,7 +57,7 @@ __metadata: peerDependenciesMeta: "@datadog/browser-logs": optional: true - checksum: 10c0/0d05abf96a68f5c709c21e04b6cce6708f1a5e63d146efe4d42276b36d64abdeaccee39858bc0279a5011ab529d4d0d4430b344e34d0a01eed9b913649acb072 + checksum: 10c0/6690b8782df911bfbc4298914ed42bb1b6d1360a5ba43da082423cfa7e083f36e503505e2353ab20df6da1e0d094203257d6a8ae62d5a69cc27c9885fea2e394 languageName: node linkType: hard diff --git a/test/apps/vanilla/yarn.lock b/test/apps/vanilla/yarn.lock index e590d67bc8..3400902bf5 100644 --- a/test/apps/vanilla/yarn.lock +++ b/test/apps/vanilla/yarn.lock @@ -7,14 +7,14 @@ __metadata: "@datadog/browser-core@file:../../../packages/core/package.tgz::locator=app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c8e335&locator=app%40workspace%3A." - checksum: 10c0/e98c1c4d1dc900848961a233c9cc28fb442219bd45bae226f9a48f6b7282c6ed743a6be9a14477c12affb948184db28a9f9d5c7cd8ce4cf08f6141cbd8bc4679 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=3d1932&locator=app%40workspace%3A." + checksum: 10c0/994366172286b77f3f9fba0e134c87b205a1b8bd55d7f2fa01b71a1151d696f4147d9885c3d10e2dea847adb15e70784115595730cf1126f2fe4c899aabb95db languageName: node linkType: hard "@datadog/browser-logs@file:../../../packages/logs/package.tgz::locator=app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-logs@file:../../../packages/logs/package.tgz#../../../packages/logs/package.tgz::hash=471336&locator=app%40workspace%3A." + resolution: "@datadog/browser-logs@file:../../../packages/logs/package.tgz#../../../packages/logs/package.tgz::hash=5e7803&locator=app%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" peerDependencies: @@ -22,22 +22,22 @@ __metadata: peerDependenciesMeta: "@datadog/browser-rum": optional: true - checksum: 10c0/8266d763df3a6fdc22b3dbd2855fea5799d0f27a476ade83850499a8ee2bc890fd09fa1f534c2ad79d8dce75007d908fc4928f77987bb6f74c1c1e4b11115a7e + checksum: 10c0/542438b155eba1f576c00ec24b36f5a191634c2a1912f4723159e934a02f5b485fa549bc8d8b230941ac68b4b3fe07aad38e40a7fc9407c15e2d6d4961a0d2ce languageName: node linkType: hard "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c25ad6&locator=app%40workspace%3A." + resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=c95a64&locator=app%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" - checksum: 10c0/0b46745d5dd37acb24a715b013622ed35ab01e482a0dfed56aca95cde24cfb907cb1f9deba304f8c7f49885519b2238cdc59bf3a5df0eb90e2d756aea02d346d + checksum: 10c0/272794feb33c274a6517d2bafb1107a77fabd1574f36be3ed9c4735fa8355e286d072355e7d55fafc705fea6b96ce3925a748635b9709771093a013330f3ca6f languageName: node linkType: hard "@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=app%40workspace%3A.": version: 6.19.0 - resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=603883&locator=app%40workspace%3A." + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=ee0f6e&locator=app%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.19.0" "@datadog/browser-rum-core": "npm:6.19.0" @@ -46,7 +46,7 @@ __metadata: peerDependenciesMeta: "@datadog/browser-logs": optional: true - checksum: 10c0/0d05abf96a68f5c709c21e04b6cce6708f1a5e63d146efe4d42276b36d64abdeaccee39858bc0279a5011ab529d4d0d4430b344e34d0a01eed9b913649acb072 + checksum: 10c0/6690b8782df911bfbc4298914ed42bb1b6d1360a5ba43da082423cfa7e083f36e503505e2353ab20df6da1e0d094203257d6a8ae62d5a69cc27c9885fea2e394 languageName: node linkType: hard diff --git a/test/e2e/lib/framework/createExtension.ts b/test/e2e/lib/framework/createExtension.ts new file mode 100644 index 0000000000..fc473bda6e --- /dev/null +++ b/test/e2e/lib/framework/createExtension.ts @@ -0,0 +1,44 @@ +import type { RumInitConfiguration } from '@datadog/browser-rum-core' +import type test from '@playwright/test' +import type { LogsInitConfiguration } from '@datadog/browser-logs' +import { createExtensionTest } from '../helpers/extensionFixture' +import { DEFAULT_LOGS_CONFIGURATION, DEFAULT_RUM_CONFIGURATION } from './createTest' + +export function createExtension(path: string) { + return new Extension(path) +} + +// TODO: the recorder is lazy loaded and does not works in an browser extension content script +const DISABLE_SESSION_REPLAY_CONFIGURATION = { sessionReplaySampleRate: 0 } + +export class Extension { + public fixture: typeof test + public rumConfiguration: RumInitConfiguration | undefined + public logsConfiguration: LogsInitConfiguration | undefined + + constructor(path: string) { + this.fixture = createExtensionTest(path) + + return this + } + + withRum(rumInitConfiguration: Partial = {}) { + this.rumConfiguration = { + ...DEFAULT_RUM_CONFIGURATION, + ...DISABLE_SESSION_REPLAY_CONFIGURATION, + ...rumInitConfiguration, + } + + return this + } + + withLogs(logsInitConfiguration: Partial = {}) { + this.logsConfiguration = { + ...DEFAULT_LOGS_CONFIGURATION, + ...DISABLE_SESSION_REPLAY_CONFIGURATION, + ...logsInitConfiguration, + } + + return this + } +} diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 90ac85d03a..023ed52d24 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -3,7 +3,6 @@ import type { RumInitConfiguration, RemoteConfiguration } from '@datadog/browser import { DefaultPrivacyLevel } from '@datadog/browser-rum' import type { BrowserContext, Page } from '@playwright/test' import { test, expect } from '@playwright/test' -import { createExtensionTest } from '../helpers/extensionFixture' import { addTag, addTestOptimizationTags } from '../helpers/tags' import { getRunId } from '../../../envUtils' import type { BrowserLog } from '../helpers/browser' @@ -19,6 +18,7 @@ import type { SetupFactory, SetupOptions } from './pageSetups' import { DEFAULT_SETUPS, npmSetup, reactSetup } from './pageSetups' import { createIntakeServerApp } from './serverApps/intake' import { createMockServerApp } from './serverApps/mock' +import type { Extension } from './createExtension' export const DEFAULT_RUM_CONFIGURATION = { applicationId: APPLICATION_ID, @@ -76,6 +76,10 @@ class TestBuilder { private eventBridge = false private setups: Array<{ factory: SetupFactory; name?: string }> = DEFAULT_SETUPS private testFixture: typeof test = test + private extension: { + rumConfiguration?: RumInitConfiguration + logsConfiguration?: LogsInitConfiguration + } = {} constructor(private title: string) {} @@ -129,8 +133,16 @@ class TestBuilder { return this } - withExtension(extensionPath: string) { - this.testFixture = createExtensionTest(extensionPath) + withSetup(setup: SetupFactory) { + this.setups = [{ factory: setup }] + return this + } + + withExtension(extension: Extension) { + this.testFixture = extension.fixture + this.extension.rumConfiguration = extension.rumConfiguration + this.extension.logsConfiguration = extension.logsConfiguration + return this } @@ -156,6 +168,7 @@ class TestBuilder { test_name: '', }, testFixture: this.testFixture, + extension: this.extension, } if (this.alsoRunWithRumSlim) { @@ -202,7 +215,7 @@ function declareTestsForSetups( } function declareTest(title: string, setupOptions: SetupOptions, factory: SetupFactory, runner: TestRunner) { - const testFixture = setupOptions.testFixture + const testFixture = setupOptions.testFixture ?? test testFixture(title, async ({ page, context }) => { const browserName = getBrowserName(test.info().project.name) addTag('test.browserName', browserName) diff --git a/test/e2e/lib/framework/index.ts b/test/e2e/lib/framework/index.ts index dfb8e79529..4b65b4f4bc 100644 --- a/test/e2e/lib/framework/index.ts +++ b/test/e2e/lib/framework/index.ts @@ -1,8 +1,10 @@ export { createTest, DEFAULT_RUM_CONFIGURATION, DEFAULT_LOGS_CONFIGURATION } from './createTest' -export { bundleSetup, html, npmSetup, reactSetup } from './pageSetups' +export { createExtension } from './createExtension' +export { bundleSetup, html, npmSetup, reactSetup, formatConfiguration, createCrossOriginScriptUrls } from './pageSetups' export { IntakeRegistry } from './intakeRegistry' export { getTestServers, waitForServersIdle } from './httpServers' export { flushEvents } from './flushEvents' export { waitForRequests } from './waitForRequests' export { LARGE_RESPONSE_MIN_BYTE_SIZE } from './serverApps/mock' export { getSdkBundlePath, getTestAppBundlePath } from './sdkBuilds' +export type { BrowserLog } from '../helpers/browser' diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index 80c5c187a7..547c63c0e5 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -20,6 +20,10 @@ export interface SetupOptions { test_name: string } testFixture: typeof test + extension?: { + rumConfiguration?: RumInitConfiguration + logsConfiguration?: LogsInitConfiguration + } } export type SetupFactory = (options: SetupOptions, servers: Servers) => string @@ -46,6 +50,10 @@ export function asyncSetup(options: SetupOptions, servers: Servers) { header += setupEventBridge(servers) } + if (options.extension) { + header += setupExtension(options, servers) + } + function formatSnippet(url: string, globalName: string) { return `(function(h,o,u,n,d) { h=h[d]=h[d]||{q:[],onReady:function(c){h.q.push(c)}} @@ -93,6 +101,10 @@ export function bundleSetup(options: SetupOptions, servers: Servers) { header += setupEventBridge(servers) } + if (options.extension) { + header += setupExtension(options, servers) + } + const { logsScriptUrl, rumScriptUrl } = createCrossOriginScriptUrls(servers, options) if (options.logs) { @@ -129,6 +141,10 @@ export function npmSetup(options: SetupOptions, servers: Servers) { header += setupEventBridge(servers) } + if (options.extension) { + header += setupExtension(options, servers) + } + if (options.logs) { header += html` + ` + } + + if (options.extension?.logsConfiguration) { + header += html` + + ` + } + + return header +} + +export function formatConfiguration(initConfiguration: LogsInitConfiguration | RumInitConfiguration, servers: Servers) { const fns = new Map void>() let result = JSON.stringify( @@ -268,7 +316,7 @@ function formatConfiguration(initConfiguration: LogsInitConfiguration | RumInitC return result } -function createCrossOriginScriptUrls(servers: Servers, options: SetupOptions) { +export function createCrossOriginScriptUrls(servers: Servers, options: SetupOptions) { return { logsScriptUrl: `${servers.crossOrigin.url}/datadog-logs.js`, rumScriptUrl: `${servers.crossOrigin.url}/${options.useRumSlim ? 'datadog-rum-slim.js' : 'datadog-rum.js'}`, diff --git a/test/e2e/scenario/browser-extensions/browserExtensions.scenario.ts b/test/e2e/scenario/browser-extensions/browserExtensions.scenario.ts index 59b9b079ae..2ee1a4c33f 100644 --- a/test/e2e/scenario/browser-extensions/browserExtensions.scenario.ts +++ b/test/e2e/scenario/browser-extensions/browserExtensions.scenario.ts @@ -1,133 +1,118 @@ import path from 'path' import { test, expect } from '@playwright/test' -import { DEFAULT_LOGS_CONFIGURATION, DEFAULT_RUM_CONFIGURATION, createTest } from '../../lib/framework' +import type { BrowserLog } from '../../lib/framework' +import { createTest, createExtension, createCrossOriginScriptUrls, formatConfiguration } from '../../lib/framework' -// Different extension build paths for different configurations -const pathToBaseExtension = path.join(__dirname, '../../../../test/apps/base-extension') -// Contains allowedTrackingOrigins parameter with chrome-extension origin -const pathToAllowedTrackingOriginExtension = path.join(__dirname, '../../../../test/apps/allowed-tracking-origin') -// Contains allowedTrackingOrigins parameter with app.example.com origin -const pathToInvalidTrackingOriginExtension = path.join(__dirname, '../../../../test/apps/invalid-tracking-origin') - -const warningMessage = +const WARNING_MESSAGE = 'Datadog Browser SDK: Running the Browser SDK in a Web extension content script is discouraged and will be forbidden in a future major release unless the `allowedTrackingOrigins` option is provided.' -const errorMessage = 'Datadog Browser SDK: SDK initialized on a non-allowed domain.' - -test.describe('browser extensions', () => { - createTest('popup page should load extension popup and display expected content') - .withExtension(pathToBaseExtension) - .run(async ({ page, getExtensionId }) => { - const extensionId = await getExtensionId() - await page.goto(`chrome-extension://${extensionId}/src/popup.html`) - await expect(page).toHaveTitle(/Extension Popup/) - }) - - createTest( - 'SDK is initialized in an unsupported environment without allowedTrackingOrigins and warns when used in content script' - ) - .withExtension(pathToBaseExtension) - .withRum() - .withLogs() - .run(async ({ page, baseUrl, getExtensionId, withBrowserLogs }) => { - const extensionId = await getExtensionId() +const ERROR_MESSAGE = 'Datadog Browser SDK: SDK initialized on a non-allowed domain.' - await page.goto(`chrome-extension://${extensionId}/src/popup.html`) +const BASE_PATH = path.join(process.cwd(), 'test/apps') +const EXTENSIONS = ['base-extension', 'cdn-extension'] - const rumResult = await page.evaluate(() => window.DD_RUM?.getInitConfiguration()) - const logsResult = await page.evaluate(() => window.DD_LOGS?.getInitConfiguration()) - expect(rumResult?.applicationId).toBe('1234') - expect(logsResult?.clientToken).toBe('abcd') +// NOTE: logs might contain a warning about the SDK being loaded twice when using the npm config +// because the SDK is loaded even though it's not initialized. +// We ignore it here because it's not relevant to the test. +const isNotSdkLoadedMoreThanOnce = (log: BrowserLog) => !log.message.includes('SDK is loaded more than once') - await page.goto(baseUrl) - - const pageRumResult = await page.evaluate(() => window.DD_RUM?.getInitConfiguration()) - const pageLogsResult = await page.evaluate(() => window.DD_LOGS?.getInitConfiguration()) - - expect(pageRumResult?.applicationId).toBe(DEFAULT_RUM_CONFIGURATION.applicationId) - expect(pageLogsResult?.clientToken).toBe(DEFAULT_LOGS_CONFIGURATION.clientToken) - - // Check for warnings in console messages - should have one from RUM and one from Logs - // But since we also go to the base url, we can have more than 2 logs - withBrowserLogs((logs) => { - expect(logs.length).toBeGreaterThanOrEqual(2) - expect(logs).toContainEqual( - expect.objectContaining({ - level: 'warning', - message: warningMessage, +test.describe('browser extensions', () => { + for (const name of EXTENSIONS) { + test.describe(`with ${name} extension`, () => { + createTest('should warn and start tracking when SDK is initialized in an unsupported environment') + .withExtension(createExtension(path.join(BASE_PATH, name)).withRum().withLogs()) + .run(async ({ withBrowserLogs, flushEvents, intakeRegistry }) => { + await flushEvents() + + expect(intakeRegistry.rumViewEvents).toHaveLength(1) + + withBrowserLogs((logs) => { + const filteredLogs = logs.filter(isNotSdkLoadedMoreThanOnce) + + // Two warnings, one for RUM and one for LOGS SDK + expect(filteredLogs).toHaveLength(2) + filteredLogs.forEach((log) => { + expect(log).toMatchObject({ + level: 'warning', + message: WARNING_MESSAGE, + }) + }) }) - ) - }) - }) + }) - createTest('SDK with correct allowedTrackingOrigins parameter works correctly for both RUM and Logs') - .withExtension(pathToAllowedTrackingOriginExtension) - .run(async ({ page, getExtensionId, flushBrowserLogs }) => { - const extensionId = await getExtensionId() - const expectedOriginPattern = /^chrome-extension:\/\// - const extensionLogs: any[] = [] - - // Listen for console events and filter for extension page only - // Because the test also goes to the base url, we need to filter for the extension page only - page.on('console', (msg) => { - const url = msg.location().url - if (url && url.startsWith(`chrome-extension://${extensionId}`)) { - extensionLogs.push({ - level: msg.type(), - message: msg.text(), - source: 'console', - url, + createTest('should start tracking when allowedTrackingOrigins matches current domain') + .withExtension( + createExtension(path.join(BASE_PATH, name)) + .withRum({ allowedTrackingOrigins: ['LOCATION_ORIGIN'] }) + .withLogs({ allowedTrackingOrigins: ['LOCATION_ORIGIN'] }) + ) + .run(async ({ withBrowserLogs, flushEvents, intakeRegistry }) => { + await flushEvents() + + expect(intakeRegistry.rumViewEvents).toHaveLength(1) + + withBrowserLogs((logs) => + expect(logs).not.toContainEqual( + expect.objectContaining({ + level: 'warning', + message: WARNING_MESSAGE, + }) + ) + ) + }) + + createTest('should not start tracking when allowedTrackingOrigins does not match current domain') + .withExtension( + createExtension(path.join(BASE_PATH, name)) + .withRum({ allowedTrackingOrigins: ['https://app.example.com'] }) + .withLogs({ allowedTrackingOrigins: ['https://app.example.com'] }) + ) + .run(async ({ withBrowserLogs, flushEvents, intakeRegistry }) => { + await flushEvents() + + expect(intakeRegistry.rumViewEvents).toHaveLength(0) + + withBrowserLogs((logs) => { + const filteredLogs = logs.filter(isNotSdkLoadedMoreThanOnce) + // Two errors, one for RUM and one for LOGS SDK + expect(filteredLogs).toHaveLength(2) + filteredLogs.forEach((log) => { + expect(log).toMatchObject({ + level: 'error', + message: ERROR_MESSAGE, + }) + }) }) - } - }) - - await page.goto(`chrome-extension://${extensionId}/src/popup.html`) - - const rumResult = await page.evaluate(() => window.DD_RUM?.getInitConfiguration()) - const logsResult = await page.evaluate(() => window.DD_LOGS?.getInitConfiguration()) - - expect(rumResult?.applicationId).toBe('1234') - expect(rumResult?.allowedTrackingOrigins).toEqual([expectedOriginPattern]) - expect(logsResult?.clientToken).toBe('abcd') - expect(logsResult?.allowedTrackingOrigins).toEqual([expectedOriginPattern]) - - expect(extensionLogs).toEqual([]) - - flushBrowserLogs() + }) }) - - createTest('SDK with incorrect allowedTrackingOrigins shows error message for both RUM and Logs') - .withExtension(pathToInvalidTrackingOriginExtension) + } + + /** + * This test is reconstruction of an edge case that happens when using some extension that override `appendChild` and + * the sync installation method using NextJs ` + + + ` + }) + .run(async ({ withBrowserLogs, flushEvents, intakeRegistry }) => { + test.fail() // TODO: remove this once the issue is fixed - const pageRumResult = await page.evaluate(() => window.DD_RUM?.getInitConfiguration()) - const pageLogsResult = await page.evaluate(() => window.DD_LOGS?.getInitConfiguration()) + await flushEvents() - expect(pageRumResult?.applicationId).toBe(DEFAULT_RUM_CONFIGURATION.applicationId) - expect(pageLogsResult?.clientToken).toBe(DEFAULT_LOGS_CONFIGURATION.clientToken) + expect(intakeRegistry.rumViewEvents).toHaveLength(1) - withBrowserLogs((logs) => { - expect(logs.length).toBeGreaterThanOrEqual(2) - expect(logs).toContainEqual( - expect.objectContaining({ - level: 'error', - message: errorMessage, - }) - ) - }) + withBrowserLogs((logs) => expect(logs.length).toBe(0)) }) }) diff --git a/test/e2e/scenario/developer-extension/developerExtension.scenario.ts b/test/e2e/scenario/developer-extension/developerExtension.scenario.ts index f8e3cbf4ed..2b45fd9c55 100644 --- a/test/e2e/scenario/developer-extension/developerExtension.scenario.ts +++ b/test/e2e/scenario/developer-extension/developerExtension.scenario.ts @@ -1,9 +1,11 @@ import path from 'path' import { expect } from '@playwright/test' -import { createTest } from '../../lib/framework' +import { createExtension, createTest } from '../../lib/framework' + +const developerExtensionPath = path.join(process.cwd(), 'developer-extension', 'dist') createTest('should switch between tabs') - .withExtension(path.join(process.cwd(), 'developer-extension', 'dist')) + .withExtension(createExtension(developerExtensionPath)) .run(async ({ page, getExtensionId, flushBrowserLogs }) => { const extensionId = await getExtensionId()