diff --git a/packages/core/src/tools/experimentalFeatures.ts b/packages/core/src/tools/experimentalFeatures.ts index 7d4de3ef76..e6c90f7a68 100644 --- a/packages/core/src/tools/experimentalFeatures.ts +++ b/packages/core/src/tools/experimentalFeatures.ts @@ -14,7 +14,6 @@ export enum ExperimentalFeature { PAGEHIDE = 'pagehide', FEATURE_FLAGS = 'feature_flags', RESOURCE_PAGE_STATES = 'resource_page_states', - PAGE_STATES = 'page_states', COLLECT_FLUSH_REASON = 'collect_flush_reason', SCROLLMAP = 'scrollmap', } diff --git a/packages/rum-core/src/domain/contexts/foregroundContexts.spec.ts b/packages/rum-core/src/domain/contexts/foregroundContexts.spec.ts deleted file mode 100644 index 140372ad84..0000000000 --- a/packages/rum-core/src/domain/contexts/foregroundContexts.spec.ts +++ /dev/null @@ -1,249 +0,0 @@ -import type { RelativeTime, Duration, ServerDuration } from '@datadog/browser-core' -import { relativeNow } from '@datadog/browser-core' -import type { TestSetupBuilder } from '../../../test' -import { setup } from '../../../test' -import { mapToForegroundPeriods } from './foregroundContexts' -import type { PageStateHistory } from './pageStateHistory' -import { PageState, startPageStateHistory } from './pageStateHistory' - -const FOCUS_PERIOD_LENGTH = 10 as Duration -const BLUR_PERIOD_LENGTH = 5 as Duration - -describe('foreground context', () => { - let setupBuilder: TestSetupBuilder - let pageStateHistory: PageStateHistory - - function addNewForegroundPeriod() { - pageStateHistory.addPageState(PageState.ACTIVE) - } - - function closeForegroundPeriod() { - pageStateHistory.addPageState(PageState.PASSIVE) - } - - function selectInForegroundPeriodsFor(startTime: RelativeTime, duration: Duration) { - const pageStates = pageStateHistory.findAll(startTime, duration) - return mapToForegroundPeriods(pageStates || [], duration) - } - - function isInForegroundAt(startTime: RelativeTime) { - return pageStateHistory.isInActivePageStateAt(startTime) - } - - beforeEach(() => { - setupBuilder = setup() - .withFakeClock() - .beforeBuild(() => { - pageStateHistory = startPageStateHistory() - return pageStateHistory - }) - }) - - afterEach(() => { - setupBuilder.cleanup() - }) - - describe('when the page do not have the focus when starting', () => { - beforeEach(() => { - spyOn(Document.prototype, 'hasFocus').and.callFake(() => false) - pageStateHistory = startPageStateHistory() - }) - describe('without any focus nor blur event', () => { - describe('isInForegroundAt', () => { - it('should return false', () => { - const { clock } = setupBuilder.build() - - clock.tick(1_000) - - expect(isInForegroundAt(relativeNow())).toEqual(false) - }) - }) - - describe('selectInForegroundPeriodsFor', () => { - it('should an empty array', () => { - const { clock } = setupBuilder.build() - - clock.tick(1_000) - - expect(selectInForegroundPeriodsFor(relativeNow(), 0 as Duration)).toEqual([]) - }) - }) - }) - - describe('with two closed focus period & one active one', () => { - /* - events F B F B F - periods <------> <-------> <---- - - - - time 0 5 10 15 20 25 30 35 40 45 - */ - beforeEach(() => { - const { clock } = setupBuilder.build() - clock.tick(BLUR_PERIOD_LENGTH) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH) - closeForegroundPeriod() - clock.tick(BLUR_PERIOD_LENGTH) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH) - closeForegroundPeriod() - clock.tick(BLUR_PERIOD_LENGTH) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH) - }) - - it('isInForegroundAt should match the focused/burred period', () => { - // first blurred period - expect(isInForegroundAt(2 as RelativeTime)).toEqual(false) - - // first focused period - expect(isInForegroundAt(10 as RelativeTime)).toEqual(true) - - // second blurred period - expect(isInForegroundAt(17 as RelativeTime)).toEqual(false) - - // second focused period - expect(isInForegroundAt(25 as RelativeTime)).toEqual(true) - - // third blurred period - expect(isInForegroundAt(32 as RelativeTime)).toEqual(false) - - // current focused periods - expect(isInForegroundAt(42 as RelativeTime)).toEqual(true) - }) - - describe('selectInForegroundPeriodsFor', () => { - it('should have 3 in foreground periods for the whole period', () => { - const periods = selectInForegroundPeriodsFor(0 as RelativeTime, 50 as Duration) - - expect(periods).toHaveSize(3) - expect(periods[0]).toEqual({ - start: (5 * 1e6) as ServerDuration, - duration: (10 * 1e6) as ServerDuration, - }) - expect(periods[1]).toEqual({ - start: (20 * 1e6) as ServerDuration, - duration: (10 * 1e6) as ServerDuration, - }) - expect(periods[2]).toEqual({ - start: (35 * 1e6) as ServerDuration, - duration: (15 * 1e6) as ServerDuration, - }) - }) - - it('should have 2 in foreground periods when in between the two full periods', () => { - const periods = selectInForegroundPeriodsFor(10 as RelativeTime, 15 as Duration) - - expect(periods).toHaveSize(2) - expect(periods[0]).toEqual({ - start: 0 as ServerDuration, - duration: (5 * 1e6) as ServerDuration, - }) - expect(periods[1]).toEqual({ - start: (10 * 1e6) as ServerDuration, - duration: (5 * 1e6) as ServerDuration, - }) - }) - - it('should have 2 periods, when in between the the full period and ongoing periods', () => { - const periods = selectInForegroundPeriodsFor(25 as RelativeTime, 20 as Duration) - - expect(periods).toHaveSize(2) - expect(periods[0]).toEqual({ - start: 0 as ServerDuration, - duration: (5 * 1e6) as ServerDuration, - }) - expect(periods[1]).toEqual({ - start: (10 * 1e6) as ServerDuration, - duration: (10 * 1e6) as ServerDuration, - }) - }) - }) - }) - - describe('with one missing blur event. with two closed focus period every 5 seconds lasting 10 seconds', () => { - /* - events F F B - periods <------><-------> - time 0 5 10 15 20 25 - */ - beforeEach(() => { - const { clock } = setupBuilder.build() - clock.tick(BLUR_PERIOD_LENGTH) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH) - closeForegroundPeriod() - clock.tick(BLUR_PERIOD_LENGTH) - }) - it('isInForegroundAt should match the focused/burred period', () => { - expect(isInForegroundAt(2 as RelativeTime)).toEqual(false) - expect(isInForegroundAt(10 as RelativeTime)).toEqual(true) - expect(isInForegroundAt(20 as RelativeTime)).toEqual(true) - expect(isInForegroundAt(30 as RelativeTime)).toEqual(false) - }) - }) - - it('should not be in foreground, when the periods is closed twice', () => { - const { clock } = setupBuilder.build() - addNewForegroundPeriod() - clock.tick(BLUR_PERIOD_LENGTH) - pageStateHistory.addPageState(PageState.PASSIVE) - - expect(isInForegroundAt(relativeNow())).toEqual(false) - }) - - it('after starting with a blur even, should not be in foreground', () => { - pageStateHistory.addPageState(PageState.PASSIVE) - - expect(isInForegroundAt(relativeNow())).toEqual(false) - }) - }) - - describe('when the page has focus when starting', () => { - beforeEach(() => { - spyOn(Document.prototype, 'hasFocus').and.callFake(() => true) - pageStateHistory = startPageStateHistory() - }) - - describe('when there is no focus event', () => { - it('should return true during the focused period', () => { - const { clock } = setupBuilder.build() - clock.tick(FOCUS_PERIOD_LENGTH) - closeForegroundPeriod() - - expect(isInForegroundAt(2 as RelativeTime)).toEqual(true) - }) - - it('should return false after the first focused period', () => { - const { clock } = setupBuilder.build() - clock.tick(FOCUS_PERIOD_LENGTH) - closeForegroundPeriod() - - expect(isInForegroundAt(12 as RelativeTime)).toEqual(false) - }) - }) - - describe('when still getting the first focus event and closing the first periods after 10 seconds', () => { - it('should return true during the focused period', () => { - const { clock } = setupBuilder.build() - clock.tick(FOCUS_PERIOD_LENGTH / 2) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH / 2) - closeForegroundPeriod() - - expect(isInForegroundAt(2 as RelativeTime)).toEqual(true) - }) - - it('should return false after the focused period', () => { - const { clock } = setupBuilder.build() - clock.tick(FOCUS_PERIOD_LENGTH / 2) - addNewForegroundPeriod() - clock.tick(FOCUS_PERIOD_LENGTH / 2) - closeForegroundPeriod() - - expect(isInForegroundAt(12 as RelativeTime)).toEqual(false) - }) - }) - }) -}) diff --git a/packages/rum-core/src/domain/contexts/foregroundContexts.ts b/packages/rum-core/src/domain/contexts/foregroundContexts.ts deleted file mode 100644 index ce21728ea5..0000000000 --- a/packages/rum-core/src/domain/contexts/foregroundContexts.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { RelativeTime, Duration, ServerDuration } from '@datadog/browser-core' -import { toServerDuration } from '@datadog/browser-core' -import type { InForegroundPeriod, PageStateServerEntry } from '../../rawRumEvent.types' -import { PageState } from './pageStateHistory' - -export interface ForegroundPeriod { - start: RelativeTime - end?: RelativeTime -} - -// Todo: Remove in the next major release -export function mapToForegroundPeriods( - pageStateServerEntries: PageStateServerEntry[], - duration: Duration -): InForegroundPeriod[] { - const foregroundPeriods: InForegroundPeriod[] = [] - for (let i = 0; i < pageStateServerEntries.length; i++) { - const current = pageStateServerEntries[i] - const next = pageStateServerEntries[i + 1] - - if (current.state === PageState.ACTIVE) { - const start = current.start >= 0 ? current.start : (0 as ServerDuration) - const end = next ? next.start : toServerDuration(duration) - foregroundPeriods.push({ - start, - duration: (end - start) as ServerDuration, - }) - } - } - - return foregroundPeriods -} diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts index 44b8c939fa..56e605a89a 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.spec.ts @@ -1,5 +1,5 @@ import type { Duration, RelativeTime, ServerDuration, TimeStamp } from '@datadog/browser-core' -import { resetExperimentalFeatures, ExperimentalFeature, addExperimentalFeatures } from '@datadog/browser-core' +import { resetExperimentalFeatures } from '@datadog/browser-core' import type { RecorderApi } from '../../../boot/rumPublicApi' import type { TestSetupBuilder } from '../../../../test' import { setup, noopRecorderApi } from '../../../../test' @@ -105,7 +105,10 @@ describe('viewCollection', () => { _dd: { document_version: 3, replay_stats: undefined, - page_states: undefined, + page_states: [ + { start: 0 as ServerDuration, state: PageState.ACTIVE }, + { start: 10 as ServerDuration, state: PageState.PASSIVE }, + ], }, date: jasmine.any(Number), type: RumEventType.VIEW, @@ -144,7 +147,6 @@ describe('viewCollection', () => { count: 10, }, time_spent: (100 * 1e6) as ServerDuration, - in_foreground_periods: [{ start: 0 as ServerDuration, duration: 10 as ServerDuration }], }, session: { has_replay: undefined, @@ -207,25 +209,6 @@ describe('viewCollection', () => { expect(rawRumViewEvent.view.loading_time).toBeUndefined() }) - it('should include page_states but not in_foreground_periods when PAGE_STATES ff is enabled', () => { - addExperimentalFeatures([ExperimentalFeature.PAGE_STATES]) - const { lifeCycle, rawRumEvents } = setupBuilder.build() - lifeCycle.notify(LifeCycleEventType.VIEW_UPDATED, VIEW) - const rawRumViewEvent = rawRumEvents[rawRumEvents.length - 1].rawRumEvent as RawRumViewEvent - - expect(rawRumViewEvent._dd.page_states).toBeDefined() - expect(rawRumViewEvent.view.in_foreground_periods).toBeUndefined() - }) - - it('should include in_foreground_periods but not page_states when PAGE_STATES ff is disabled', () => { - const { lifeCycle, rawRumEvents } = setupBuilder.build() - lifeCycle.notify(LifeCycleEventType.VIEW_UPDATED, VIEW) - const rawRumViewEvent = rawRumEvents[rawRumEvents.length - 1].rawRumEvent as RawRumViewEvent - - expect(rawRumViewEvent._dd.page_states).toBeUndefined() - expect(rawRumViewEvent.view.in_foreground_periods).toBeDefined() - }) - it('should not include scroll metrics when there are not scroll metrics in the raw event', () => { const { lifeCycle, rawRumEvents } = setupBuilder.build() lifeCycle.notify(LifeCycleEventType.VIEW_UPDATED, { ...VIEW, scrollMetrics: undefined }) diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts index 3ef63f2456..2b330ac0ff 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/viewCollection.ts @@ -1,18 +1,10 @@ import type { Duration, ServerDuration, Observable } from '@datadog/browser-core' -import { - isExperimentalFeatureEnabled, - ExperimentalFeature, - isEmptyObject, - mapValues, - toServerDuration, - isNumber, -} from '@datadog/browser-core' +import { isEmptyObject, mapValues, toServerDuration, isNumber } from '@datadog/browser-core' import type { RecorderApi } from '../../../boot/rumPublicApi' import type { RawRumViewEvent } from '../../../rawRumEvent.types' import { RumEventType } from '../../../rawRumEvent.types' import type { LifeCycle, RawRumEventCollectedData } from '../../lifeCycle' import { LifeCycleEventType } from '../../lifeCycle' -import { mapToForegroundPeriods } from '../../contexts/foregroundContexts' import type { LocationChange } from '../../../browser/locationChangeObservable' import type { RumConfiguration } from '../../configuration' import type { FeatureFlagContexts } from '../../contexts/featureFlagContext' @@ -59,13 +51,12 @@ function processViewUpdate( ): RawRumEventCollectedData { const replayStats = recorderApi.getReplayStats(view.id) const featureFlagContext = featureFlagContexts.findFeatureFlagEvaluations(view.startClocks.relative) - const pageStatesEnabled = isExperimentalFeatureEnabled(ExperimentalFeature.PAGE_STATES) const pageStates = pageStateHistory.findAll(view.startClocks.relative, view.duration) const viewEvent: RawRumViewEvent = { _dd: { document_version: view.documentVersion, replay_stats: replayStats, - page_states: pageStatesEnabled ? pageStates : undefined, + page_states: pageStates, }, date: view.startClocks.timeStamp, type: RumEventType.VIEW, @@ -100,8 +91,6 @@ function processViewUpdate( count: view.eventCounts.resourceCount, }, time_spent: toServerDuration(view.duration), - in_foreground_periods: - !pageStatesEnabled && pageStates ? mapToForegroundPeriods(pageStates, view.duration) : undefined, // Todo: Remove in the next major release }, feature_flags: featureFlagContext && !isEmptyObject(featureFlagContext) ? featureFlagContext : undefined, display: view.scrollMetrics diff --git a/packages/rum-core/src/rawRumEvent.types.ts b/packages/rum-core/src/rawRumEvent.types.ts index 1064893791..aa1f61c814 100644 --- a/packages/rum-core/src/rawRumEvent.types.ts +++ b/packages/rum-core/src/rawRumEvent.types.ts @@ -101,7 +101,6 @@ export interface RawRumViewEvent { long_task: Count resource: Count frustration: Count - in_foreground_periods?: InForegroundPeriod[] } session: { has_replay: true | undefined @@ -128,11 +127,6 @@ interface ViewDisplay { } } -export interface InForegroundPeriod { - start: ServerDuration - duration: ServerDuration -} - export type PageStateServerEntry = { state: PageState; start: ServerDuration } export const enum ViewLoadingType { diff --git a/rum-events-format b/rum-events-format index 427239c247..1c5eaa897c 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit 427239c2479c13535aacff4f01d2b4085dd4c0d5 +Subproject commit 1c5eaa897c065e5f790a5f8aaf6fc8782d706051