From aa05615c270c97403dfcba4f1e48be395e821cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 17:55:24 +0000 Subject: [PATCH 1/6] feat: add support for setup/cleanup functions during each test run --- README.md | 12 ++++++ docusaurus/docs/api.md | 14 +++++- .../src/__tests__/measure-function.test.tsx | 43 +++++++++++++++++++ .../src/__tests__/measure-renders.test.tsx | 23 ++++++++++ .../measure/src/measure-async-function.tsx | 4 ++ packages/measure/src/measure-function.tsx | 10 ++++- packages/measure/src/measure-renders.tsx | 6 +++ packages/reassure/README.md | 12 ++++++ 8 files changed, 121 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d1e73b5b..da2c8780 100644 --- a/README.md +++ b/README.md @@ -374,6 +374,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -383,6 +385,8 @@ interface MeasureRendersOptions { - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results; only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the UI by utilising RNTL or RTL functions - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. #### `measureFunction` function @@ -403,6 +407,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -410,6 +416,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. #### `measureAsyncFunction` function @@ -432,6 +440,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -439,6 +449,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. ### Configuration diff --git a/docusaurus/docs/api.md b/docusaurus/docs/api.md index 4ab0e3e1..c9b54142 100644 --- a/docusaurus/docs/api.md +++ b/docusaurus/docs/api.md @@ -54,6 +54,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -62,7 +64,9 @@ interface MeasureRendersOptions { - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results, only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the ui by utilized RNTL functions -- **`writeFile`**: should write output to file (default `true`) +- **`writeFile`**: (default `true`) should write output to file. +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. ### `measureFunction` function {#measure-function} @@ -95,6 +99,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -102,6 +108,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. ### `measureAsyncFunction` function {#measure-async-function} @@ -142,6 +150,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -149,6 +159,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: (default `true`) should write output to file +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. ## Configuration diff --git a/packages/measure/src/__tests__/measure-function.test.tsx b/packages/measure/src/__tests__/measure-function.test.tsx index 7f9d7cdd..e4d3892c 100644 --- a/packages/measure/src/__tests__/measure-function.test.tsx +++ b/packages/measure/src/__tests__/measure-function.test.tsx @@ -57,6 +57,49 @@ test('measureFunction applies "warmupRuns" option', async () => { expect(results.stdevCount).toBe(0); }); +test('measureFunction executes setup and cleanup functions for each run', async () => { + const fn = jest.fn(() => fib(5)); + const beforeFn = jest.fn(); + const afterFn = jest.fn(); + const results = await measureFunction(fn, { + runs: 10, + warmupRuns: 1, + writeFile: false, + beforeEachRun: beforeFn, + afterEachRun: afterFn, + }); + + expect(beforeFn).toHaveBeenCalledTimes(11); + expect(fn).toHaveBeenCalledTimes(11); + expect(afterFn).toHaveBeenCalledTimes(11); + expect(results.runs).toBe(10); + expect(results.durations).toHaveLength(10); + expect(results.counts).toHaveLength(10); +}); + +test('measureAsyncFunction executes setup and cleanup functions for each run', async () => { + const fn = jest.fn(async () => { + await Promise.resolve(); + return fib(5); + }); + const beforeFn = jest.fn(); + const afterFn = jest.fn(); + const results = await measureAsyncFunction(fn, { + runs: 10, + warmupRuns: 1, + writeFile: false, + beforeEachRun: beforeFn, + afterEachRun: afterFn, + }); + + expect(beforeFn).toHaveBeenCalledTimes(11); + expect(fn).toHaveBeenCalledTimes(11); + expect(afterFn).toHaveBeenCalledTimes(11); + expect(results.runs).toBe(10); + expect(results.durations).toHaveLength(10); + expect(results.counts).toHaveLength(10); +}); + const errorsToIgnore = ['❌ Measure code is running under incorrect Node.js configuration.']; const realConsole = jest.requireActual('console') as Console; diff --git a/packages/measure/src/__tests__/measure-renders.test.tsx b/packages/measure/src/__tests__/measure-renders.test.tsx index 0917279c..c751521d 100644 --- a/packages/measure/src/__tests__/measure-renders.test.tsx +++ b/packages/measure/src/__tests__/measure-renders.test.tsx @@ -38,6 +38,29 @@ test('measureRenders applies "warmupRuns" option', async () => { expect(results.stdevCount).toBe(0); }); +test('measureRenders executes setup and cleanup functions for each run', async () => { + const scenario = jest.fn(() => Promise.resolve(null)); + const beforeFn = jest.fn(); + const afterFn = jest.fn(); + const results = await measureRenders(, { + runs: 10, + warmupRuns: 1, + scenario, + writeFile: false, + beforeEachRun: beforeFn, + afterEachRun: afterFn, + }); + + expect(beforeFn).toHaveBeenCalledTimes(11); + expect(scenario).toHaveBeenCalledTimes(11); + expect(afterFn).toHaveBeenCalledTimes(11); + expect(results.runs).toBe(10); + expect(results.durations).toHaveLength(10); + expect(results.counts).toHaveLength(10); + expect(results.meanCount).toBe(1); + expect(results.stdevCount).toBe(0); +}); + test('measureRenders should log error when running under incorrect node flags', async () => { jest.spyOn(realConsole, 'error').mockImplementation((message) => { if (!errorsToIgnore.some((error) => message.includes(error))) { diff --git a/packages/measure/src/measure-async-function.tsx b/packages/measure/src/measure-async-function.tsx index 3e27dffc..b2282287 100644 --- a/packages/measure/src/measure-async-function.tsx +++ b/packages/measure/src/measure-async-function.tsx @@ -31,10 +31,14 @@ async function measureAsyncFunctionInternal( const runResults: RunResult[] = []; for (let i = 0; i < runs + warmupRuns; i += 1) { + await options?.beforeEachRun?.(); + const timeStart = getCurrentTime(); await fn(); const timeEnd = getCurrentTime(); + await options?.afterEachRun?.(); + const duration = timeEnd - timeStart; runResults.push({ duration, count: 1 }); } diff --git a/packages/measure/src/measure-function.tsx b/packages/measure/src/measure-function.tsx index a9f075b1..e7f5a928 100644 --- a/packages/measure/src/measure-function.tsx +++ b/packages/measure/src/measure-function.tsx @@ -8,10 +8,12 @@ export interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } export async function measureFunction(fn: () => void, options?: MeasureFunctionOptions): Promise { - const stats = measureFunctionInternal(fn, options); + const stats = await measureFunctionInternal(fn, options); if (options?.writeFile !== false) { await writeTestStats(stats, 'function'); @@ -20,7 +22,7 @@ export async function measureFunction(fn: () => void, options?: MeasureFunctionO return stats; } -function measureFunctionInternal(fn: () => void, options?: MeasureFunctionOptions): MeasureResults { +async function measureFunctionInternal(fn: () => void, options?: MeasureFunctionOptions): Promise { const runs = options?.runs ?? config.runs; const warmupRuns = options?.warmupRuns ?? config.warmupRuns; const removeOutliers = options?.removeOutliers ?? config.removeOutliers; @@ -29,10 +31,14 @@ function measureFunctionInternal(fn: () => void, options?: MeasureFunctionOption const runResults: RunResult[] = []; for (let i = 0; i < runs + warmupRuns; i += 1) { + await options?.beforeEachRun?.(); + const timeStart = getCurrentTime(); fn(); const timeEnd = getCurrentTime(); + await options?.afterEachRun?.(); + const duration = timeEnd - timeStart; runResults.push({ duration, count: 1 }); } diff --git a/packages/measure/src/measure-renders.tsx b/packages/measure/src/measure-renders.tsx index 521a6496..8d1bde2c 100644 --- a/packages/measure/src/measure-renders.tsx +++ b/packages/measure/src/measure-renders.tsx @@ -20,6 +20,8 @@ export interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: React.ReactElement }>; scenario?: (screen: any) => Promise; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } export async function measureRenders( @@ -69,6 +71,8 @@ async function measureRendersInternal( let initialRenderCount = 0; for (let iteration = 0; iteration < runs + warmupRuns; iteration += 1) { + await options?.beforeEachRun?.(); + let duration = 0; let count = 0; let renderResult: any = null; @@ -108,6 +112,8 @@ async function measureRendersInternal( cleanup(); global.gc?.(); + await options?.afterEachRun?.(); + runResults.push({ duration, count }); } diff --git a/packages/reassure/README.md b/packages/reassure/README.md index d1e73b5b..da2c8780 100644 --- a/packages/reassure/README.md +++ b/packages/reassure/README.md @@ -374,6 +374,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -383,6 +385,8 @@ interface MeasureRendersOptions { - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results; only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the UI by utilising RNTL or RTL functions - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. #### `measureFunction` function @@ -403,6 +407,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -410,6 +416,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. #### `measureAsyncFunction` function @@ -432,6 +440,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; + beforeEachRun?: () => Promise | void; + afterEachRun?: () => Promise | void; } ``` @@ -439,6 +449,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) +- **`beforeEachRun`**: function to execute before each test run. +- **`afterEachRun`**: function to execute after each test run. ### Configuration From 72912d13065442f88877823a5e21345bfa7c0796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 17:58:55 +0000 Subject: [PATCH 2/6] chore: simplify measureRenders test --- packages/measure/src/__tests__/measure-renders.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/measure/src/__tests__/measure-renders.test.tsx b/packages/measure/src/__tests__/measure-renders.test.tsx index c751521d..bd0e3dae 100644 --- a/packages/measure/src/__tests__/measure-renders.test.tsx +++ b/packages/measure/src/__tests__/measure-renders.test.tsx @@ -57,8 +57,6 @@ test('measureRenders executes setup and cleanup functions for each run', async ( expect(results.runs).toBe(10); expect(results.durations).toHaveLength(10); expect(results.counts).toHaveLength(10); - expect(results.meanCount).toBe(1); - expect(results.stdevCount).toBe(0); }); test('measureRenders should log error when running under incorrect node flags', async () => { From 4e467e35aec86d4e0a369eb7b5c3e22964093ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 18:04:04 +0000 Subject: [PATCH 3/6] Add changeset --- .changeset/silent-cups-fail.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/silent-cups-fail.md diff --git a/.changeset/silent-cups-fail.md b/.changeset/silent-cups-fail.md new file mode 100644 index 00000000..7d338704 --- /dev/null +++ b/.changeset/silent-cups-fail.md @@ -0,0 +1,6 @@ +--- +'reassure': minor +'@callstack/reassure-measure': minor +--- + +feat: add support for setup/cleanup functions during each test run From 86579bd4c13f2b84a0c0d41475660bc69f6dffcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 18:25:54 +0000 Subject: [PATCH 4/6] chore: rename options --- README.md | 24 +++++++++---------- docusaurus/docs/api.md | 24 +++++++++---------- .../src/__tests__/measure-function.test.tsx | 8 +++---- .../src/__tests__/measure-renders.test.tsx | 4 ++-- .../measure/src/measure-async-function.tsx | 4 ++-- packages/measure/src/measure-function.tsx | 8 +++---- packages/measure/src/measure-renders.tsx | 8 +++---- packages/reassure/README.md | 24 +++++++++---------- 8 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index da2c8780..645976d1 100644 --- a/README.md +++ b/README.md @@ -374,8 +374,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -385,8 +385,8 @@ interface MeasureRendersOptions { - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results; only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the UI by utilising RNTL or RTL functions - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. #### `measureFunction` function @@ -407,8 +407,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -416,8 +416,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. #### `measureAsyncFunction` function @@ -440,8 +440,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -449,8 +449,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. ### Configuration diff --git a/docusaurus/docs/api.md b/docusaurus/docs/api.md index c9b54142..9d881344 100644 --- a/docusaurus/docs/api.md +++ b/docusaurus/docs/api.md @@ -54,8 +54,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -65,8 +65,8 @@ interface MeasureRendersOptions { - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results, only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the ui by utilized RNTL functions - **`writeFile`**: (default `true`) should write output to file. -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. ### `measureFunction` function {#measure-function} @@ -99,8 +99,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -108,8 +108,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. ### `measureAsyncFunction` function {#measure-async-function} @@ -150,8 +150,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -159,8 +159,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: (default `true`) should write output to file -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. ## Configuration diff --git a/packages/measure/src/__tests__/measure-function.test.tsx b/packages/measure/src/__tests__/measure-function.test.tsx index e4d3892c..dbea849a 100644 --- a/packages/measure/src/__tests__/measure-function.test.tsx +++ b/packages/measure/src/__tests__/measure-function.test.tsx @@ -65,8 +65,8 @@ test('measureFunction executes setup and cleanup functions for each run', async runs: 10, warmupRuns: 1, writeFile: false, - beforeEachRun: beforeFn, - afterEachRun: afterFn, + beforeEach: beforeFn, + afterEach: afterFn, }); expect(beforeFn).toHaveBeenCalledTimes(11); @@ -88,8 +88,8 @@ test('measureAsyncFunction executes setup and cleanup functions for each run', a runs: 10, warmupRuns: 1, writeFile: false, - beforeEachRun: beforeFn, - afterEachRun: afterFn, + beforeEach: beforeFn, + afterEach: afterFn, }); expect(beforeFn).toHaveBeenCalledTimes(11); diff --git a/packages/measure/src/__tests__/measure-renders.test.tsx b/packages/measure/src/__tests__/measure-renders.test.tsx index bd0e3dae..ae94732f 100644 --- a/packages/measure/src/__tests__/measure-renders.test.tsx +++ b/packages/measure/src/__tests__/measure-renders.test.tsx @@ -47,8 +47,8 @@ test('measureRenders executes setup and cleanup functions for each run', async ( warmupRuns: 1, scenario, writeFile: false, - beforeEachRun: beforeFn, - afterEachRun: afterFn, + beforeEach: beforeFn, + afterEach: afterFn, }); expect(beforeFn).toHaveBeenCalledTimes(11); diff --git a/packages/measure/src/measure-async-function.tsx b/packages/measure/src/measure-async-function.tsx index b2282287..4afb9fa9 100644 --- a/packages/measure/src/measure-async-function.tsx +++ b/packages/measure/src/measure-async-function.tsx @@ -31,13 +31,13 @@ async function measureAsyncFunctionInternal( const runResults: RunResult[] = []; for (let i = 0; i < runs + warmupRuns; i += 1) { - await options?.beforeEachRun?.(); + await options?.beforeEach?.(); const timeStart = getCurrentTime(); await fn(); const timeEnd = getCurrentTime(); - await options?.afterEachRun?.(); + await options?.afterEach?.(); const duration = timeEnd - timeStart; runResults.push({ duration, count: 1 }); diff --git a/packages/measure/src/measure-function.tsx b/packages/measure/src/measure-function.tsx index e7f5a928..65523cfc 100644 --- a/packages/measure/src/measure-function.tsx +++ b/packages/measure/src/measure-function.tsx @@ -8,8 +8,8 @@ export interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } export async function measureFunction(fn: () => void, options?: MeasureFunctionOptions): Promise { @@ -31,13 +31,13 @@ async function measureFunctionInternal(fn: () => void, options?: MeasureFunction const runResults: RunResult[] = []; for (let i = 0; i < runs + warmupRuns; i += 1) { - await options?.beforeEachRun?.(); + await options?.beforeEach?.(); const timeStart = getCurrentTime(); fn(); const timeEnd = getCurrentTime(); - await options?.afterEachRun?.(); + await options?.afterEach?.(); const duration = timeEnd - timeStart; runResults.push({ duration, count: 1 }); diff --git a/packages/measure/src/measure-renders.tsx b/packages/measure/src/measure-renders.tsx index 8d1bde2c..6e746d18 100644 --- a/packages/measure/src/measure-renders.tsx +++ b/packages/measure/src/measure-renders.tsx @@ -20,8 +20,8 @@ export interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: React.ReactElement }>; scenario?: (screen: any) => Promise; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } export async function measureRenders( @@ -71,7 +71,7 @@ async function measureRendersInternal( let initialRenderCount = 0; for (let iteration = 0; iteration < runs + warmupRuns; iteration += 1) { - await options?.beforeEachRun?.(); + await options?.beforeEach?.(); let duration = 0; let count = 0; @@ -112,7 +112,7 @@ async function measureRendersInternal( cleanup(); global.gc?.(); - await options?.afterEachRun?.(); + await options?.afterEach?.(); runResults.push({ duration, count }); } diff --git a/packages/reassure/README.md b/packages/reassure/README.md index da2c8780..645976d1 100644 --- a/packages/reassure/README.md +++ b/packages/reassure/README.md @@ -374,8 +374,8 @@ interface MeasureRendersOptions { wrapper?: React.ComponentType<{ children: ReactElement }>; scenario?: (view?: RenderResult) => Promise; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -385,8 +385,8 @@ interface MeasureRendersOptions { - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results; only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the UI by utilising RNTL or RTL functions - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. #### `measureFunction` function @@ -407,8 +407,8 @@ interface MeasureFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -416,8 +416,8 @@ interface MeasureFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. #### `measureAsyncFunction` function @@ -440,8 +440,8 @@ interface MeasureAsyncFunctionOptions { warmupRuns?: number; removeOutliers?: boolean; writeFile?: boolean; - beforeEachRun?: () => Promise | void; - afterEachRun?: () => Promise | void; + beforeEach?: () => Promise | void; + afterEach?: () => Promise | void; } ``` @@ -449,8 +449,8 @@ interface MeasureAsyncFunctionOptions { - **`warmupRuns`**: number of additional warmup runs that will be done and discarded before the actual runs - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`writeFile`**: should write output to file (default `true`) -- **`beforeEachRun`**: function to execute before each test run. -- **`afterEachRun`**: function to execute after each test run. +- **`beforeEach`**: function to execute before each test run. +- **`afterEach`**: function to execute after each test run. ### Configuration From 8518d8a54a8e689fdc6c07785e100aab20277634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 18:34:51 +0000 Subject: [PATCH 5/6] chore: fix rebase --- docusaurus/docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus/docs/api.md b/docusaurus/docs/api.md index 9d881344..dc7f6dad 100644 --- a/docusaurus/docs/api.md +++ b/docusaurus/docs/api.md @@ -64,7 +64,7 @@ interface MeasureRendersOptions { - **`removeOutliers`**: should remove statistical outlier results (default: `true`) - **`wrapper`**: React component, such as a `Provider`, which the `ui` will be wrapped with. Note: the render duration of the `wrapper` itself is excluded from the results, only the wrapped component is measured. - **`scenario`**: a custom async function, which defines user interaction within the ui by utilized RNTL functions -- **`writeFile`**: (default `true`) should write output to file. +- **`writeFile`**: should write output to file (default `true`) - **`beforeEach`**: function to execute before each test run. - **`afterEach`**: function to execute after each test run. From 6e850193f1186567894a72e68c7762cda559c9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 11 Mar 2025 18:41:27 +0000 Subject: [PATCH 6/6] chore: fix tests --- .../__tests__/measure-async-function.test.tsx | 20 +++++++++++++++ .../src/__tests__/measure-function.test.tsx | 25 +------------------ .../src/__tests__/measure-renders.test.tsx | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/measure/src/__tests__/measure-async-function.test.tsx b/packages/measure/src/__tests__/measure-async-function.test.tsx index 26a77ed1..0c4722f0 100644 --- a/packages/measure/src/__tests__/measure-async-function.test.tsx +++ b/packages/measure/src/__tests__/measure-async-function.test.tsx @@ -45,6 +45,26 @@ test('measureAsyncFunction applies "warmupRuns" option', async () => { expect(results.stdevCount).toBe(0); }); +test('measureAsyncFunction executes setup and cleanup functions for each run', async () => { + const fn = jest.fn(() => Promise.resolve().then(() => fib(5))); + const beforeFn = jest.fn(); + const afterFn = jest.fn(); + const results = await measureAsyncFunction(fn, { + runs: 10, + warmupRuns: 1, + writeFile: false, + beforeEach: beforeFn, + afterEach: afterFn, + }); + + expect(beforeFn).toHaveBeenCalledTimes(11); + expect(fn).toHaveBeenCalledTimes(11); + expect(afterFn).toHaveBeenCalledTimes(11); + expect(results.runs).toBe(10); + expect(results.durations.length + (results.outlierDurations?.length ?? 0)).toBe(10); + expect(results.counts).toHaveLength(10); +}); + const errorsToIgnore = ['❌ Measure code is running under incorrect Node.js configuration.']; const realConsole = jest.requireActual('console') as Console; diff --git a/packages/measure/src/__tests__/measure-function.test.tsx b/packages/measure/src/__tests__/measure-function.test.tsx index dbea849a..0caaecbf 100644 --- a/packages/measure/src/__tests__/measure-function.test.tsx +++ b/packages/measure/src/__tests__/measure-function.test.tsx @@ -73,30 +73,7 @@ test('measureFunction executes setup and cleanup functions for each run', async expect(fn).toHaveBeenCalledTimes(11); expect(afterFn).toHaveBeenCalledTimes(11); expect(results.runs).toBe(10); - expect(results.durations).toHaveLength(10); - expect(results.counts).toHaveLength(10); -}); - -test('measureAsyncFunction executes setup and cleanup functions for each run', async () => { - const fn = jest.fn(async () => { - await Promise.resolve(); - return fib(5); - }); - const beforeFn = jest.fn(); - const afterFn = jest.fn(); - const results = await measureAsyncFunction(fn, { - runs: 10, - warmupRuns: 1, - writeFile: false, - beforeEach: beforeFn, - afterEach: afterFn, - }); - - expect(beforeFn).toHaveBeenCalledTimes(11); - expect(fn).toHaveBeenCalledTimes(11); - expect(afterFn).toHaveBeenCalledTimes(11); - expect(results.runs).toBe(10); - expect(results.durations).toHaveLength(10); + expect(results.durations.length + (results.outlierDurations?.length ?? 0)).toBe(10); expect(results.counts).toHaveLength(10); }); diff --git a/packages/measure/src/__tests__/measure-renders.test.tsx b/packages/measure/src/__tests__/measure-renders.test.tsx index ae94732f..1a75bef4 100644 --- a/packages/measure/src/__tests__/measure-renders.test.tsx +++ b/packages/measure/src/__tests__/measure-renders.test.tsx @@ -55,7 +55,7 @@ test('measureRenders executes setup and cleanup functions for each run', async ( expect(scenario).toHaveBeenCalledTimes(11); expect(afterFn).toHaveBeenCalledTimes(11); expect(results.runs).toBe(10); - expect(results.durations).toHaveLength(10); + expect(results.durations.length + (results.outlierDurations?.length ?? 0)).toBe(10); expect(results.counts).toHaveLength(10); });