Skip to content

Commit

Permalink
Add more tests - useSharedValue, useAnimatedStyle , useDerivedValue (#…
Browse files Browse the repository at this point in the history
…5981)

## Summary
**The new test runs 35 seconds ⏰**

In this PR I add two tests of our core functionalities:
* useSharedValue
* useAnimatedStyle - test reusing animated style to animate several
components simultaneusly
* useDerivedValue - test some basic snapshots
* useDerivedValue - test chaining 10 derived values

I also
* Mark Presets as not suitable for spell-check
* Modify color function to JSON.stringify objects
* Add additional types `null | undefined` to TestValue type so, after
some more checks we will be able to remove function `expectToBeNullable`
* Create function `getComparisonModeForProp`

<img width="542" alt="image"
src="https://github.com/software-mansion/react-native-reanimated/assets/56199675/6de4c38e-f1c9-4815-9dbc-59b435dbb26e">

## Recording



https://github.com/software-mansion/react-native-reanimated/assets/56199675/dcd11c06-b9a5-454c-be30-e56999d84497
  • Loading branch information
Latropos committed May 15, 2024
1 parent 6e08def commit 44d55dd
Show file tree
Hide file tree
Showing 16 changed files with 994 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComparisonMode, TestValue } from './types';
import { ComparisonMode, TestValue, ValidPropNames } from './types';

const DISTANCE_TOLERANCE = 0.5;

Expand Down Expand Up @@ -54,6 +54,13 @@ const COMPARATORS: {
},

