From a3933e687828eeda5b2faac1f24962c95adf4a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 7 Jul 2025 06:42:23 -0700 Subject: [PATCH] Remove legacy tests for Performance API (#52465) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52465 Changelog: [internal] Now that we have enough coverage for the Performance API in Fantom, Jest tests where most of the API is mocked are redundant and useless, so this removes them (and the mock). Reviewed By: huntie Differential Revision: D77860888 fbshipit-source-id: 7dbd1a8a43b056a3b34e4e37d578be9ccb521824 --- .../performance/__tests__/Performance-test.js | 292 ------------------ .../__tests__/PerformanceObserver-test.js | 58 ---- .../specs/__mocks__/NativePerformanceMock.js | 269 ---------------- 3 files changed, 619 deletions(-) delete mode 100644 packages/react-native/src/private/webapis/performance/__tests__/Performance-test.js delete mode 100644 packages/react-native/src/private/webapis/performance/__tests__/PerformanceObserver-test.js delete mode 100644 packages/react-native/src/private/webapis/performance/specs/__mocks__/NativePerformanceMock.js diff --git a/packages/react-native/src/private/webapis/performance/__tests__/Performance-test.js b/packages/react-native/src/private/webapis/performance/__tests__/Performance-test.js deleted file mode 100644 index 2b7a3108952..00000000000 --- a/packages/react-native/src/private/webapis/performance/__tests__/Performance-test.js +++ /dev/null @@ -1,292 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -// eslint-disable-next-line @react-native/monorepo/sort-imports -import type Performance from '../Performance'; - -import {performanceEntryTypeToRaw} from '../internals/RawPerformanceEntry'; -import {reportEntry} from '../specs/__mocks__/NativePerformanceMock'; - -jest.mock('../specs/NativePerformance', () => - require('../specs/__mocks__/NativePerformanceMock'), -); - -declare var performance: Performance; - -const NativePerformanceMock = - require('../specs/__mocks__/NativePerformanceMock').default; - -describe('Performance', () => { - beforeEach(() => { - jest.resetModules(); - - const PerformanceClass = require('../Performance').default; - // $FlowExpectedError[cannot-write] - global.performance = new PerformanceClass(); - }); - - it('reports marks and measures', () => { - NativePerformanceMock.setCurrentTime(25); - - performance.mark('mark-now'); - performance.mark('mark-in-the-past', { - startTime: 10, - }); - performance.mark('mark-in-the-future', { - startTime: 50, - }); - performance.measure('measure-with-specific-time', { - start: 30, - duration: 4, - }); - performance.measure('measure-now-with-start-mark', 'mark-in-the-past'); - performance.measure( - 'measure-with-start-and-end-mark', - 'mark-in-the-past', - 'mark-in-the-future', - ); - - const entries = performance.getEntries(); - expect(entries.length).toBe(6); - expect(entries.map(entry => entry.toJSON())).toEqual([ - { - duration: 0, - entryType: 'mark', - name: 'mark-in-the-past', - startTime: 10, - }, - { - duration: 15, - entryType: 'measure', - name: 'measure-now-with-start-mark', - startTime: 10, - }, - { - duration: 40, - entryType: 'measure', - name: 'measure-with-start-and-end-mark', - startTime: 10, - }, - { - duration: 0, - entryType: 'mark', - name: 'mark-now', - startTime: 25, - }, - { - duration: 4, - entryType: 'measure', - name: 'measure-with-specific-time', - startTime: 30, - }, - { - duration: 0, - entryType: 'mark', - name: 'mark-in-the-future', - startTime: 50, - }, - ]); - }); - - it('clearMarks and clearMeasures remove correct entry types', async () => { - performance.mark('entry1', {startTime: 0}); - performance.mark('mark2', {startTime: 0}); - - performance.measure('entry1', {start: 0, duration: 0}); - performance.measure('measure2', {start: 0, duration: 0}); - - performance.clearMarks(); - - expect(performance.getEntries().map(e => e.name)).toStrictEqual([ - 'entry1', - 'measure2', - ]); - - performance.mark('entry2', {startTime: 0}); - performance.mark('mark3', {startTime: 0}); - - performance.clearMeasures(); - - expect(performance.getEntries().map(e => e.name)).toStrictEqual([ - 'entry2', - 'mark3', - ]); - - performance.clearMarks(); - - expect(performance.getEntries().map(e => e.name)).toStrictEqual([]); - }); - - it('getEntries only works with allowed entry types', async () => { - performance.clearMarks(); - performance.clearMeasures(); - - performance.mark('entry1', {startTime: 0}); - performance.mark('mark2', {startTime: 0}); - - jest.spyOn(console, 'warn').mockImplementation(() => {}); - - performance.getEntriesByType('mark'); - expect(console.warn).not.toHaveBeenCalled(); - - performance.getEntriesByType('measure'); - expect(console.warn).not.toHaveBeenCalled(); - - performance.getEntriesByName('entry1'); - expect(console.warn).not.toHaveBeenCalled(); - - performance.getEntriesByName('entry1', 'event'); - expect(console.warn).toHaveBeenCalled(); - - performance.getEntriesByName('entry1', 'mark'); - expect(console.warn).toHaveBeenCalled(); - - performance.getEntriesByType('event'); - expect(console.warn).toHaveBeenCalled(); - }); - - it('getEntries works with marks and measures', async () => { - performance.clearMarks(); - performance.clearMeasures(); - - performance.mark('entry1', {startTime: 0}); - performance.mark('mark2', {startTime: 0}); - - performance.measure('entry1', {start: 0, duration: 0}); - performance.measure('measure2', {start: 0, duration: 0}); - - expect(performance.getEntries().map(e => e.name)).toStrictEqual([ - 'entry1', - 'mark2', - 'entry1', - 'measure2', - ]); - - expect(performance.getEntriesByType('mark').map(e => e.name)).toStrictEqual( - ['entry1', 'mark2'], - ); - - expect( - performance.getEntriesByType('measure').map(e => e.name), - ).toStrictEqual(['entry1', 'measure2']); - - expect( - performance.getEntriesByName('entry1').map(e => e.entryType), - ).toStrictEqual(['mark', 'measure']); - - expect( - performance.getEntriesByName('entry1', 'measure').map(e => e.entryType), - ).toStrictEqual(['measure']); - }); - - it('defines EventCounts for Performance', () => { - expect(performance.eventCounts).not.toBeUndefined(); - }); - - it('consistently implements the API for EventCounts', async () => { - let interactionId = 0; - const eventDefaultValues = { - entryType: performanceEntryTypeToRaw('event'), - startTime: 0, // startTime - duration: 100, // duration - processingStart: 0, // processing start - processingEnd: 100, // processingEnd - }; - - reportEntry({ - name: 'click', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'input', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'input', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - - const eventCounts = performance.eventCounts; - expect(eventCounts.size).toBe(3); - expect(Array.from(eventCounts.entries())).toStrictEqual([ - ['click', 1], - ['input', 2], - ['keyup', 3], - ]); - - expect(eventCounts.get('click')).toEqual(1); - expect(eventCounts.get('input')).toEqual(2); - expect(eventCounts.get('keyup')).toEqual(3); - - expect(eventCounts.has('click')).toEqual(true); - expect(eventCounts.has('input')).toEqual(true); - expect(eventCounts.has('keyup')).toEqual(true); - - expect(Array.from(eventCounts.keys())).toStrictEqual([ - 'click', - 'input', - 'keyup', - ]); - expect(Array.from(eventCounts.values())).toStrictEqual([1, 2, 3]); - - await jest.runAllTicks(); - reportEntry({ - name: 'input', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - expect(Array.from(eventCounts.values())).toStrictEqual([1, 3, 5]); - - await jest.runAllTicks(); - reportEntry({ - name: 'click', - ...eventDefaultValues, - interactionId: interactionId++, - }); - - await jest.runAllTicks(); - - reportEntry({ - name: 'keyup', - ...eventDefaultValues, - interactionId: interactionId++, - }); - - expect(Array.from(eventCounts.values())).toStrictEqual([2, 3, 6]); - }); -}); diff --git a/packages/react-native/src/private/webapis/performance/__tests__/PerformanceObserver-test.js b/packages/react-native/src/private/webapis/performance/__tests__/PerformanceObserver-test.js deleted file mode 100644 index 9e96798378c..00000000000 --- a/packages/react-native/src/private/webapis/performance/__tests__/PerformanceObserver-test.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -import type Performance from '../Performance'; -import type {PerformanceEntryList} from '../PerformanceEntry'; - -jest.mock( - '../specs/NativePerformance', - () => require('../specs/__mocks__/NativePerformanceMock').default, -); - -declare var performance: Performance; - -describe('PerformanceObserver', () => { - let PerformanceObserver; - - beforeEach(() => { - jest.resetModules(); - - // $FlowExpectedError[cannot-write] - global.performance = new (require('../Performance').default)(); - PerformanceObserver = require('../PerformanceObserver').PerformanceObserver; - }); - - it('prevents durationThreshold to be used together with entryTypes', async () => { - const observer = new PerformanceObserver((list, _observer) => {}); - - expect(() => - observer.observe({entryTypes: ['event', 'mark'], durationThreshold: 100}), - ).toThrow(); - }); - - it('ignores durationThreshold when used with marks or measures', async () => { - let entries: PerformanceEntryList = []; - - const observer = new PerformanceObserver((list, _observer) => { - entries = [...entries, ...list.getEntries()]; - }); - - observer.observe({type: 'measure', durationThreshold: 100}); - - performance.measure('measure1', { - start: 0, - duration: 10, - }); - - await jest.runAllTicks(); - expect(entries).toHaveLength(1); - expect(entries.map(e => e.name)).toStrictEqual(['measure1']); - }); -}); diff --git a/packages/react-native/src/private/webapis/performance/specs/__mocks__/NativePerformanceMock.js b/packages/react-native/src/private/webapis/performance/specs/__mocks__/NativePerformanceMock.js deleted file mode 100644 index 518ca5e61e9..00000000000 --- a/packages/react-native/src/private/webapis/performance/specs/__mocks__/NativePerformanceMock.js +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -import type { - NativeBatchedObserverCallback, - NativeMemoryInfo, - NativePerformanceMarkResult, - NativePerformanceMeasureResult, - OpaqueNativeObserverHandle, - PerformanceObserverInit, - RawPerformanceEntry, - RawPerformanceEntryType, - ReactNativeStartupTiming, -} from '../NativePerformance'; -import typeof NativePerformance from '../NativePerformance'; - -import {RawPerformanceEntryTypeValues} from '../../internals/RawPerformanceEntry'; - -type MockObserver = { - handleEntry: (entry: RawPerformanceEntry) => void, - callback: NativeBatchedObserverCallback, - didScheduleFlushBuffer: boolean, - entries: Array, - options: PerformanceObserverInit, - droppedEntriesCount: number, -}; - -const eventCounts: Map = new Map(); -const observers: Set = new Set(); -const marks: Map = new Map(); -let entries: Array = []; - -function getMockObserver( - opaqueNativeObserverHandle: OpaqueNativeObserverHandle, -): MockObserver { - return opaqueNativeObserverHandle as $FlowFixMe as MockObserver; -} - -function createMockObserver(callback: NativeBatchedObserverCallback) { - const observer: MockObserver = { - callback, - didScheduleFlushBuffer: false, - entries: [], - options: {}, - droppedEntriesCount: 0, - handleEntry: (entry: RawPerformanceEntry) => { - if ( - observer.options.type !== entry.entryType && - !observer.options.entryTypes?.includes(entry.entryType) - ) { - return; - } - - if ( - entry.entryType === RawPerformanceEntryTypeValues.EVENT && - entry.duration < (observer.options?.durationThreshold ?? 0) - ) { - return; - } - - observer.entries.push(entry); - - if (!observer.didScheduleFlushBuffer) { - observer.didScheduleFlushBuffer = true; - // $FlowFixMe[incompatible-call] - global.queueMicrotask(() => { - observer.didScheduleFlushBuffer = false; - // We want to emulate the way it's done in native (i.e. async/batched) - observer.callback(); - }); - } - }, - }; - - return observer; -} - -export function reportEntry(entry: RawPerformanceEntry) { - entries.push(entry); - - switch (entry.entryType) { - case RawPerformanceEntryTypeValues.MARK: - marks.set(entry.name, entry.startTime); - break; - case RawPerformanceEntryTypeValues.MEASURE: - break; - case RawPerformanceEntryTypeValues.EVENT: - eventCounts.set(entry.name, (eventCounts.get(entry.name) ?? 0) + 1); - break; - } - - for (const observer of observers) { - observer.handleEntry(entry); - } -} - -let currentTime: number = 12; - -const NativePerformanceMock = { - setCurrentTime: (time: number): void => { - currentTime = time; - }, - - now: (): number => currentTime, - - markWithResult: ( - name: string, - startTime?: number, - ): NativePerformanceMarkResult => { - const computedStartTime = startTime ?? performance.now(); - - marks.set(name, computedStartTime); - reportEntry({ - entryType: RawPerformanceEntryTypeValues.MARK, - name, - startTime: computedStartTime, - duration: 0, - }); - - return computedStartTime; - }, - - measure: ( - name: string, - startTime?: number, - endTime?: number, - duration?: number, - startMark?: string, - endMark?: string, - ): NativePerformanceMeasureResult => { - const start = startMark != null ? marks.get(startMark) : startTime ?? 0; - const end = - endMark != null ? marks.get(endMark) : endTime ?? performance.now(); - - if (start === undefined) { - throw new Error('startMark does not exist'); - } - - if (end === undefined) { - throw new Error('endMark does not exist'); - } - - const computedDuration = duration ?? end - start; - reportEntry({ - entryType: RawPerformanceEntryTypeValues.MEASURE, - name, - startTime: start, - duration: computedDuration, - }); - - return [start, computedDuration]; - }, - - getSimpleMemoryInfo: (): NativeMemoryInfo => { - return {}; - }, - - getReactNativeStartupTiming: (): ReactNativeStartupTiming => { - return { - startTime: 0, - endTime: 0, - executeJavaScriptBundleEntryPointStart: 0, - executeJavaScriptBundleEntryPointEnd: 0, - initializeRuntimeStart: 0, - initializeRuntimeEnd: 0, - }; - }, - - getEventCounts: (): $ReadOnlyArray<[string, number]> => { - return Array.from(eventCounts.entries()); - }, - - createObserver: ( - callback: NativeBatchedObserverCallback, - ): OpaqueNativeObserverHandle => { - // $FlowExpectedError[incompatible-return] - return createMockObserver(callback); - }, - - getDroppedEntriesCount: (observer: OpaqueNativeObserverHandle): number => { - return getMockObserver(observer).droppedEntriesCount; - }, - - observe: ( - observer: OpaqueNativeObserverHandle, - options: PerformanceObserverInit, - ): void => { - const mockObserver = getMockObserver(observer); - mockObserver.options = options; - observers.add(mockObserver); - }, - - disconnect: (observer: OpaqueNativeObserverHandle): void => { - const mockObserver = getMockObserver(observer); - observers.delete(mockObserver); - }, - - takeRecords: ( - observer: OpaqueNativeObserverHandle, - ): $ReadOnlyArray => { - const mockObserver = getMockObserver(observer); - const observerEntries = mockObserver.entries; - mockObserver.entries = []; - return observerEntries.sort((a, b) => a.startTime - b.startTime); - }, - - clearMarks: (entryName?: string) => { - if (entryName != null) { - marks.delete(entryName); - } else { - marks.clear(); - } - - entries = entries.filter( - entry => - entry.entryType !== RawPerformanceEntryTypeValues.MARK || - (entryName != null && entry.name !== entryName), - ); - }, - - clearMeasures: (entryName?: string) => { - entries = entries.filter( - entry => - entry.entryType !== RawPerformanceEntryTypeValues.MEASURE || - (entryName != null && entry.name !== entryName), - ); - }, - - getEntries: (): $ReadOnlyArray => { - return [...entries].sort((a, b) => a.startTime - b.startTime); - }, - - getEntriesByName: ( - entryName: string, - entryType?: ?RawPerformanceEntryType, - ): $ReadOnlyArray => { - return NativePerformanceMock.getEntries().filter( - entry => - (entryType == null || entry.entryType === entryType) && - entry.name === entryName, - ); - }, - - getEntriesByType: ( - entryType: RawPerformanceEntryType, - ): $ReadOnlyArray => { - return entries.filter(entry => entry.entryType === entryType); - }, - - getSupportedPerformanceEntryTypes: - (): $ReadOnlyArray => { - return [ - RawPerformanceEntryTypeValues.MARK, - RawPerformanceEntryTypeValues.MEASURE, - RawPerformanceEntryTypeValues.EVENT, - ]; - }, -}; - -(NativePerformanceMock: NativePerformance); - -export default NativePerformanceMock;