diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 69e63a8648..6dbb2b5224 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -1,6 +1,7 @@ import type { LogsInitConfiguration } from '@datadog/browser-logs' import type { DebuggerInitConfiguration } from '@datadog/browser-debugger' -import type { RumInitConfiguration, RemoteConfiguration } from '@datadog/browser-rum-core' +import type { RemoteConfiguration } from '@datadog/browser-remote-config' +import type { RumInitConfiguration } from '@datadog/browser-rum-core' import type { BrowserContext, Page } from '@playwright/test' import { test, expect } from '@playwright/test' import { addTag, addTestOptimizationTags } from '../helpers/tags' diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index e64c41e5ff..ee18ba30dd 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -1,6 +1,7 @@ import { generateUUID, INTAKE_URL_PARAMETERS } from '@datadog/browser-core' import type { LogsInitConfiguration } from '@datadog/browser-logs' -import type { RumInitConfiguration, RemoteConfiguration } from '@datadog/browser-rum-core' +import type { RemoteConfiguration } from '@datadog/browser-remote-config' +import type { RumInitConfiguration } from '@datadog/browser-rum-core' import type { DebuggerInitConfiguration } from '@datadog/browser-debugger' import type test from '@playwright/test' import { isBrowserStack, isContinuousIntegration } from './environment' diff --git a/test/e2e/scenario/rum/embeddedConfig.scenario.ts b/test/e2e/scenario/rum/embeddedConfig.scenario.ts new file mode 100644 index 0000000000..08c2b729e2 --- /dev/null +++ b/test/e2e/scenario/rum/embeddedConfig.scenario.ts @@ -0,0 +1,131 @@ +import { generateCombinedBundle } from '@datadog/browser-sdk-endpoint' +import { test, expect } from '@playwright/test' +import { createTest } from '../../lib/framework' + +test.describe('embedded configuration', () => { + createTest('should load SDK with embedded config and expose getInitConfiguration') + .withRum({ + sessionSampleRate: 75, + service: 'embedded-config-test', + env: 'staging', + }) + .run(async ({ page }) => { + const initConfig = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!) + expect(initConfig).toBeDefined() + expect(initConfig.service).toBe('embedded-config-test') + expect(initConfig.env).toBe('staging') + }) + + createTest('should send RUM view events with embedded config') + .withRum({ + sessionSampleRate: 100, + }) + .run(async ({ intakeRegistry, flushEvents }) => { + await flushEvents() + expect(intakeRegistry.rumViewEvents.length).toBeGreaterThanOrEqual(1) + }) + + createTest('should work with rum-slim variant') + .withRum({ + service: 'slim-embedded-test', + }) + .withRumSlim() + .run(async ({ page }) => { + const initConfig = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!) + expect(initConfig).toBeDefined() + expect(initConfig.service).toBe('slim-embedded-test') + }) + + test('generateCombinedBundle produces valid JavaScript bundle', () => { + const sdkCode = 'window.__TEST_SDK_LOADED__ = true;' + const config = { + applicationId: 'test-app-id', + clientToken: 'pub-test-token', + sessionSampleRate: 100, + } + + const bundle = generateCombinedBundle({ + sdkCode, + config, + variant: 'rum', + }) + + // Bundle is valid JavaScript + // eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval + expect(() => new Function(bundle)).not.toThrow() + + // Bundle contains the embedded config + expect(bundle).toContain('"applicationId": "test-app-id"') + expect(bundle).toContain('"sessionSampleRate": 100') + + // Bundle contains SDK code + expect(bundle).toContain('__TEST_SDK_LOADED__') + + // Bundle has correct metadata + expect(bundle).toContain('SDK Variant: rum') + expect(bundle).toContain('Embedded Remote Configuration') + expect(bundle).toContain('No additional network requests needed') + }) + + test('generateCombinedBundle wraps content in IIFE with auto-init', () => { + const sdkCode = '// SDK placeholder' + const config = { + applicationId: 'iife-test-app', + clientToken: 'pub-test-token', + } + + const bundle = generateCombinedBundle({ + sdkCode, + config, + variant: 'rum', + }) + + expect(bundle).toContain('(function() {') + expect(bundle).toContain("'use strict';") + expect(bundle).toContain('__DATADOG_REMOTE_CONFIG__') + expect(bundle).toContain('DD_RUM.init(__DATADOG_REMOTE_CONFIG__)') + expect(bundle).toContain('})();') + }) + + test('generateCombinedBundle preserves config with dynamic value markers', () => { + const sdkCode = '// SDK placeholder' + const config = { + applicationId: 'dynamic-test-app', + clientToken: 'pub-test-token', + version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'app_version' }, + } + + const bundle = generateCombinedBundle({ + sdkCode, + config, + variant: 'rum', + }) + + // Dynamic markers should be preserved as-is in the bundle + expect(bundle).toContain('"rcSerializedType": "dynamic"') + expect(bundle).toContain('"strategy": "cookie"') + expect(bundle).toContain('"name": "app_version"') + }) + + createTest('should not make requests to remote config endpoint when config is embedded') + .withRum({ + sessionSampleRate: 100, + }) + .run(async ({ page, servers: _servers }) => { + // The SDK with embedded config (via .withRum()) should not fetch remote config + // since no remoteConfigurationId is provided + const configRequests: string[] = [] + + page.on('request', (request) => { + if (request.url().includes('/config') || request.url().includes('sdk-configuration')) { + configRequests.push(request.url()) + } + }) + + // Wait for any potential config fetches + await page.waitForTimeout(2000) + + // No remote config requests should have been made + expect(configRequests).toHaveLength(0) + }) +}) diff --git a/test/e2e/scenario/rum/embeddedConfigDynamic.scenario.ts b/test/e2e/scenario/rum/embeddedConfigDynamic.scenario.ts new file mode 100644 index 0000000000..c917f774a4 --- /dev/null +++ b/test/e2e/scenario/rum/embeddedConfigDynamic.scenario.ts @@ -0,0 +1,158 @@ +import { test, expect } from '@playwright/test' +import { generateCombinedBundle } from '@datadog/browser-sdk-endpoint' + +test.describe('embedded configuration with dynamic values', () => { + test('preserves cookie dynamic value markers in generated bundle', () => { + const config = { + applicationId: 'dynamic-cookie-app', + clientToken: 'pub-test-token', + version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'app_version' }, + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + expect(bundle).toContain('"rcSerializedType": "dynamic"') + expect(bundle).toContain('"strategy": "cookie"') + expect(bundle).toContain('"name": "app_version"') + }) + + test('preserves DOM selector dynamic value markers in generated bundle', () => { + const config = { + applicationId: 'dynamic-dom-app', + clientToken: 'pub-test-token', + version: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#tracking-id' }, + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + expect(bundle).toContain('"strategy": "dom"') + expect(bundle).toContain('"selector": "#tracking-id"') + }) + + test('preserves DOM attribute dynamic value markers in generated bundle', () => { + const config = { + applicationId: 'dynamic-dom-attr-app', + clientToken: 'pub-test-token', + version: { + rcSerializedType: 'dynamic', + strategy: 'dom', + selector: '#app-meta', + attribute: 'data-version', + }, + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + expect(bundle).toContain('"strategy": "dom"') + expect(bundle).toContain('"selector": "#app-meta"') + expect(bundle).toContain('"attribute": "data-version"') + }) + + test('preserves JS path dynamic value markers in generated bundle', () => { + const config = { + applicationId: 'dynamic-js-app', + clientToken: 'pub-test-token', + version: { rcSerializedType: 'dynamic', strategy: 'js', path: 'appState.tracking.version' }, + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + expect(bundle).toContain('"strategy": "js"') + expect(bundle).toContain('"path": "appState.tracking.version"') + }) + + test('mixed static and dynamic values coexist in generated bundle', () => { + const config = { + applicationId: 'mixed-config-app', + clientToken: 'pub-test-token', + sessionSampleRate: 80, + service: 'static-service', + version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'app_version' }, + env: { rcSerializedType: 'dynamic', strategy: 'js', path: 'deployment.env' }, + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + // Static values embedded + expect(bundle).toContain('"applicationId": "mixed-config-app"') + expect(bundle).toContain('"sessionSampleRate": 80') + expect(bundle).toContain('"service": "static-service"') + + // Dynamic markers preserved + expect(bundle).toContain('"strategy": "cookie"') + expect(bundle).toContain('"strategy": "js"') + expect(bundle).toContain('"name": "app_version"') + expect(bundle).toContain('"path": "deployment.env"') + }) + + test('nested dynamic values preserve structure in generated bundle', () => { + const config = { + applicationId: 'nested-dynamic-app', + clientToken: 'pub-test-token', + allowedTracingUrls: [ + { + match: { rcSerializedType: 'string' as const, value: 'https://api.example.com' }, + propagatorTypes: ['tracecontext'], + }, + ], + user: [ + { key: 'id', value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'user_id' } }, + { key: 'email', value: { rcSerializedType: 'dynamic', strategy: 'js', path: 'userData.email' } }, + ], + } + + const bundle = generateCombinedBundle({ + sdkCode: '// SDK', + config, + variant: 'rum', + }) + + expect(bundle).toContain('"rcSerializedType": "string"') + expect(bundle).toContain('"value": "https://api.example.com"') + expect(bundle).toContain('"propagatorTypes"') + expect(bundle).toContain('"key": "id"') + expect(bundle).toContain('"key": "email"') + expect(bundle).toContain('"name": "user_id"') + expect(bundle).toContain('"path": "userData.email"') + }) + + test('generated bundle with dynamic values is valid JavaScript', () => { + const config = { + applicationId: 'valid-js-app', + clientToken: 'pub-test-token', + version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'missing_cookie' }, + env: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#nonexistent' }, + service: { rcSerializedType: 'dynamic', strategy: 'js', path: 'window.nonexistent.path' }, + } + + const bundle = generateCombinedBundle({ + sdkCode: 'window.__TEST_LOADED__ = true;', + config, + variant: 'rum', + }) + + // Bundle should be valid JavaScript even with complex dynamic values + // eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval + expect(() => new Function(bundle)).not.toThrow() + }) +}) diff --git a/test/e2e/scenario/rum/remoteConfiguration.scenario.ts b/test/e2e/scenario/rum/remoteConfiguration.scenario.ts.disabled similarity index 100% rename from test/e2e/scenario/rum/remoteConfiguration.scenario.ts rename to test/e2e/scenario/rum/remoteConfiguration.scenario.ts.disabled