diff --git a/packages/rum-react/src/domain/reactRouter/createRouter.spec.ts b/packages/rum-react/src/domain/reactRouter/createRouter.spec.ts index 44de336268..8e8efced6a 100644 --- a/packages/rum-react/src/domain/reactRouter/createRouter.spec.ts +++ b/packages/rum-react/src/domain/reactRouter/createRouter.spec.ts @@ -15,10 +15,6 @@ describe('createRouter', () => { let router: ReturnType beforeEach(() => { - if (!window.AbortController) { - pending('createMemoryRouter relies on AbortController') - } - startViewSpy = jasmine.createSpy() initializeReactPlugin({ configuration: { diff --git a/packages/worker/src/domain/deflate.d.ts b/packages/worker/src/domain/deflate.d.ts index b88c815ce2..07b608818a 100644 --- a/packages/worker/src/domain/deflate.d.ts +++ b/packages/worker/src/domain/deflate.d.ts @@ -11,5 +11,3 @@ export class Deflate { export const constants: { [name: string]: number } - -export function string2buf(input: string): Uint8ArrayBuffer diff --git a/test/apps/base-extension/src/cdn.ts b/test/apps/base-extension/src/cdn.ts index b722b1a054..27351878c4 100644 --- a/test/apps/base-extension/src/cdn.ts +++ b/test/apps/base-extension/src/cdn.ts @@ -23,6 +23,7 @@ function load( ) { const script = document.createElement('script') script.src = url + script.crossOrigin = '' script.onload = () => { if (!window[sdk]) { console.error(`${sdk} is not loaded`) diff --git a/test/apps/microfrontend/common.ts b/test/apps/microfrontend/common.ts index 283a2d8170..00c6d00c3c 100644 --- a/test/apps/microfrontend/common.ts +++ b/test/apps/microfrontend/common.ts @@ -57,8 +57,8 @@ export function createApp(id: string, title: string, borderColor: string) { }) createButton(container, 'vital', () => { - const ref = window.DD_RUM.startDurationVital(`${id}-vital`) - window.DD_RUM.stopDurationVital(ref) + window.DD_RUM.startDurationVital(`${id}-vital`) + window.DD_RUM.stopDurationVital(`${id}-vital`) }) createButton(container, 'feature-operation', () => { diff --git a/test/apps/microfrontend/types.d.ts b/test/apps/microfrontend/types.d.ts index 57e02c9f9d..795a9a7e8f 100644 --- a/test/apps/microfrontend/types.d.ts +++ b/test/apps/microfrontend/types.d.ts @@ -2,8 +2,8 @@ interface Window { DD_RUM: { addError: (error: Error) => void addAction: (name: string, context?: any) => void - startDurationVital: (name: string) => any - stopDurationVital: (ref: any) => void + startDurationVital: (name: string, options?: { vitalKey?: string }) => void + stopDurationVital: (name: string, options?: { vitalKey?: string }) => void startFeatureOperation: (name: string) => void startView: (options: { name: string }) => void } diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index bb443e9de2..17c5b7f4b3 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -55,6 +55,7 @@ class TestBuilder { private eventBridge: EventBridgeOptions | undefined private setups: Array<{ factory: SetupFactory; name?: string }> = DEFAULT_SETUPS private testFixture: typeof test = test + private mockClock = false private extension: { rumConfiguration?: RumInitConfiguration logsConfiguration?: LogsInitConfiguration @@ -111,6 +112,11 @@ class TestBuilder { return this } + withMockClock() { + this.mockClock = true + return this + } + withVueApp() { this.baseUrlHooks.push((baseUrl, servers, { rum, context }) => { baseUrl.port = VUE_ROUTER_APP_PORT @@ -232,6 +238,7 @@ class TestBuilder { extension: this.extension, worker: this.worker, callerLocation: this.callerLocation, + mockClock: this.mockClock, } if (this.alsoRunWithRumSlim) { @@ -360,7 +367,7 @@ function declareTest(title: string, setupOptions: SetupOptions, factory: SetupFa servers.base.bindServerApp(createMockServerApp(servers, setup, setupOptions)) servers.crossOrigin.bindServerApp(createMockServerApp(servers, setup)) - await setUpTest(browserLogs, testContext) + await setUpTest(browserLogs, setupOptions, testContext) try { await runner(testContext) @@ -426,7 +433,11 @@ function createTestContext( } } -async function setUpTest(browserLogsManager: BrowserLogsManager, { baseUrl, page, browserContext }: TestContext) { +async function setUpTest( + browserLogsManager: BrowserLogsManager, + { mockClock }: SetupOptions, + { baseUrl, page, browserContext }: TestContext +) { browserContext.on('console', (msg) => { browserLogsManager.add({ level: msg.type() as BrowserLog['level'], @@ -445,6 +456,13 @@ async function setUpTest(browserLogsManager: BrowserLogsManager, { baseUrl, page }) }) + if (mockClock) { + try { + await page.clock.install() + } catch (e) { + test.skip(true, `Mock clock is not supported in this browser: ${String(e)}`) + } + } await page.goto(baseUrl) await waitForServersIdle() } diff --git a/test/e2e/lib/framework/intakeProxyMiddleware.ts b/test/e2e/lib/framework/intakeProxyMiddleware.ts index 4cfc0dbf24..0da25010ca 100644 --- a/test/e2e/lib/framework/intakeProxyMiddleware.ts +++ b/test/e2e/lib/framework/intakeProxyMiddleware.ts @@ -12,6 +12,7 @@ interface BaseIntakeRequest { isBridge: boolean encoding: string | null transport: string | null + batchTime: number | null } export type LogsIntakeRequest = { @@ -58,6 +59,7 @@ interface IntakeRequestInfos { intakeType: IntakeRequest['intakeType'] encoding: string | null transport: string | null + batchTime: number | null } interface IntakeProxyOptions { @@ -90,6 +92,8 @@ function computeIntakeRequestInfos(req: express.Request): IntakeRequestInfos { const encoding = req.headers['content-encoding'] || searchParams.get('dd-evp-encoding') const transport = searchParams.get('_dd.api') + const batchTimeRaw = searchParams.get('batch_time') + const batchTime = batchTimeRaw ? Number(batchTimeRaw) : null if (req.query.bridge === 'true') { const eventType = req.query.event_type @@ -97,6 +101,7 @@ function computeIntakeRequestInfos(req: express.Request): IntakeRequestInfos { isBridge: true, encoding, transport, + batchTime, intakeType: eventType === 'log' ? 'logs' : eventType === 'record' ? 'replay' : 'rum', } } @@ -113,6 +118,7 @@ function computeIntakeRequestInfos(req: express.Request): IntakeRequestInfos { isBridge: false, encoding, transport, + batchTime, intakeType, } } diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index 5029c7f5d2..dfaeb88b31 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -27,6 +27,7 @@ export interface SetupOptions { } worker?: WorkerOptions callerLocation?: CallerLocation + mockClock: boolean } export interface CallerLocation { @@ -74,7 +75,7 @@ export function asyncSetup(options: SetupOptions, servers: 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)}} -d=o.createElement(u);d.async=1;d.src=n +d=o.createElement(u);d.async=1;d.src=n;d.crossOrigin='' n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n) })(window,document,'script','${url}','${globalName}')` } @@ -122,7 +123,7 @@ export function bundleSetup(options: SetupOptions, servers: Servers) { const { logsScriptUrl, rumScriptUrl } = createCrossOriginScriptUrls(servers, options) if (options.logs) { - header += html`` + header += html`` header += html`` + header += html`` header += html`` + header += html`` header += html` - + + - `) - .run(async ({ intakeRegistry, flushEvents, page }) => { - const button = page.locator('my-button').first().locator('button') - await button.click() - await flushEvents() - const actionEvents = intakeRegistry.rumActionEvents expect(actionEvents).toHaveLength(1) expect(actionEvents[0].action?.target?.name).toBe('Shadow Button') expect(actionEvents[0]._dd.action?.target?.selector).toEqual('#shadow-host::shadow BUTTON') }) - createTest('with betaTrackActionsInShadowDom, traverse shadow boundary for data-dd-action-name') - .withRum({ trackUserInteractions: true, betaTrackActionsInShadowDom: true }) + createTest('traverse shadow boundary for data-dd-action-name') + .withRum({ trackUserInteractions: true }) .withBody(html` `) - .withRum({ trackEarlyRequests: true }) + .withRum() .run(async ({ intakeRegistry, flushEvents }) => { await flushEvents() const resourceEvent = intakeRegistry.rumResourceEvents.find((event) => event.resource.type === 'fetch') @@ -95,7 +95,12 @@ test.describe('rum resources', () => { test.describe('XHR abort support', () => { createTest('track aborted XHR') .withRum() - .run(async ({ intakeRegistry, flushEvents, page }) => { + .run(async ({ intakeRegistry, flushEvents, page, browserName }) => { + test.skip( + browserName === 'webkit', + 'WebKit does not reporte a PerformanceResourceTiming entry for aborted XHRs' + ) + await page.evaluate( () => new Promise((resolve) => { @@ -198,7 +203,12 @@ test.describe('rum resources', () => { test.describe('fetch abort support', () => { createTest('track aborted fetch') .withRum() - .run(async ({ intakeRegistry, flushEvents, page }) => { + .run(async ({ intakeRegistry, flushEvents, page, browserName }) => { + test.skip( + browserName === 'webkit', + 'WebKit does not reporte a PerformanceResourceTiming entry for aborted fetches' + ) + await page.evaluate( () => new Promise((resolve) => { @@ -385,7 +395,6 @@ function expectToHaveValidTimings(resourceEvent: RumResourceEvent) { test.describe('resource headers with trackResourceHeaders', () => { const TRACK_RESOURCE_HEADERS_CONFIG = { - enableExperimentalFeatures: ['track_resource_headers'], trackResourceHeaders: true, } @@ -468,7 +477,7 @@ test.describe('resource headers with trackResourceHeaders', () => { }) createTest('collect default and custom headers using DEFAULT_TRACKED_RESOURCE_HEADERS pattern') - .withRum({ enableExperimentalFeatures: ['track_resource_headers'] }) + .withRum() .withRumInit((configuration) => { configuration.trackResourceHeaders = [ ...window.DD_RUM!.DEFAULT_TRACKED_RESOURCE_HEADERS.map((name) => ({ name })), diff --git a/test/e2e/scenario/rum/sessions.scenario.ts b/test/e2e/scenario/rum/sessions.scenario.ts index 0e3b8832a7..b16a243ecb 100644 --- a/test/e2e/scenario/rum/sessions.scenario.ts +++ b/test/e2e/scenario/rum/sessions.scenario.ts @@ -1,4 +1,4 @@ -import { SESSION_STORE_KEY, SESSION_TIME_OUT_DELAY } from '@datadog/browser-core' +import { ONE_HOUR, ONE_MINUTE, SESSION_STORE_KEY, SESSION_TIME_OUT_DELAY } from '@datadog/browser-core' import { RecordType } from '@datadog/browser-rum/src/types' import { test, expect } from '@playwright/test' import { setCookie } from '../../lib/helpers/browser' @@ -40,6 +40,91 @@ test.describe('rum sessions', () => { }) }) + test.describe('session freeze/resume', () => { + createTest('does not send a view update with stale session data when the session expires while the page is frozen') + .withRum() + .withMockClock() + .run(async ({ intakeRegistry, flushEvents, page }) => { + const initialDate = await page.evaluate(() => Date.now()) + + // Fast forward 10 minutes: the view should still be active at this point. + await page.clock.fastForward('10:00') + + // Simulate a 24-hour page freeze by jumping time without firing timers. + await page.clock.fastForward('24:00:00') + + await flushEvents() + + // No requests were sent after the 24-hour freeze. + for (const request of intakeRegistry.rumRequests) { + expect(request.batchTime).toBeLessThan(initialDate + ONE_HOUR) + } + + // View events should only contain data from before the freeze (< 11 minutes of time_spent) + expect(intakeRegistry.rumViewEvents).not.toHaveLength(0) + for (const view of intakeRegistry.rumViewEvents) { + expect(view.view.time_spent / 1e6 / ONE_MINUTE).toBeLessThan(11) + } + }) + }) + + test.describe('cross-tab session isolation', () => { + createTest('a tab should not collect events under another tab session until an interaction happens') + .withRum() + .run(async ({ page, intakeRegistry, flushEvents }) => { + await expireSession(page, page.context()) + + // Simulate another tab opening a fresh session + const otherSessionId = '00000000-0000-0000-0000-000000000000' + await setCookie( + page, + SESSION_STORE_KEY, + `id=${otherSessionId}&created=${Date.now()}&expire=${Date.now() + 15 * ONE_MINUTE}`, + ONE_HOUR + ) + + await page.evaluate(() => { + window.DD_RUM!.addAction('not collected') + }) + + // Makes the tab picks up the new session + await page.locator('html').click() + + await page.evaluate(() => { + window.DD_RUM!.addAction('collected') + }) + + await flushEvents() + + const actionEvents = intakeRegistry.rumActionEvents + expect(actionEvents).toHaveLength(1) + expect(actionEvents[0].action.target!.name).toBe('collected') + expect(actionEvents[0].session.id).toBe(otherSessionId) + }) + + createTest('a visible tab with an expired session should not extend another tab session') + .withRum() + .withMockClock() + .run(async ({ page, browserContext }) => { + // Expire the current session + await expireSession(page, browserContext) + + // Simulate another tab opening a fresh session + const otherTabExpire = Date.now() + 15 * ONE_MINUTE + await setCookie( + page, + SESSION_STORE_KEY, + `id=other-tab-session&created=${Date.now()}&expire=${otherTabExpire}`, + ONE_HOUR + ) + + await page.clock.fastForward(ONE_MINUTE) + + const cookie = await findSessionCookie(browserContext) + expect(Number(cookie?.expire)).toEqual(otherTabExpire) + }) + }) + test.describe('session expiration', () => { createTest("don't send events when session is expired") // prevent recording start to generate late events diff --git a/test/e2e/scenario/rum/tracing.scenario.ts b/test/e2e/scenario/rum/tracing.scenario.ts index 0fe8e9d575..ef4422f3da 100644 --- a/test/e2e/scenario/rum/tracing.scenario.ts +++ b/test/e2e/scenario/rum/tracing.scenario.ts @@ -69,11 +69,10 @@ test.describe('tracing', () => { checkTraceAssociatedToRumEvent(intakeRegistry) }) - createTest('propagate trace baggage') + createTest('propagate trace baggage with user and account') .withRum({ service: 'service', allowedTracingUrls: ['LOCATION_ORIGIN'], - propagateTraceBaggage: true, enableExperimentalFeatures: ['user_account_trace_header'], }) .run(async ({ intakeRegistry, flushEvents, page }) => { @@ -88,7 +87,28 @@ test.describe('tracing', () => { .catch(() => new Error('Fetch request failed!')) ) const headers = parseHeaders(rawHeaders) - checkRequestHeaders(headers, { withBaggage: true }) + checkRequestHeaders(headers) + expect(headers['baggage']).toMatch(/user\.id=p1745/) + expect(headers['baggage']).toMatch(/account\.id=c9wpq8xrvd9t/) + await flushEvents() + checkTraceAssociatedToRumEvent(intakeRegistry) + }) + + createTest('do not propagate trace baggage when disabled') + .withRum({ + service: 'service', + allowedTracingUrls: ['LOCATION_ORIGIN'], + propagateTraceBaggage: false, + }) + .run(async ({ intakeRegistry, flushEvents, page }) => { + const rawHeaders = await page.evaluate(() => + window + .fetch('/headers') + .then((response) => response.text()) + .catch(() => new Error('Fetch request failed!')) + ) + const headers = parseHeaders(rawHeaders) + checkRequestHeaders(headers, { withBaggage: false }) await flushEvents() checkTraceAssociatedToRumEvent(intakeRegistry) }) @@ -107,18 +127,18 @@ test.describe('tracing', () => { return JSON.parse(rawHeaders) } - // By default, we send both Datadog and W3C tracecontext headers + // By default, we send both Datadog and W3C tracecontext headers, and baggage with session.id function checkRequestHeaders( headers: ParsedHeaders, - { withBaggage }: { withBaggage: boolean } = { withBaggage: false } + { withBaggage }: { withBaggage: boolean } = { withBaggage: true } ) { expect(headers['x-datadog-trace-id']).toMatch(/\d+/) expect(headers['x-datadog-origin']).toBe('rum') expect(headers['traceparent']).toMatch(/^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-01$/) if (withBaggage) { - expect(headers['baggage']).toMatch(/^session.id=.*,user.id=.*,account.id=.*$/) + expect(headers['baggage']).toMatch(/session\.id=\S+/) } else { - expect(headers['baggage']).not.toBeDefined() + expect(headers['baggage']).toBeUndefined() } } diff --git a/test/e2e/scenario/rum/views.scenario.ts b/test/e2e/scenario/rum/views.scenario.ts index 794af71a13..1c965a4cca 100644 --- a/test/e2e/scenario/rum/views.scenario.ts +++ b/test/e2e/scenario/rum/views.scenario.ts @@ -127,22 +127,6 @@ test.describe('rum views', () => { }) }) - createTest('send performance first input delay') - .withRum() - .withBody(html` `) - .run(async ({ browserName, flushEvents, intakeRegistry, page }) => { - test.skip( - browserName === 'webkit', - "Safari have an issue with 'event.timeStamp', so the 'first-input' polyfill is ignoring it and doesn't send a performance entry. See https://bugs.webkit.org/show_bug.cgi?id=211101" - ) - const button = page.locator('button') - await button.click() - await flushEvents() - const viewEvent = intakeRegistry.rumViewEvents[0] - expect(viewEvent).toBeDefined() - expect(viewEvent.view.first_input_delay).toBeGreaterThanOrEqual(0) - }) - test.describe('anchor navigation', () => { createTest("don't create a new view when it is an Anchor navigation") .withRum() diff --git a/test/e2e/scenario/rum/vitals.scenario.ts b/test/e2e/scenario/rum/vitals.scenario.ts index 3541711b02..af3734e950 100644 --- a/test/e2e/scenario/rum/vitals.scenario.ts +++ b/test/e2e/scenario/rum/vitals.scenario.ts @@ -7,10 +7,10 @@ test.describe('vital collection', () => { .withRum() .run(async ({ flushEvents, intakeRegistry, page }) => { await page.evaluate(() => { - const vital = window.DD_RUM!.startDurationVital('foo') + window.DD_RUM!.startDurationVital('foo') return new Promise((resolve) => { setTimeout(() => { - window.DD_RUM!.stopDurationVital(vital) + window.DD_RUM!.stopDurationVital('foo') resolve() }, 5) }) @@ -22,6 +22,29 @@ test.describe('vital collection', () => { expect(intakeRegistry.rumVitalEvents[0].vital.duration).toEqual(expect.any(Number)) }) + createTest('send two simultaneous duration vitals using vitalKey') + .withRum() + .run(async ({ flushEvents, intakeRegistry, page }) => { + await page.evaluate(() => { + const key1 = 'key-1' + const key2 = 'key-2' + window.DD_RUM!.startDurationVital('foo', { vitalKey: key1 }) + window.DD_RUM!.startDurationVital('foo', { vitalKey: key2 }) + return new Promise((resolve) => { + setTimeout(() => { + window.DD_RUM!.stopDurationVital('foo', { vitalKey: key1 }) + window.DD_RUM!.stopDurationVital('foo', { vitalKey: key2 }) + resolve() + }, 5) + }) + }) + await flushEvents() + + expect(intakeRegistry.rumVitalEvents).toHaveLength(2) + expect(intakeRegistry.rumVitalEvents[0].vital.name).toEqual('foo') + expect(intakeRegistry.rumVitalEvents[1].vital.name).toEqual('foo') + }) + createTest('send operation step vital') .withRum({ enableExperimentalFeatures: [ExperimentalFeature.FEATURE_OPERATION_VITAL], diff --git a/test/e2e/scenario/sessionStore.scenario.ts b/test/e2e/scenario/sessionStore.scenario.ts index 7cab8efc92..851daf63d7 100644 --- a/test/e2e/scenario/sessionStore.scenario.ts +++ b/test/e2e/scenario/sessionStore.scenario.ts @@ -1,3 +1,4 @@ +import type { MemorySession } from '@datadog/browser-core' import { SESSION_STORE_KEY, MEMORY_SESSION_STORE_KEY } from '@datadog/browser-core' import type { BrowserContext, Page } from '@playwright/test' import { test, expect } from '@playwright/test' @@ -29,16 +30,20 @@ test.describe('Session Stores', () => { expect(rumContext?.session_id).toBe(cookieSessionId) }) - createTest('when cookies are unavailable, Logs should start, but not RUM') + createTest('when cookies are unavailable, SDKs should not start') .withLogs() .withRum() .withHead(DISABLE_COOKIES) - .run(async ({ page }) => { + .run(async ({ page, withBrowserLogs }) => { const logsContext = await page.evaluate(() => window.DD_LOGS?.getInternalContext()) const rumContext = await page.evaluate(() => window.DD_RUM?.getInternalContext()) - expect(logsContext).not.toBeUndefined() + expect(logsContext).toBeUndefined() expect(rumContext).toBeUndefined() + + withBrowserLogs((logs) => { + expect(logs.filter((logs) => logs.message.includes('No storage available for session'))).toHaveLength(2) + }) }) test.describe('trackSessionAcrossSubdomains: false', () => { @@ -107,23 +112,14 @@ test.describe('Session Stores', () => { }) }) - for (const betaEncodeCookieOptions of [true, false]) { - createTest( - betaEncodeCookieOptions - ? 'should not fails when RUM and LOGS are initialized with different trackSessionAcrossSubdomains values when Encode Cookie Options is enabled' - : 'should fails when RUM and LOGS are initialized with different trackSessionAcrossSubdomains values when Encode Cookie Options is disabled' - ) - .withRum({ trackSessionAcrossSubdomains: true, betaEncodeCookieOptions }) - .withLogs({ trackSessionAcrossSubdomains: false, betaEncodeCookieOptions }) + test.describe('RUM and Logs with conflicting cookie options', () => { + createTest('with different trackSessionAcrossSubdomains values') + .withRum({ trackSessionAcrossSubdomains: true }) + .withLogs({ trackSessionAcrossSubdomains: false }) .withHostName(FULL_HOSTNAME) .run(async ({ page }) => { await page.waitForTimeout(1000) - if (!betaEncodeCookieOptions) { - // ensure the test is failing when betaEncodeCookieOptions is disabled - test.fail() - } - const [rumInternalContext, logsInternalContext] = await page.evaluate(() => [ window.DD_RUM?.getInternalContext(), window.DD_LOGS?.getInternalContext(), @@ -133,22 +129,13 @@ test.describe('Session Stores', () => { expect(logsInternalContext).toBeDefined() }) - createTest( - betaEncodeCookieOptions - ? 'should not fails when RUM and LOGS are initialized with different usePartitionedCrossSiteSessionCookie values when Encode Cookie Options is enabled' - : 'should fails when RUM and LOGS are initialized with different usePartitionedCrossSiteSessionCookie values when Encode Cookie Options is disabled' - ) - .withRum({ usePartitionedCrossSiteSessionCookie: true, betaEncodeCookieOptions }) - .withLogs({ usePartitionedCrossSiteSessionCookie: false, betaEncodeCookieOptions }) + createTest('with different usePartitionedCrossSiteSessionCookie values') + .withRum({ usePartitionedCrossSiteSessionCookie: true }) + .withLogs({ usePartitionedCrossSiteSessionCookie: false }) .withHostName(FULL_HOSTNAME) .run(async ({ page }) => { await page.waitForTimeout(1000) - if (!betaEncodeCookieOptions) { - // ensure the test is failing when betaEncodeCookieOptions is disabled - test.fail() - } - const [rumInternalContext, logsInternalContext] = await page.evaluate(() => [ window.DD_RUM?.getInternalContext(), window.DD_LOGS?.getInternalContext(), @@ -157,7 +144,7 @@ test.describe('Session Stores', () => { expect(rumInternalContext).toBeDefined() expect(logsInternalContext).toBeDefined() }) - } + }) async function injectSdkInAnIframe(page: Page, bundleUrl: string) { await page.evaluate( @@ -177,6 +164,7 @@ test.describe('Session Stores', () => { const script = iframeWindow.document.createElement('script') script.async = true script.src = browserSdkUrl + script.crossOrigin = '' iframeWindow.document.head.appendChild(script) }), bundleUrl @@ -198,16 +186,20 @@ test.describe('Session Stores', () => { expect(rumContext?.session_id).toBe(sessionId) }) - createTest('when localStorage is unavailable, Logs should start, but not RUM') + createTest('when localStorage is unavailable, SDKs should not start') .withLogs({ sessionPersistence: 'local-storage' }) .withRum({ sessionPersistence: 'local-storage' }) .withHead(DISABLE_LOCAL_STORAGE) - .run(async ({ page }) => { + .run(async ({ page, withBrowserLogs }) => { const logsContext = await page.evaluate(() => window.DD_LOGS?.getInternalContext()) const rumContext = await page.evaluate(() => window.DD_RUM?.getInternalContext()) - expect(logsContext).not.toBeUndefined() + expect(logsContext).toBeUndefined() expect(rumContext).toBeUndefined() + + withBrowserLogs((logs) => { + expect(logs.filter((logs) => logs.message.includes('No storage available for session'))).toHaveLength(2) + }) }) }) @@ -226,20 +218,6 @@ test.describe('Session Stores', () => { }) }) - createTest('allowFallbackToLocalStorage (deprecated)') - .withLogs({ allowFallbackToLocalStorage: true }) - .withRum({ allowFallbackToLocalStorage: true }) - .withHead(DISABLE_COOKIES) - .run(async ({ page }) => { - const sessionId = await getSessionIdFromLocalStorage(page) - - const logsContext = await page.evaluate(() => window.DD_LOGS?.getInternalContext()) - const rumContext = await page.evaluate(() => window.DD_RUM?.getInternalContext()) - - expect(logsContext?.session_id).toBe(sessionId) - expect(rumContext?.session_id).toBe(sessionId) - }) - createTest('sessionPersistence fallback') .withLogs({ sessionPersistence: ['local-storage', 'memory'] }) .withRum({ sessionPersistence: ['local-storage', 'memory'] }) @@ -261,11 +239,11 @@ async function getSessionIdFromLocalStorage(page: Page): Promise { - const sessionState = await page.evaluate( - (key) => (window as any)[key] as { id: string } | undefined, + const memorySession = await page.evaluate( + (key) => (window as any)[key] as MemorySession | undefined, MEMORY_SESSION_STORE_KEY ) - return sessionState?.id + return memorySession?.state?.id } async function getSessionIdFromCookie(browserContext: BrowserContext): Promise { diff --git a/test/e2e/scenario/telemetry.scenario.ts b/test/e2e/scenario/telemetry.scenario.ts index e98b649ee9..0f671801f8 100644 --- a/test/e2e/scenario/telemetry.scenario.ts +++ b/test/e2e/scenario/telemetry.scenario.ts @@ -67,6 +67,7 @@ test.describe('telemetry', () => { const event = intakeRegistry.telemetryConfigurationEvents[0] expect(event.service).toEqual('browser-rum-sdk') expect(event.telemetry.configuration.track_user_interactions).toEqual(true) + expect(event.session!.id).toEqual(expect.any(String)) expect(event.application!.id).toEqual(expect.any(String)) }) @@ -135,7 +136,7 @@ test.describe('telemetry', () => { Object.defineProperty(Document.prototype, 'cookie', { get: () => originalDescriptor.get.call(document), set: (value) => { - if (value.includes('_dd_s=')) { + if (value.includes('_dd_s_v2=')) { throw new Error('expected error') } originalDescriptor.set.call(document, value) diff --git a/test/e2e/scenario/trackingConsent.scenario.ts b/test/e2e/scenario/trackingConsent.scenario.ts index 8254b52f1c..3b5303d0f0 100644 --- a/test/e2e/scenario/trackingConsent.scenario.ts +++ b/test/e2e/scenario/trackingConsent.scenario.ts @@ -97,5 +97,19 @@ test.describe('tracking consent', () => { expect(intakeRegistry.isEmpty).toBe(false) expect(await findSessionCookie(browserContext)).toBeDefined() }) + + createTest('stops sending events if tracking consent is revoked') + .withLogs() + .run(async ({ intakeRegistry, flushEvents, browserContext, page }) => { + await page.evaluate(() => { + window.DD_LOGS!.setTrackingConsent('not-granted') + window.DD_LOGS!.logger.log('should not be sent') + }) + + await flushEvents() + + expect(intakeRegistry.logsEvents).toHaveLength(0) + expect((await findSessionCookie(browserContext))?.isExpired).toEqual('1') + }) }) }) diff --git a/test/performance/createBenchmarkTest.ts b/test/performance/createBenchmarkTest.ts index 2a124fd891..2e08d47a4e 100644 --- a/test/performance/createBenchmarkTest.ts +++ b/test/performance/createBenchmarkTest.ts @@ -94,6 +94,7 @@ async function injectSDK(page: Page, scenarioConfiguration: ScenarioConfiguratio const s = o.createElement(u) as HTMLScriptElement s.async = true s.src = n + s.crossOrigin = '' o.head.appendChild(s) })(window, document, 'script', sdkBundleUrl, 'DD_RUM') browserWindow.DD_RUM?.onReady(function () { diff --git a/test/unit/browsers.conf.ts b/test/unit/browsers.conf.ts index ebf634fb2e..7fdf9bd42d 100644 --- a/test/unit/browsers.conf.ts +++ b/test/unit/browsers.conf.ts @@ -13,21 +13,21 @@ export const browserConfigurations: BrowserConfiguration[] = [ { sessionName: 'Firefox', name: 'Firefox', - version: '67.0', + version: '78.0', os: 'Windows', osVersion: '11', }, { sessionName: 'Safari desktop', name: 'Safari', - version: '12.1', + version: '14.0', os: 'OS X', - osVersion: 'Mojave', + osVersion: 'Big Sur', }, { sessionName: 'Chrome desktop', name: 'Chrome', - version: '63.0', + version: '80.0', os: 'Windows', osVersion: '11', }, diff --git a/yarn.lock b/yarn.lock index 5e55799398..cc87776662 100644 --- a/yarn.lock +++ b/yarn.lock @@ -303,7 +303,7 @@ __metadata: languageName: node linkType: hard -"@datadog/browser-core@npm:6.33.0, @datadog/browser-core@workspace:*, @datadog/browser-core@workspace:packages/core": +"@datadog/browser-core@npm:7.0.0, @datadog/browser-core@workspace:*, @datadog/browser-core@workspace:packages/core": version: 0.0.0-use.local resolution: "@datadog/browser-core@workspace:packages/core" dependencies: @@ -316,9 +316,9 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-logs@workspace:packages/logs" dependencies: - "@datadog/browser-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" peerDependencies: - "@datadog/browser-rum": 6.33.0 + "@datadog/browser-rum": 7.0.0 peerDependenciesMeta: "@datadog/browser-rum": optional: true @@ -334,8 +334,8 @@ __metadata: "@angular/core": "npm:21.2.10" "@angular/platform-browser": "npm:21.2.10" "@angular/router": "npm:21.2.10" - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" rxjs: "npm:7.8.2" peerDependencies: "@angular/core": ">=15 <=21" @@ -344,11 +344,11 @@ __metadata: languageName: unknown linkType: soft -"@datadog/browser-rum-core@npm:6.33.0, @datadog/browser-rum-core@workspace:*, @datadog/browser-rum-core@workspace:packages/rum-core": +"@datadog/browser-rum-core@npm:7.0.0, @datadog/browser-rum-core@workspace:*, @datadog/browser-rum-core@workspace:packages/rum-core": version: 0.0.0-use.local resolution: "@datadog/browser-rum-core@workspace:packages/rum-core" dependencies: - "@datadog/browser-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" ajv: "npm:8.20.0" languageName: unknown linkType: soft @@ -357,9 +357,9 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-rum-nextjs@workspace:packages/rum-nextjs" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" - "@datadog/browser-rum-react": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" + "@datadog/browser-rum-react": "npm:7.0.0" "@types/react": "npm:19.2.14" next: "npm:16.2.4" react: "npm:19.2.5" @@ -379,8 +379,8 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-rum-nuxt@workspace:packages/rum-nuxt" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" vue: "npm:3.5.33" vue-router: "npm:5.0.6" peerDependencies: @@ -401,12 +401,12 @@ __metadata: languageName: unknown linkType: soft -"@datadog/browser-rum-react@npm:6.33.0, @datadog/browser-rum-react@workspace:packages/rum-react": +"@datadog/browser-rum-react@npm:7.0.0, @datadog/browser-rum-react@workspace:packages/rum-react": version: 0.0.0-use.local resolution: "@datadog/browser-rum-react@workspace:packages/rum-react" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" "@tanstack/react-router": "npm:1.168.25" "@types/react": "npm:19.2.14" "@types/react-dom": "npm:19.2.3" @@ -440,10 +440,10 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-rum-slim@workspace:packages/rum-slim" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" peerDependencies: - "@datadog/browser-logs": 6.33.0 + "@datadog/browser-logs": 7.0.0 peerDependenciesMeta: "@datadog/browser-logs": optional: true @@ -454,8 +454,8 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-rum-vue@workspace:packages/rum-vue" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" "@vue/test-utils": "npm:2.4.9" vue: "npm:3.5.33" vue-router: "npm:5.0.6" @@ -478,12 +478,12 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-rum@workspace:packages/rum" dependencies: - "@datadog/browser-core": "npm:6.33.0" - "@datadog/browser-rum-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" + "@datadog/browser-rum-core": "npm:7.0.0" "@types/pako": "npm:2.0.4" pako: "npm:2.1.0" peerDependencies: - "@datadog/browser-logs": 6.33.0 + "@datadog/browser-logs": 7.0.0 peerDependenciesMeta: "@datadog/browser-logs": optional: true @@ -517,7 +517,7 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/browser-worker@workspace:packages/worker" dependencies: - "@datadog/browser-core": "npm:6.33.0" + "@datadog/browser-core": "npm:7.0.0" webpack: "npm:5.106.2" languageName: unknown linkType: soft