Skip to content

Commit

Permalink
Extract logic from TestRunner (#6390)
Browse files Browse the repository at this point in the history
## Summary

This PR extracts from TestRunner logic that isn't directly related to
running tests.

## Test plan
  • Loading branch information
Latropos committed Aug 26, 2024
1 parent 37f20f3 commit 591690e
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const testRunner = new TestRunner();
const windowDimensionsMocker = testRunner.getWindowDimensionsMocker();
const animationRecorder = testRunner.getAnimationUpdatesRecorder();
const valueRegistry = testRunner.getValueRegistry();
const callTrackerRegistry = testRunner.getCallTrackerRegistry();
const notificationRegistry = testRunner.getNotificationRegistry();

type DescribeFunction = (name: string, buildSuite: BuildFunction) => void;
type TestFunction = (name: string, buildTest: BuildFunction) => void;
Expand Down Expand Up @@ -85,7 +87,7 @@ export function useTestRef(name: string) {
}

// eslint-disable-next-line @typescript-eslint/unbound-method
const testRunnerCallTrackerFn = testRunner.callTracker;
const testRunnerCallTrackerFn = callTrackerRegistry.callTracker;
export function callTracker(name: string) {
'worklet';
return testRunnerCallTrackerFn(name);
Expand All @@ -100,7 +102,7 @@ export function callTrackerFn(name: string) {
}

export function getTrackerCallCount(name: string) {
return testRunner.getTrackerCallCount(name);
return callTrackerRegistry.getTrackerCallCount(name);
}

export function registerValue(name: string, value: SharedValue) {
Expand All @@ -120,22 +122,22 @@ export async function runTests() {
}

export async function wait(delay: number) {
return testRunner.wait(delay);
await animationRecorder.wait(delay);
}

export async function waitForAnimationUpdates(updatesCount: number) {
return testRunner.waitForAnimationUpdates(updatesCount);
await animationRecorder.waitForAnimationUpdates(updatesCount);
}

// eslint-disable-next-line @typescript-eslint/unbound-method
const testRunnerNotifyFn = testRunner.notify;
const testRunnerNotifyFn = notificationRegistry.notify;
export function notify(name: string) {
'worklet';
return testRunnerNotifyFn(name);
}

export async function waitForNotify(name: string) {
return testRunner.waitForNotify(name);
return notificationRegistry.waitForNotify(name);
}

export function expect(value: TestValue) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { makeMutable } from 'react-native-reanimated';
import type { Operation } from '../types';
import { SyncUIRunner } from '../utils/SyncUIRunner';
import { createUpdatesContainer } from './UpdatesContainer';
import { assertMockedAnimationTimestamp } from './Asserts';

export class AnimationUpdatesRecorder {
private _syncUIRunner: SyncUIRunner = new SyncUIRunner();
Expand Down Expand Up @@ -109,4 +111,29 @@ export class AnimationUpdatesRecorder {
}
});
}

public wait(delay: number) {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}

public waitForAnimationUpdates(updatesCount: number): Promise<boolean> {
const CHECK_INTERVAL = 20;
const flag = makeMutable(false);
return new Promise<boolean>(resolve => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const interval = setInterval(async () => {
await new SyncUIRunner().runOnUIBlocking(() => {
'worklet';
assertMockedAnimationTimestamp(global.framesCount);
flag.value = global.framesCount >= updatesCount - 1;
});
if (flag.value) {
clearInterval(interval);
resolve(true);
}
}, CHECK_INTERVAL);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { makeMutable } from 'react-native-reanimated';
import type { TrackerCallCount } from '../types';

let callCallTrackerRegistryJS: Record<string, number> = {};
const callCallTrackerRegistryUI = makeMutable<Record<string, number>>({});
function callTrackerJS(name: string) {
if (!callCallTrackerRegistryJS[name]) {
callCallTrackerRegistryJS[name] = 0;
}
callCallTrackerRegistryJS[name]++;
}

export class CallTrackerRegistry {
public callTracker(name: string) {
'worklet';
if (_WORKLET) {
if (!callCallTrackerRegistryUI.value[name]) {
callCallTrackerRegistryUI.value[name] = 0;
}
callCallTrackerRegistryUI.value[name]++;
callCallTrackerRegistryUI.value = { ...callCallTrackerRegistryUI.value };
} else {
callTrackerJS(name);
}
}

public getTrackerCallCount(name: string): TrackerCallCount {
return {
name,
onJS: callCallTrackerRegistryJS[name] ?? 0,
onUI: callCallTrackerRegistryUI.value[name] ?? 0,
};
}

public resetRegistry() {
callCallTrackerRegistryUI.value = {};
callCallTrackerRegistryJS = {};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { runOnJS } from 'react-native-reanimated';

const notificationRegistry: Record<string, boolean> = {};
function notifyJS(name: string) {
notificationRegistry[name] = true;
}

export class NotificationRegistry {
public notify(name: string) {
'worklet';
if (_WORKLET) {
runOnJS(notifyJS)(name);
} else {
notifyJS(name);
}
}

public async waitForNotify(name: string) {
return new Promise(resolve => {
const interval = setInterval(() => {
if (notificationRegistry[name]) {
clearInterval(interval);
resolve(true);
}
}, 10);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
import type { Component, MutableRefObject, ReactElement } from 'react';
import { useRef } from 'react';
import type { BuildFunction, TestCase, TestConfiguration, TestSuite, TestValue, TrackerCallCount } from '../types';
import type { BuildFunction, TestCase, TestConfiguration, TestSuite, TestValue } from '../types';
import { DescribeDecorator, TestDecorator } from '../types';
import { TestComponent } from '../TestComponent';
import { applyMarkdown, formatTestName } from '../utils/stringFormatUtils';
import { Matchers } from '../matchers/Matchers';
import { assertMockedAnimationTimestamp, assertTestCase, assertTestSuite } from './Asserts';
import { makeMutable, runOnJS } from 'react-native-reanimated';
import { RenderLock, SyncUIRunner } from '../utils/SyncUIRunner';
import { assertTestCase, assertTestSuite } from './Asserts';
import { RenderLock } from '../utils/SyncUIRunner';
import { ValueRegistry } from './ValueRegistry';
import { TestSummaryLogger } from './TestSummaryLogger';
import { WindowDimensionsMocker } from './WindowDimensionsMocker';
import { AnimationUpdatesRecorder } from './AnimationUpdatesRecorder';
import { CallTrackerRegistry } from './CallTrackerRegistry';
import { NotificationRegistry } from './NotificationRegistry';
export { Presets } from '../Presets';

let callTrackerRegistryJS: Record<string, number> = {};
const callTrackerRegistryUI = makeMutable<Record<string, number>>({});
function callTrackerJS(name: string) {
if (!callTrackerRegistryJS[name]) {
callTrackerRegistryJS[name] = 0;
}
callTrackerRegistryJS[name]++;
}

const notificationRegistry: Record<string, boolean> = {};
function notifyJS(name: string) {
notificationRegistry[name] = true;
}

export class TestRunner {
private _testSuites: TestSuite[] = [];
private _currentTestSuite: TestSuite | null = null;
Expand All @@ -39,6 +26,8 @@ export class TestRunner {
private _windowDimensionsMocker: WindowDimensionsMocker = new WindowDimensionsMocker();
private _animationRecorder = new AnimationUpdatesRecorder();
private _valueRegistry = new ValueRegistry();
private _callTrackerRegistry = new CallTrackerRegistry();
private _notificationRegistry = new NotificationRegistry();

public getWindowDimensionsMocker() {
return this._windowDimensionsMocker;
Expand All @@ -52,24 +41,12 @@ export class TestRunner {
return this._valueRegistry;
}

public notify(name: string) {
'worklet';
if (_WORKLET) {
runOnJS(notifyJS)(name);
} else {
notifyJS(name);
}
public getCallTrackerRegistry() {
return this._callTrackerRegistry;
}

public async waitForNotify(name: string) {
return new Promise(resolve => {
const interval = setInterval(() => {
if (notificationRegistry[name]) {
clearInterval(interval);
resolve(true);
}
}, 10);
});
public getNotificationRegistry() {
return this._notificationRegistry;
}

public configure(config: TestConfiguration) {
Expand Down Expand Up @@ -164,27 +141,6 @@ export class TestRunner {
return ref;
}

public callTracker(name: string) {
'worklet';
if (_WORKLET) {
if (!callTrackerRegistryUI.value[name]) {
callTrackerRegistryUI.value[name] = 0;
}
callTrackerRegistryUI.value[name]++;
callTrackerRegistryUI.value = { ...callTrackerRegistryUI.value };
} else {
callTrackerJS(name);
}
}

public getTrackerCallCount(name: string): TrackerCallCount {
return {
name,
onJS: callTrackerRegistryJS[name] ?? 0,
onUI: callTrackerRegistryUI.value[name] ?? 0,
};
}

public getTestComponent(name: string): TestComponent {
assertTestCase(this._currentTestCase);
const componentRef = this._currentTestCase.componentsRefs[name];
Expand Down Expand Up @@ -252,8 +208,7 @@ export class TestRunner {
}

private async runTestCase(testSuite: TestSuite, testCase: TestCase) {
callTrackerRegistryUI.value = {};
callTrackerRegistryJS = {};
this._callTrackerRegistry.resetRegistry();
this._currentTestCase = testCase;

if (testSuite.beforeEach) {
Expand Down Expand Up @@ -297,29 +252,4 @@ export class TestRunner {
assertTestSuite(this._currentTestSuite);
this._currentTestSuite.afterEach = job;
}

public wait(delay: number) {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}

public waitForAnimationUpdates(updatesCount: number): Promise<boolean> {
const CHECK_INTERVAL = 20;
const flag = makeMutable(false);
return new Promise<boolean>(resolve => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const interval = setInterval(async () => {
await new SyncUIRunner().runOnUIBlocking(() => {
'worklet';
assertMockedAnimationTimestamp(global.framesCount);
flag.value = global.framesCount >= updatesCount - 1;
});
if (flag.value) {
clearInterval(interval);
resolve(true);
}
}, CHECK_INTERVAL);
});
}
}

0 comments on commit 591690e

Please sign in to comment.