diff --git a/test/app/yarn.lock b/test/app/yarn.lock index a54ea0122b..0dcb7bdc3a 100644 --- a/test/app/yarn.lock +++ b/test/app/yarn.lock @@ -2,21 +2,21 @@ # yarn lockfile v1 -"@datadog/browser-core@1.22.0", "@datadog/browser-core@file:../../packages/core": - version "1.22.0" +"@datadog/browser-core@1.25.0", "@datadog/browser-core@file:../../packages/core": + version "1.25.0" dependencies: tslib "^1.10.0" "@datadog/browser-logs@file:../../packages/logs": - version "1.22.0" + version "1.25.0" dependencies: - "@datadog/browser-core" "1.22.0" + "@datadog/browser-core" "1.25.0" tslib "^1.10.0" "@datadog/browser-rum@file:../../packages/rum": - version "1.22.0" + version "1.25.0" dependencies: - "@datadog/browser-core" "1.22.0" + "@datadog/browser-core" "1.25.0" tslib "^1.10.0" "@webassemblyjs/ast@1.8.5": diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 2fbc148cb8..8cb49a7ef8 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -1,5 +1,6 @@ import { deleteAllCookies, withBrowserLogs } from '../helpers/browser' import { flushEvents } from '../helpers/sdk' +import { validateFormat } from '../helpers/validation' import { EventRegistry } from './eventsRegistry' import { getTestServers, Servers, waitForServersIdle } from './httpServers' import { log } from './logger' @@ -9,12 +10,12 @@ import { createIntakeServerApp } from './serverApps/intake' import { createMockServerApp } from './serverApps/mock' const DEFAULT_RUM_OPTIONS = { - applicationId: 'appId', + applicationId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', clientToken: 'token', + enableExperimentalFeatures: ['v2_format'], } const DEFAULT_LOGS_OPTIONS = { - applicationId: 'appId', clientToken: 'token', } @@ -143,6 +144,7 @@ async function setUpTest({ baseUrl }: TestContext) { async function tearDownTest({ events }: TestContext) { await flushEvents() expect(events.internalMonitoring).toEqual([]) + validateFormat(events.rum) await withBrowserLogs((logs) => { logs.forEach((browserLog) => { log(`Browser ${browserLog.source}: ${browserLog.level} ${browserLog.message}`) diff --git a/test/e2e/lib/helpers/validation.ts b/test/e2e/lib/helpers/validation.ts new file mode 100644 index 0000000000..86008984ca --- /dev/null +++ b/test/e2e/lib/helpers/validation.ts @@ -0,0 +1,30 @@ +import ajv from 'ajv' +import rumEventsFormatJson from '../../../../rum-events-format/rum-events-format.json' +import _commonSchemaJson from '../../../../rum-events-format/schemas/_common-schema.json' +import actionSchemaJson from '../../../../rum-events-format/schemas/action-schema.json' +import errorSchemaJson from '../../../../rum-events-format/schemas/error-schema.json' +import long_taskSchemaJson from '../../../../rum-events-format/schemas/long_task-schema.json' +import resourceSchemaJson from '../../../../rum-events-format/schemas/resource-schema.json' +import viewSchemaJson from '../../../../rum-events-format/schemas/view-schema.json' +import { ServerRumEvent } from '../types/serverEvents' + +export function validateFormat(events: ServerRumEvent[]) { + events.forEach((event) => { + const instance = new ajv({ + allErrors: true, + }) + instance + .addSchema(_commonSchemaJson, 'schemas/_common-schema.json') + .addSchema(viewSchemaJson, 'schemas/view-schema.json') + .addSchema(actionSchemaJson, 'schemas/action-schema.json') + .addSchema(resourceSchemaJson, 'schemas/resource-schema.json') + .addSchema(long_taskSchemaJson, 'schemas/long_task-schema.json') + .addSchema(errorSchemaJson, 'schemas/error-schema.json') + .addSchema(rumEventsFormatJson, 'rum-events-format.json') + .validate('rum-events-format.json', event) + + if (instance.errors) { + instance.errors.map((error) => fail(`${error.dataPath || 'event'} ${error.message}`)) + } + }) +} diff --git a/test/e2e/lib/types/serverEvents.ts b/test/e2e/lib/types/serverEvents.ts index 8b8a76afb0..620c8322b0 100644 --- a/test/e2e/lib/types/serverEvents.ts +++ b/test/e2e/lib/types/serverEvents.ts @@ -1,5 +1,3 @@ -export type AnyServerEvent = ServerLogsMessage | ServerRumEvent | ServerInternalMonitoringMessage - export interface ServerLogsMessage { message: string application_id: string @@ -25,9 +23,7 @@ export interface ServerInternalMonitoringMessage { export interface ServerRumEvent { date: number - evt: { - category: 'resource' | 'long_task' | 'user_action' | 'error' | 'view' - } + type: 'resource' | 'long_task' | 'action' | 'error' | 'view' } interface PerformanceTiming { @@ -36,55 +32,53 @@ interface PerformanceTiming { } export interface ServerRumResourceEvent extends ServerRumEvent { - evt: { - category: 'resource' - } - http: { + type: 'resource' + resource: { url: string method: string status_code: number - performance?: { - download: PerformanceTiming - redirect: PerformanceTiming - } - } - resource: { - kind: 'fetch' | 'xhr' | 'document' + download: PerformanceTiming + redirect: PerformanceTiming + type: 'fetch' | 'xhr' | 'document' id?: string + duration: number } - duration: number _dd?: { trace_id: string span_id?: string } - user_action?: { + action?: { id: string } } export function isRumResourceEvent(event: ServerRumEvent): event is ServerRumResourceEvent { - return event.evt.category === 'resource' + return event.type === 'resource' } -export interface ServerRumUserActionEvent extends ServerRumEvent { - evt: { - category: 'user_action' - name: string - } - duration: number - user_action: { +export interface ServerRumActionEvent extends ServerRumEvent { + type: 'action' + action: { + loading_time: number id?: string type: 'click' | 'custom' - measures: { - resource_count: number - error_count: number - long_task_count: number + resource: { + count: number + } + error: { + count: number + } + long_task: { + count: number + } + target: { + name: string } } } -export function isRumUserActionEvent(event: ServerRumEvent): event is ServerRumUserActionEvent { - return event.evt.category === 'user_action' +export function isRumUserActionEvent(event: ServerRumEvent): event is ServerRumActionEvent { + return event.type === 'action' } export enum ServerRumViewLoadingType { @@ -93,39 +87,35 @@ export enum ServerRumViewLoadingType { } export interface ServerRumViewEvent extends ServerRumEvent { - evt: { - category: 'view' - } - rum: { + type: 'view' + _dd: { document_version: number } - session_id: string + session: { + id: string + } view: { id: string loading_type: ServerRumViewLoadingType - measures: { - dom_complete: number - dom_content_loaded: number - dom_interactive: number - load_event_end: number - } + dom_complete: number + dom_content_loaded: number + dom_interactive: number + load_event_end: number } } export function isRumViewEvent(event: ServerRumEvent): event is ServerRumViewEvent { - return event.evt.category === 'view' + return event.type === 'view' } export interface ServerRumErrorEvent extends ServerRumEvent { - evt: { - category: 'error' - } - message: string + type: 'error' error: { - origin: string + message: string + source: string } } export function isRumErrorEvent(event: ServerRumEvent): event is ServerRumErrorEvent { - return event.evt.category === 'error' + return event.type === 'error' } diff --git a/test/e2e/scenario/logs.scenario.ts b/test/e2e/scenario/logs.scenario.ts index d48825a1d9..90f4ec977b 100644 --- a/test/e2e/scenario/logs.scenario.ts +++ b/test/e2e/scenario/logs.scenario.ts @@ -40,7 +40,7 @@ describe('logs', () => { await flushEvents() expect(events.logs.length).toBe(1) expect(events.logs[0].view.id).toBeDefined() - expect(events.logs[0].application_id).toBe('appId') + expect(events.logs[0].application_id).toBe('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee') }) createTest('track fetch error') diff --git a/test/e2e/scenario/rum/actions.scenario.ts b/test/e2e/scenario/rum/actions.scenario.ts index e5840fba4f..04ab09f601 100644 --- a/test/e2e/scenario/rum/actions.scenario.ts +++ b/test/e2e/scenario/rum/actions.scenario.ts @@ -22,17 +22,23 @@ describe('action collection', () => { const actionEvents = events.rumActions expect(actionEvents.length).toBe(1) - expect(actionEvents[0].user_action).toEqual({ + expect(actionEvents[0].action).toEqual({ + error: { + count: 0, + }, id: (jasmine.any(String) as unknown) as string, - measures: { - error_count: 0, - long_task_count: (jasmine.any(Number) as unknown) as number, - resource_count: 0, + loading_time: (jasmine.any(Number) as unknown) as number, + long_task: { + count: (jasmine.any(Number) as unknown) as number, + }, + resource: { + count: 0, + }, + target: { + name: 'click me', }, type: 'click', }) - expect(actionEvents[0].evt.name).toBe('click me') - expect(actionEvents[0].duration).toBeGreaterThanOrEqual(0) }) createTest('associate a request to its action') @@ -54,22 +60,28 @@ describe('action collection', () => { await waitForServersIdle() await flushEvents() const actionEvents = events.rumActions - const resourceEvents = events.rumResources.filter((event) => event.resource.kind === 'fetch') + const resourceEvents = events.rumResources.filter((event) => event.resource.type === 'fetch') expect(actionEvents.length).toBe(1) - expect(actionEvents[0].user_action).toEqual({ + expect(actionEvents[0].action).toEqual({ + error: { + count: 0, + }, id: (jasmine.any(String) as unknown) as string, - measures: { - error_count: 0, - long_task_count: (jasmine.any(Number) as unknown) as number, - resource_count: 1, + loading_time: (jasmine.any(Number) as unknown) as number, + long_task: { + count: (jasmine.any(Number) as unknown) as number, + }, + resource: { + count: 1, + }, + target: { + name: 'click me', }, type: 'click', }) - expect(actionEvents[0].evt.name).toBe('click me') - expect(actionEvents[0].duration).toBeGreaterThan(0) expect(resourceEvents.length).toBe(1) - expect(resourceEvents[0].user_action!.id).toBe(actionEvents[0].user_action.id!) + expect(resourceEvents[0].action!.id).toBe(actionEvents[0].action.id!) }) }) diff --git a/test/e2e/scenario/rum/errors.scenario.ts b/test/e2e/scenario/rum/errors.scenario.ts index 17d41fe80d..c511c73119 100644 --- a/test/e2e/scenario/rum/errors.scenario.ts +++ b/test/e2e/scenario/rum/errors.scenario.ts @@ -11,8 +11,8 @@ describe('rum errors', () => { }) await flushEvents() expect(events.rumErrors.length).toBe(1) - expect(events.rumErrors[0].message).toBe('console error: oh snap') - expect(events.rumErrors[0].error.origin).toBe('console') + expect(events.rumErrors[0].error.message).toBe('console error: oh snap') + expect(events.rumErrors[0].error.source).toBe('console') await withBrowserLogs((browserLogs) => { expect(browserLogs.length).toEqual(1) }) diff --git a/test/e2e/scenario/rum/resources.scenario.ts b/test/e2e/scenario/rum/resources.scenario.ts index 00cb534348..7e3212fbf1 100644 --- a/test/e2e/scenario/rum/resources.scenario.ts +++ b/test/e2e/scenario/rum/resources.scenario.ts @@ -11,10 +11,10 @@ describe('rum resources', () => { .run(async ({ events }) => { await sendXhr(`/ok?duration=${REQUEST_DURATION}`) await flushEvents() - const resourceEvent = events.rumResources.find((r) => r.http.url.includes('/ok'))! + const resourceEvent = events.rumResources.find((r) => r.resource.url.includes('/ok'))! expect(resourceEvent).toBeDefined() - expect(resourceEvent.http.method).toBe('GET') - expect(resourceEvent.http.status_code).toBe(200) + expect(resourceEvent.resource.method).toBe('GET') + expect(resourceEvent.resource.status_code).toBe(200) expectToHaveValidTimings(resourceEvent) }) @@ -23,13 +23,13 @@ describe('rum resources', () => { .run(async ({ events }) => { await sendXhr(`/redirect?duration=${REQUEST_DURATION}`) await flushEvents() - const resourceEvent = events.rumResources.find((r) => r.http.url.includes('/redirect'))! + const resourceEvent = events.rumResources.find((r) => r.resource.url.includes('/redirect'))! expect(resourceEvent).not.toBeUndefined() - expect(resourceEvent.http.method).toEqual('GET') - expect(resourceEvent.http.status_code).toEqual(200) + expect(resourceEvent.resource.method).toEqual('GET') + expect(resourceEvent.resource.status_code).toEqual(200) expectToHaveValidTimings(resourceEvent) - expect(resourceEvent.http.performance!.redirect).not.toBeUndefined() - expect(resourceEvent.http.performance!.redirect!.duration).toBeGreaterThan(0) + expect(resourceEvent.resource.redirect).not.toBeUndefined() + expect(resourceEvent.resource.redirect!.duration).toBeGreaterThan(0) }) createTest("don't track disallowed cross origin xhr timings") @@ -37,12 +37,12 @@ describe('rum resources', () => { .run(async ({ crossOriginUrl, events }) => { await sendXhr(`${crossOriginUrl}/ok?duration=${REQUEST_DURATION}`) await flushEvents() - const resourceEvent = events.rumResources.find((r) => r.http.url.includes('/ok'))! + const resourceEvent = events.rumResources.find((r) => r.resource.url.includes('/ok'))! expect(resourceEvent).toBeDefined() - expect(resourceEvent.http.method).toEqual('GET') - expect(resourceEvent.http.status_code).toEqual(200) - expect(resourceEvent.duration).toBeGreaterThan(0) - expect(resourceEvent.http.performance).toBeUndefined() + expect(resourceEvent.resource.method).toEqual('GET') + expect(resourceEvent.resource.status_code).toEqual(200) + expect(resourceEvent.resource.duration).toBeGreaterThan(0) + expect(resourceEvent.resource.download).toBeUndefined() }) createTest('track allowed cross origin xhr timings') @@ -50,10 +50,10 @@ describe('rum resources', () => { .run(async ({ crossOriginUrl, events }) => { await sendXhr(`${crossOriginUrl}/ok?timing-allow-origin=true&duration=${REQUEST_DURATION}`) await flushEvents() - const resourceEvent = events.rumResources.find((r) => r.http.url.includes('/ok'))! + const resourceEvent = events.rumResources.find((r) => r.resource.url.includes('/ok'))! expect(resourceEvent).not.toBeUndefined() - expect(resourceEvent.http.method).toEqual('GET') - expect(resourceEvent.http.status_code).toEqual(200) + expect(resourceEvent.resource.method).toEqual('GET') + expect(resourceEvent.resource.status_code).toEqual(200) expectToHaveValidTimings(resourceEvent) }) @@ -66,7 +66,7 @@ describe('rum resources', () => { ) .run(async ({ events }) => { await flushEvents() - const resourceEvent = events.rumResources.find((event) => event.http.url.includes('empty.css')) + const resourceEvent = events.rumResources.find((event) => event.resource.url.includes('empty.css')) expect(resourceEvent).toBeDefined() expectToHaveValidTimings(resourceEvent!) }) @@ -75,19 +75,19 @@ describe('rum resources', () => { .withRum() .run(async ({ baseUrl, events }) => { await flushEvents() - const resourceEvent = events.rumResources.find((event) => event.resource.kind === 'document') + const resourceEvent = events.rumResources.find((event) => event.resource.type === 'document') expect(resourceEvent).toBeDefined() - expect(resourceEvent!.http.url).toBe(`${baseUrl}/`) + expect(resourceEvent!.resource.url).toBe(`${baseUrl}/`) expectToHaveValidTimings(resourceEvent!) }) }) function expectToHaveValidTimings(resourceEvent: ServerRumResourceEvent) { expect(resourceEvent.date).toBeGreaterThan(0) - expect(resourceEvent.duration).toBeGreaterThan(0) - const performance = resourceEvent.http.performance + expect(resourceEvent.resource.duration).toBeGreaterThan(0) + const download = resourceEvent.resource.download // timing could have been discarded by the SDK if there was not in the correct order - if (performance) { - expect(performance.download.start).toBeGreaterThan(0) + if (download) { + expect(download.start).toBeGreaterThan(0) } } diff --git a/test/e2e/scenario/tracing.scenario.ts b/test/e2e/scenario/rum/tracing.scenario.ts similarity index 83% rename from test/e2e/scenario/tracing.scenario.ts rename to test/e2e/scenario/rum/tracing.scenario.ts index d76019b987..dbc5a954dd 100644 --- a/test/e2e/scenario/tracing.scenario.ts +++ b/test/e2e/scenario/rum/tracing.scenario.ts @@ -1,6 +1,6 @@ -import { createTest, EventRegistry } from '../lib/framework' -import { sendFetch, sendXhr } from '../lib/helpers/browser' -import { flushEvents } from '../lib/helpers/sdk' +import { createTest, EventRegistry } from '../../lib/framework' +import { sendFetch, sendXhr } from '../../lib/helpers/browser' +import { flushEvents } from '../../lib/helpers/sdk' describe('tracing', () => { createTest('trace xhr') @@ -30,7 +30,7 @@ describe('tracing', () => { async function checkTraceAssociatedToRumEvent(events: EventRegistry) { const requests = events.rumResources.filter( - (event) => event.resource.kind === 'xhr' || event.resource.kind === 'fetch' + (event) => event.resource.type === 'xhr' || event.resource.type === 'fetch' ) expect(requests.length).toBe(1) expect(requests[0]._dd!.trace_id).toMatch(/\d+/) diff --git a/test/e2e/scenario/rum/views.scenario.ts b/test/e2e/scenario/rum/views.scenario.ts index 667d51523c..cbfb6958d0 100644 --- a/test/e2e/scenario/rum/views.scenario.ts +++ b/test/e2e/scenario/rum/views.scenario.ts @@ -10,11 +10,10 @@ describe('rum views', () => { await flushEvents() const viewEvent = events.rumViews[0] expect(viewEvent).toBeDefined() - const measures = viewEvent!.view.measures! - expect(measures.dom_complete).toBeGreaterThan(0) - expect(measures.dom_content_loaded).toBeGreaterThan(0) - expect(measures.dom_interactive).toBeGreaterThan(0) - expect(measures.load_event_end).toBeGreaterThan(0) + expect(viewEvent.view.dom_complete).toBeGreaterThan(0) + expect(viewEvent.view.dom_content_loaded).toBeGreaterThan(0) + expect(viewEvent.view.dom_interactive).toBeGreaterThan(0) + expect(viewEvent.view.load_event_end).toBeGreaterThan(0) }) createTest('create a new View when the session is renewed') @@ -25,7 +24,7 @@ describe('rum views', () => { const viewEvents = events.rumViews const firstViewEvent = viewEvents[0] const lastViewEvent = viewEvents[viewEvents.length - 1] - expect(firstViewEvent.session_id).not.toBe(lastViewEvent.session_id) + expect(firstViewEvent.session.id).not.toBe(lastViewEvent.session.id) expect(firstViewEvent.view.id).not.toBe(lastViewEvent.view.id) const distinctIds = new Set(viewEvents.map((viewEvent) => viewEvent.view.id)) diff --git a/test/e2e/tsconfig.json b/test/e2e/tsconfig.json index 9ce3bec904..4ad22fba6d 100644 --- a/test/e2e/tsconfig.json +++ b/test/e2e/tsconfig.json @@ -4,13 +4,14 @@ "strict": true, "experimentalDecorators": true, "esModuleInterop": true, + "resolveJsonModule": true, "target": "es5", "plugins": [ { "name": "typescript-tslint-plugin" } ], - "types": ["node", "webdriverio", "jasmine"], + "types": ["node", "webdriverio", "jasmine", "ajv"], "paths": { "@datadog/browser-logs": ["../../packages/logs/src"], "@datadog/browser-rum": ["../../packages/rum/src"], diff --git a/tslint.json b/tslint.json index 0d66adc05e..a3cbabe40e 100644 --- a/tslint.json +++ b/tslint.json @@ -20,7 +20,7 @@ "no-implicit-dependencies": [ true, "dev", - ["@datadog/browser-logs", "@datadog/browser-rum", "@datadog/browser-core"] + ["@datadog/browser-logs", "@datadog/browser-rum", "@datadog/browser-core", "ajv"] ], "no-null-keyword": true, "no-submodule-imports": [true],