Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract logic from TestRunner #6390

Merged
merged 19 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
}
}