[ComparisonMode.OBJECT]: (expected, value) => {
if (expected === null || value === null) {
return expected === null && value === null;
}
if (expected === undefined || value === undefined) {
return expected === undefined && value === undefined;
}

if (typeof expected !== 'object' || typeof value !== 'object') {
return false;
}
Expand Down Expand Up @@ -90,3 +97,16 @@ const COMPARATORS: {
export function getComparator(mode: ComparisonMode) {
return COMPARATORS[mode];
}

export function getComparisonModeForProp(prop: ValidPropNames): ComparisonMode {
const propToComparisonModeDict = {
zIndex: ComparisonMode.NUMBER,
opacity: ComparisonMode.NUMBER,
width: ComparisonMode.DISTANCE,
height: ComparisonMode.DISTANCE,
top: ComparisonMode.DISTANCE,
left: ComparisonMode.DISTANCE,
backgroundColor: ComparisonMode.COLOR,
};
return propToComparisonModeDict[prop];
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class Matchers {
constructor(private _currentValue: TestValue, private _testCase: TestCase) {}

private static _assertValueIsCallTracker(value: TrackerCallCount | TestValue): asserts value is TrackerCallCount {
if (typeof value !== 'object' || !('name' in value && 'onJS' in value && 'onUI' in value)) {
if (typeof value !== 'object' || !(value !== null && 'name' in value && 'onJS' in value && 'onUI' in value)) {
throw Error('Invalid value');
}
}
Expand Down Expand Up @@ -145,13 +145,15 @@ export class Matchers {
public toMatchNativeSnapshots(nativeSnapshots: Array<OperationUpdate>, expectNegativeMismatch = false) {
let errorString = '';
const jsUpdates = this._currentValue as Array<OperationUpdate>;
for (let i = 0; i < jsUpdates.length; i++) {
errorString += this.compareJsAndNativeSnapshot(jsUpdates, nativeSnapshots, i, expectNegativeMismatch);
}

if (jsUpdates.length !== nativeSnapshots.length - 1) {
errorString += `Expected ${jsUpdates.length} snapshots, but received ${nativeSnapshots.length - 1} snapshots\n`;
} else {
for (let i = 0; i < jsUpdates.length; i++) {
errorString += this.compareJsAndNativeSnapshot(jsUpdates, nativeSnapshots, i, expectNegativeMismatch);
}
}

if (errorString !== '') {
this._testCase.errors.push('Native snapshot mismatch: \n' + errorString);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// #region numbers
const INTEGERS = [
-1000000000, -1234567, -1000000, -1000, -10, -5, -3, -2, -1, -0, 0, +0, 1, 2, 3, 5, 10, 1000, 1000000, -1234567,
1000000000,
Expand All @@ -21,7 +22,18 @@ const BINARY_NOTATION = [0b1111111, 0b101010, 0b0001, 0b11001, 0b111111111111111

const OCTADECIMAL_NOTATION = [0o123456, 0o111111112];

const BIG_INTS = [BigInt(123456789123456789), BigInt(Number.MAX_VALUE)];
const BIG_INTS = [
BigInt(123456789123456789),
BigInt(Number.MAX_VALUE),
BigInt('0'),
BigInt('1'),
BigInt(-1),
BigInt(-123456789),
BigInt('9007199254740991'),
BigInt('0x1fffffffffffff'),
BigInt('0o377777777777777777'),
BigInt('0b11111111111111111111111111111111111111111111111111111'),
];

const UNDERSCORE_SEPARATED_NUMBES = [
0b00_000_000_000_000_000_000_000_000_000_001, -1.07808734473_36499e-3_9, 1_0_00000000.1234567,
Expand All @@ -36,13 +48,15 @@ const EXTREME_NUMBERS = [
Number.EPSILON,
];

const TYPICAL_STRINGS = [
'Aaaaaaa\n \t\t \v aaaaaa',
'Super long'.repeat(10000000),
const NOT_NUMBERS = [Infinity, -Infinity, NaN];
// #endregion

// #region strings
const TYPICAL_STRINGS = ['Aaaaaaa\n \t\t \v aaaaaa', 'Super long'.repeat(10000000), '', 'A string primitive'];

const STRING_OBJECTS = [
// eslint-disable-next-line no-new-wrappers
new String('A String object'),
'',
'A string primitive',
];

const EMOJI_STRINGS = ['Emoji consisting of multiple sub-emojis 👨‍👨‍👧‍👦', '👨‍👨‍👧‍👦', '😎', '👩🏽‍🏫'];
Expand Down Expand Up @@ -72,30 +86,9 @@ const MISC_CHARACTERS = [
'🅷🅴🅻🅻🅾 🆆🅾🆁🅳',
'🅗🅔🅛🅛🅞 🅦🅞🅡🅓',
];
const NOT_NUMBERS = [Infinity, -Infinity, NaN];

const EMPTIES = [[], null, undefined, {}, [[]], [{}], [null]];

const FANCY_OBJECTS = [globalThis];

const SYMBOLS = [Symbol('Hello!'), Symbol(123), Symbol()];

const DATES = [
new Date('2012-05-24'),
Intl.DateTimeFormat(navigator.language),
new Date('December 17, 1995 03:24:00'),
new Date(),
new Date(1999, 11),
new Date(1999, 11, 24),
new Date(1999, 11, 17),
new Date(1999, 11, 17, 33),
new Date(1999, 11, 17, 33, 54),
new Date(1999, 11, 17, 33, 54, 12),
new Date(1999, 120, 17, 33, 54, 12),
];

const REGEXPS = [/ab+c/i, new RegExp('ab+c', 'i'), new RegExp(/ab+c/, 'i'), /\d/y];
// #endregion

// #region bufferArrays
const iterable = (function* () {
yield* [1, 2, 3];
})();
Expand Down Expand Up @@ -142,14 +135,44 @@ const FLOAT_ARRAYS = [
new Float64Array(iterable),
];

const BUFFER_ARRAYS = [new ArrayBuffer(8), new ArrayBuffer(0)];
// #endregion

const EMPTIES = [[], null, undefined, {}, [[]], [{}], [null]];

const SYMBOLS = [Symbol('Hello!'), Symbol(123), Symbol()];

const DATES = [
new Date('2012-05-24'),
Intl.DateTimeFormat(navigator.language),
new Date('December 17, 1995 03:24:00'),
new Date(),
new Date(1999, 11),
new Date(1999, 11, 24),
new Date(1999, 11, 17),
new Date(1999, 11, 17, 33),
new Date(1999, 11, 17, 33, 54),
new Date(1999, 11, 17, 33, 54, 12),
new Date(1999, 120, 17, 33, 54, 12),
];

const REGEXPS = [/ab+c/i, new RegExp('ab+c', 'i'), new RegExp(/ab+c/, 'i'), /\d/y];

// const MAX_SIZE_OF_ARRAY = Math.pow(2, 31) - 1;
const MAX_SIZE_OF_ARRAY = 1000;

const ARRAYS = [
const NUMERICAL_ARRAYS = [
Array.from(Array(MAX_SIZE_OF_ARRAY).keys()),
[[[[[[[[[[[[[[[[[[1], 2], 3], 4], 5], 6], 7], 8], 9], [[[[], 10]]]]]]]]]]]],

new ArrayBuffer(8),
new ArrayBuffer(0),
];
const VARIOUS_TYPE_ARRAYS = [
['a', 123],
[1, 2, 3, ['A', 'B', 'C', 'D']],
[1, 2, 3, [{ A: 'A' }, 'B', 'C', 'D']],
[1, 2, 3, [{ A: ['A'] }, 'B', 'C', 'D']],
[null, 2, 3, ['A', [[[null]]], 'B', 'C', 'D']],
[null, undefined, [undefined, [[[null]]], undefined], [[], []]],
[null, undefined, [{}, [[[null]]], undefined], [[], []]],
];

const OBJECTS = [
Expand Down Expand Up @@ -181,19 +204,22 @@ export const Presets = {
...HEXADECIMAL_NOTATION,
...BINARY_NOTATION,
...OCTADECIMAL_NOTATION,
...BIG_INTS,
...UNDERSCORE_SEPARATED_NUMBES,
],
bigInts: BIG_INTS,
strings: [
...TYPICAL_STRINGS,
...EMOJI_STRINGS,
...COMMON_CHARS_AND_LIGATURES,
...NON_DEFAULT_ALPHABETS,
...MISC_CHARACTERS,
],
stringObjects: [...STRING_OBJECTS],
symbols: SYMBOLS,
regexps: REGEXPS,
dates: DATES,
objects: [...MAPS, ...SETS, ...OBJECTS, ...EMPTIES, ...FANCY_OBJECTS],
arrays: [...INT_ARRAYS, ...UINT_ARRAYS, ...FLOAT_ARRAYS, ...ARRAYS],
serializableObjects: [...OBJECTS, ...EMPTIES, ...REGEXPS],
unserializableObjects: [...MAPS, ...SETS],
serializableArrays: [...NUMERICAL_ARRAYS, ...VARIOUS_TYPE_ARRAYS],
arrays: [...INT_ARRAYS, ...UINT_ARRAYS, ...FLOAT_ARRAYS, ...NUMERICAL_ARRAYS, ...BUFFER_ARRAYS],
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const describe: {
testRunner.describe(name, buildSuite, DescribeDecorator.SKIP);
},
only: (name: string, buildSuite: () => void) => {
testRunner.describe(name, buildSuite, DescribeDecorator.SKIP);
testRunner.describe(name, buildSuite, DescribeDecorator.ONLY);
},
},
);
Expand Down Expand Up @@ -65,13 +65,13 @@ export const test: {
},
{
each: <T>(examples: Array<T>) => {
return testRunner.testEach(examples, null);
return testRunner.testEach(examples, TestDecorator.ONLY);
},
},
),
failing: Object.assign(
(name: string, warningMessage: string, testCase: () => void) => {
testRunner.test(name, testCase, TestDecorator.FAILING, warningMessage);
(name: string, expectedWarning: string, testCase: () => void) => {
testRunner.test(name, testCase, TestDecorator.FAILING, expectedWarning);
},
{
each: <T>(examples: Array<T>) => {
Expand All @@ -81,7 +81,7 @@ export const test: {
),
warn: Object.assign(
(name: string, expectedWarning: string, testCase: () => void) => {
testRunner.test(name, testCase, TestDecorator.WARN);
testRunner.test(name, testCase, TestDecorator.WARN, expectedWarning);
},
{
each: <T>(examples: Array<T>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Component } from 'react';
import { findNodeHandle } from 'react-native';
import { getViewProp } from 'react-native-reanimated';
import { ComponentRef } from './types';

export type ValidPropNames = 'zIndex' | 'opacity' | 'width' | 'height' | 'top' | 'left' | 'backgroundColor';
import { ComponentRef, ValidPropNames } from './types';

export class TestComponent {
constructor(private ref: ComponentRef) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export class TestRunner {
errors: [],
decorator,
warningMessage: warningMessage,
skip: false,
}
: {
name: applyMarkdown(name),
Expand All @@ -154,6 +155,7 @@ export class TestRunner {
callsRegistry: {},
errors: [],
decorator,
skip: decorator === TestDecorator.SKIP,
},
);
}
Expand Down Expand Up @@ -217,7 +219,7 @@ export class TestRunner {
await this.runOnUIBlocking(() => {
'worklet';
valueContainer.value = sharedValue.value;
});
}, 1000);
const uiValue = valueContainer.value;
return {
name,
Expand Down Expand Up @@ -407,26 +409,34 @@ export class TestRunner {
this._currentTestSuite.afterEach = job;
}

private waitForPropertyValueChange(targetObject: LockObject, targetProperty: 'lock', initialValue = true) {
private waitForPropertyValueChange(
targetObject: LockObject,
targetProperty: 'lock',
initialValue = true,
maxWaitTime?: number,
) {
return new Promise(resolve => {
const startTime = performance.now();
const interval = setInterval(() => {
if (targetObject[targetProperty] !== initialValue) {
const currentTime = performance.now();
const waitTimeExceeded = maxWaitTime && maxWaitTime < currentTime - startTime;
if (targetObject[targetProperty] !== initialValue || waitTimeExceeded) {
clearInterval(interval);
resolve(targetObject[targetProperty]);
}
}, 10);
});
}

public async runOnUIBlocking(worklet: () => void) {
public async runOnUIBlocking(worklet: () => void, maxWaitTime?: number) {
const unlock = () => (this._threadLock.lock = false);
this._threadLock.lock = true;
runOnUI(() => {
'worklet';
worklet();
runOnJS(unlock)();
})();
await this.waitForPropertyValueChange(this._threadLock, 'lock', true);
await this.waitForPropertyValueChange(this._threadLock, 'lock', true, maxWaitTime);
}

public async recordAnimationUpdates() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export function color(value: NullableTestValue, color: 'yellow' | 'cyan' | 'gree
orange: '\x1b[38;5;208m',
reset: '\x1b[0m',
};

return `${COLOR_CODES[color]}${value}${COLOR_CODES.reset}`;
const stringValue = typeof value === 'object' ? JSON.stringify(value) : value?.toString();
return `${COLOR_CODES[color]}${stringValue}${COLOR_CODES.reset}`;
}

export function applyMarkdown(template: string) {
Expand All @@ -51,12 +51,16 @@ export function applyMarkdown(template: string) {

export function formatString(template: string, variableObject: unknown, index: number) {
const valueToString: (arg0: unknown) => string = (value: unknown) => {
if (value instanceof Error) {
return `**${value.name}** "${value.message}"`;
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
if (typeof value === 'function') {
return value.name;
}

return value?.toString() || '';
};
let testName = template;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export type TestSuite = {
decorator?: DescribeDecorator | null;
};

export type ValidPropNames = 'zIndex' | 'opacity' | 'width' | 'height' | 'top' | 'left' | 'backgroundColor';

export enum ComparisonMode {
STRING = 'STRING',
DISTANCE = 'DISTANCE',
Expand All @@ -87,7 +89,15 @@ export interface Operation {
updates: OperationUpdate;
}

export type TestValue = TrackerCallCount | string | Array<unknown> | number | bigint | Record<string, unknown>;
export type TestValue =
| TrackerCallCount
| string
| Array<unknown>
| number
| bigint
| Record<string, unknown>
| null
| undefined;
export type NullableTestValue = TestValue | null | undefined;

export type TestConfiguration = {
Expand Down
5 changes: 5 additions & 0 deletions app/src/examples/RuntimeTests/RuntimeTestsExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import './tests/layoutAnimations/entering/enteringColors.test';

import './tests/utilities/relativeCoords.test';

import './tests/core/useSharedValue.test';
import './tests/core/useAnimatedStyle/reuseAnimatedStyle.test';
import './tests/core/useDerivedValue/basic.test';
import './tests/core/useDerivedValue/chain.test';

export default function RuntimeTestsExample() {
return <RuntimeTestsRunner />;
}
Loading

0 comments on commit 44d55dd

Please sign in to comment.