From 8a792b694427c40bcc1f7059903a3f5b010435b1 Mon Sep 17 00:00:00 2001 From: Alex Cynk Date: Thu, 9 May 2024 11:55:39 +0200 Subject: [PATCH] Add `warning` and `failing` test decorators (#5929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary After this PR we are going to support the following API: Describe: - `describe("Name of the test suite", ()=>{})` - `describe.skip("Name of the test suite", ()=>{})` - Skip test suite - `describe.only("Name of the test suite", ()=>{})` - Run only test cases and test suite(s) with **only** decorator Test: - **default** - `test("Name of the test suite", ()=>{})` - `test.each([1,2,3])("Name of the test suite", ()=>{})` - **skip** ⏭️ - skip test case(s) - `test.skip("Name of the test suite", ()=>{})` - `test.skip.each([1,2,3])("Name of the test suite", ()=>{})` - **only** - run only test case(s) and suite(s) with **only** decorator - `test.only("Name of the test suite", ()=>{})` - `test.only.each([1,2,3])("Name of the test suite", ()=>{})` - **warn** ⚠️ - expect `console.warn` to be called - `test.warn("Name of the test suite", "Expected error message", ()=>{})` - `test.warn.each([1,2,3])("Name of the test suite", "Expected error message", ()=>{})` - **failing** - expect error to be thrown - `test.failing("Name of the test suite", "Expected error message", ()=>{})` - `test.failing.each([1,2,3])("Name of the test suite", "Expected error message", ()=>{})` ## Test plan --- .../RuntimeTestsApi.ts | 123 ++++++++++----- .../RuntimeTestsRunner.tsx | 24 ++- .../TestRunner.ts | 138 ++++++++++++----- .../stringFormatUtils.ts | 22 ++- .../ReanimatedRuntimeTestsRunner/types.ts | 33 +++- .../RuntimeTests/RuntimeTestsExample.tsx | 7 +- .../withSpring/variousConfig.test.tsx | 141 ++++++++++++++++++ .../withSpring/withSpring.snapshot.ts | 6 + .../animations/withTiming/easing.test.tsx | 35 ++++- cspell.json | 6 + 10 files changed, 446 insertions(+), 89 deletions(-) create mode 100644 app/src/examples/RuntimeTests/tests/animations/withSpring/variousConfig.test.tsx create mode 100644 app/src/examples/RuntimeTests/tests/animations/withSpring/withSpring.snapshot.ts diff --git a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsApi.ts b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsApi.ts index 980475c4d1f..6574e50bfed 100644 --- a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsApi.ts +++ b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsApi.ts @@ -2,23 +2,95 @@ import { Component, ReactElement } from 'react'; import { TestRunner } from './TestRunner'; import { TestComponent } from './TestComponent'; import type { SharedValue } from 'react-native-reanimated'; -import { TestConfiguration, TestValue, NullableTestValue } from './types'; +import { TestConfiguration, TestValue, NullableTestValue, DescribeDecorator, TestDecorator } from './types'; export { Presets } from './Presets'; const testRunner = new TestRunner(); -export const describe = (name: string, buildSuite: () => void) => { - testRunner.describe(name, buildSuite); -}; - -describe.skip = (name: string, buildSuite: () => void) => { - testRunner.describe(name, buildSuite, false, true); -}; - -describe.only = (name: string, buildSuite: () => void) => { - testRunner.describe(name, buildSuite, true); -}; +type DescribeFunction = (name: string, buildSuite: () => void) => void; +export const describe: { + (name: string, buildSuite: () => void): void; + skip: DescribeFunction; + only: DescribeFunction; +} = Object.assign( + (name: string, buildSuite: () => void) => { + testRunner.describe(name, buildSuite, null); + }, + { + skip: (name: string, buildSuite: () => void) => { + testRunner.describe(name, buildSuite, DescribeDecorator.SKIP); + }, + only: (name: string, buildSuite: () => void) => { + testRunner.describe(name, buildSuite, DescribeDecorator.SKIP); + }, + }, +); + +type TestEachFunction = ( + examples: Array, +) => (name: string, testCase: (example: T, index?: number) => void) => void; +type TestEachFunctionWithWarning = ( + examples: Array, +) => (name: string, expectedWarning: string, testCase: (example: T, index?: number) => void) => void; + +export const test: { + (name: string, testCase: () => void): void; + each: TestEachFunction; + skip: { (name: string, testCase: () => void): void; each: TestEachFunction }; + only: { (name: string, testCase: () => void): void; each: TestEachFunction }; + failing: { (name: string, warningMessage: string, testCase: () => void): void; each: TestEachFunctionWithWarning }; + warn: { (name: string, warningMessage: string, testCase: () => void): void; each: TestEachFunctionWithWarning }; +} = Object.assign( + (name: string, testCase: () => void) => { + testRunner.test(name, testCase, null); + }, + { + each: (examples: Array) => { + return testRunner.testEach(examples, null); + }, + skip: Object.assign( + (name: string, testCase: () => void) => { + testRunner.test(name, testCase, TestDecorator.SKIP); + }, + { + each: (examples: Array) => { + return testRunner.testEach(examples, TestDecorator.SKIP); + }, + }, + ), + only: Object.assign( + (name: string, testCase: () => void) => { + testRunner.test(name, testCase, TestDecorator.ONLY); + }, + { + each: (examples: Array) => { + return testRunner.testEach(examples, null); + }, + }, + ), + failing: Object.assign( + (name: string, warningMessage: string, testCase: () => void) => { + testRunner.test(name, testCase, TestDecorator.FAILING, warningMessage); + }, + { + each: (examples: Array) => { + return testRunner.testEachErrorMsg(examples, TestDecorator.FAILING); + }, + }, + ), + warn: Object.assign( + (name: string, expectedWarning: string, testCase: () => void) => { + testRunner.test(name, testCase, TestDecorator.WARN); + }, + { + each: (examples: Array) => { + return testRunner.testEachErrorMsg(examples, TestDecorator.WARN); + }, + }, + ), + }, +); export function beforeAll(job: () => void) { testRunner.beforeAll(job); @@ -36,33 +108,6 @@ export function afterAll(job: () => void) { testRunner.afterAll(job); } -export const test = (name: string, testCase: () => void) => { - testRunner.test(name, testCase); -}; - -test.each = (examples: Array) => { - return testRunner.testEach(examples); -}; - -const onlyDecorator = (name: string, testCase: () => void) => { - testRunner.test(name, testCase, true); -}; - -onlyDecorator.each = (examples: Array) => { - return testRunner.testEach(examples, true); -}; -test.only = onlyDecorator; - -const skipDecorator = (name: string, testCase: () => void) => { - testRunner.test(name, testCase, false, true); -}; - -skipDecorator.each = (examples: Array) => { - return testRunner.testEach(examples, false, true); -}; - -test.skip = skipDecorator; - export async function render(component: ReactElement | null) { return testRunner.render(component); } diff --git a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsRunner.tsx b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsRunner.tsx index 0bfcf91b537..2b77e5483f2 100644 --- a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsRunner.tsx +++ b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/RuntimeTestsRunner.tsx @@ -1,9 +1,31 @@ -import { View, Button, StyleSheet } from 'react-native'; +import { View, Button, StyleSheet, Text } from 'react-native'; import React, { ReactNode, useEffect, useState } from 'react'; import { runTests, configure } from './RuntimeTestsApi'; import { LockObject } from './types'; let renderLock: LockObject = { lock: false }; +export class ErrorBoundary extends React.Component< + { children: React.JSX.Element | Array }, + { hasError: boolean } +> { + constructor(props: { children: React.JSX.Element | Array }) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(_: unknown) { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + render() { + if (this.state.hasError) { + return Something went wrong.; + } + + return this.props.children; + } +} export default function RuntimeTestsRunner() { const [component, setComponent] = useState(null); diff --git a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/TestRunner.ts b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/TestRunner.ts index dc8fd7da7b2..19cc95fc635 100644 --- a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/TestRunner.ts +++ b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/TestRunner.ts @@ -1,20 +1,23 @@ import { Component, MutableRefObject, ReactElement, useRef } from 'react'; -import type { - NullableTestValue, - LockObject, - Operation, - SharedValueSnapshot, - TestCase, - TestConfiguration, - TestSuite, - TestSummary, - TestValue, - TrackerCallCount, +import { + type NullableTestValue, + type LockObject, + type Operation, + type SharedValueSnapshot, + type TestCase, + type TestConfiguration, + type TestSuite, + type TestSummary, + type TestValue, + type TrackerCallCount, + ComparisonMode, + DescribeDecorator, + TestDecorator, } from './types'; import { TestComponent } from './TestComponent'; -import { render, stopRecordingAnimationUpdates, unmockAnimationTimer } from './RuntimeTestsApi'; +import { getTrackerCallCount, render, stopRecordingAnimationUpdates, unmockAnimationTimer } from './RuntimeTestsApi'; import { makeMutable, runOnUI, runOnJS, SharedValue } from 'react-native-reanimated'; -import { color, formatString, indentNestingLevel } from './stringFormatUtils'; +import { applyMarkdown, color, formatString, indentNestingLevel } from './stringFormatUtils'; import { createUpdatesContainer } from './UpdatesContainer'; import { Matchers, nullableMatch } from './Matchers'; import { assertMockedAnimationTimestamp, assertTestCase, assertTestSuite } from './Asserts'; @@ -86,7 +89,11 @@ export class TestRunner { } this._wasRenderedNull = !component; this._renderLock.lock = true; - this._renderHook(component); + try { + this._renderHook(component); + } catch (e) { + console.log(e); + } return this.waitForPropertyValueChange(this._renderLock, 'lock'); } @@ -94,8 +101,8 @@ export class TestRunner { return await this.render(null); } - public describe(name: string, buildSuite: () => void, only = false, skip = false) { - if (only) { + public describe(name: string, buildSuite: () => void, decorator: DescribeDecorator | null) { + if (decorator === DescribeDecorator.ONLY) { this._includesOnly = true; } @@ -116,38 +123,63 @@ export class TestRunner { } this._testSuites.splice(index, 0, { - name, + name: applyMarkdown(name), buildSuite, testCases: [], nestingLevel: (this._currentTestSuite?.nestingLevel || 0) + 1, - only: !!(only || this._currentTestSuite?.only), - skip: !!(skip || this._currentTestSuite?.skip), + decorator: decorator ? decorator : this._currentTestSuite?.decorator ? this._currentTestSuite?.decorator : null, }); } - public test(name: string, run: () => void, only = false, skip = false) { + public test(name: string, run: () => void, decorator: TestDecorator | null, warningMessage = '') { assertTestSuite(this._currentTestSuite); - if (only) { + if (decorator === TestDecorator.ONLY) { this._includesOnly = true; } - this._currentTestSuite.testCases.push({ - name, - run, - componentsRefs: {}, - callsRegistry: {}, - errors: [], - only: only, - skip: skip, - }); + this._currentTestSuite.testCases.push( + decorator === TestDecorator.WARN || decorator === TestDecorator.FAILING + ? { + name: applyMarkdown(name), + run, + componentsRefs: {}, + callsRegistry: {}, + errors: [], + decorator, + warningMessage: warningMessage, + } + : { + name: applyMarkdown(name), + run, + componentsRefs: {}, + callsRegistry: {}, + errors: [], + decorator, + }, + ); } - public testEach(examples: Array, only = false, skip = false) { + public testEachErrorMsg(examples: Array, decorator: TestDecorator) { + return (name: string, expectedWarning: string, testCase: (example: T) => void) => { + examples.forEach((example, index) => { + const currentTestCase = async () => { + await testCase(example); + }; + this.test( + formatString(name, example, index), + currentTestCase, + decorator, + formatString(expectedWarning, example, index), + ); + }); + }; + } + public testEach(examples: Array, decorator: TestDecorator | null) { return (name: string, testCase: (example: T, index?: number) => void) => { examples.forEach((example, index) => { const currentTestCase = async () => { await testCase(example, index); }; - this.test(formatString(name, example, index), currentTestCase, only, skip); + this.test(formatString(name, example, index), currentTestCase, decorator); }); }; } @@ -221,16 +253,14 @@ export class TestRunner { let skipTestSuite = testSuite.skip; if (this._includesOnly) { - skipTestSuite = skipTestSuite || !testSuite.only; + skipTestSuite = skipTestSuite || !(testSuite.decorator === DescribeDecorator.ONLY); for (const testCase of testSuite.testCases) { - if (testCase.only) { + if (testCase.decorator === TestDecorator.ONLY) { skipTestSuite = false; - } else testCase.skip = testCase.skip || !testSuite.only; - delete testCase.only; + } else testCase.skip = testCase.skip || !(testSuite.decorator === DescribeDecorator.ONLY); } } - delete testSuite.only; testSuite.skip = skipTestSuite; } @@ -280,7 +310,39 @@ export class TestRunner { await testSuite.beforeEach(); } - await testCase.run(); + if (testCase.decorator === TestDecorator.FAILING || testCase.decorator === TestDecorator.WARN) { + const consoleTrackerRef = testCase.decorator === TestDecorator.FAILING ? 'console.error' : 'console.warn'; + const message = makeMutable(''); + + const newConsoleFuncJS = (warning: string) => { + this.callTracker(consoleTrackerRef); + message.value = warning.split('\n\nThis error is located at:')[0]; + }; + console.error = newConsoleFuncJS; + console.warn = newConsoleFuncJS; + + const callTrackerCopy = this.callTracker; + + runOnUI(() => { + 'worklet'; + const newConsoleFuncUI = (warning: string) => { + callTrackerCopy(consoleTrackerRef); + message.value = warning.split('\n\nThis error is located at:')[0]; + }; + console.error = newConsoleFuncUI; + console.warn = newConsoleFuncUI; + })(); + + await testCase.run(); + + this.expect(getTrackerCallCount(consoleTrackerRef)).toBeCalled(1); + if (testCase.warningMessage) { + this.expect(message.value).toBe(testCase.warningMessage, ComparisonMode.STRING); + } + } else { + await testCase.run(); + } + this.showTestCaseSummary(testCase, testSuite.nestingLevel); if (testSuite.afterEach) { diff --git a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/stringFormatUtils.ts b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/stringFormatUtils.ts index 2c847abaf12..d482228233a 100644 --- a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/stringFormatUtils.ts +++ b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/stringFormatUtils.ts @@ -24,9 +24,29 @@ export function color(value: NullableTestValue, color: 'yellow' | 'cyan' | 'gree cyan: '\x1b[36m', gray: '\x1b[38;5;242m', orange: '\x1b[38;5;208m', + reset: '\x1b[0m', }; - return `${COLOR_CODES[color]}${value}\x1b[0m`; + return `${COLOR_CODES[color]}${value}${COLOR_CODES.reset}`; +} + +export function applyMarkdown(template: string) { + const ANSI_CODES = { + bold: '\x1b[1m', + resetBold: '\x1b[22m', + italic: '\x1b[3m', + resetItalic: '\x1b[23m', + reverse: '\x1b[7m', + resetReverse: '\x1b[27m', + underline: '\x1b[4m', + resetUnderline: '\x1b[24m', + }; + template = template.replace(/\*{3}(.+?)\*{3}(?!\*)/g, `${ANSI_CODES.reverse} $1 ${ANSI_CODES.resetReverse}`); + template = template.replace(/\*{2}(.+?)\*{2}(?!\*)/g, `${ANSI_CODES.bold}$1${ANSI_CODES.resetBold}`); + template = template.replace(/\*{1}(.+?)\*{1}(?!\*)/g, `${ANSI_CODES.italic}$1${ANSI_CODES.resetItalic}`); + template = template.replace(/_(.+?)_(?!_)/g, `${ANSI_CODES.underline}$1${ANSI_CODES.resetUnderline}`); + + return template; } export function formatString(template: string, variableObject: unknown, index: number) { diff --git a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/types.ts b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/types.ts index dd4deaf5f10..75bb10297db 100644 --- a/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/types.ts +++ b/app/src/examples/RuntimeTests/ReanimatedRuntimeTestsRunner/types.ts @@ -1,7 +1,7 @@ import { Component, Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react'; import { AnimatedStyle, StyleProps } from 'react-native-reanimated'; -type CallTrucker = { +export type CallTracker = { UICallsCount: number; JSCallsCount: number; }; @@ -24,15 +24,34 @@ export type SharedValueSnapshot = { export type ComponentRef = MutableRefObject<(Component & { props: { style: Record } }) | null>; +export enum DescribeDecorator { + ONLY = 'ONLY', + SKIP = 'SKIP', + NONE = 'NONE', +} + +export enum TestDecorator { + ONLY = 'ONLY', + SKIP = 'SKIP', + FAILING = 'FAILING', + WARN = 'WARN', + NONE = 'NONE', +} + export type TestCase = { name: string; run: () => void | Promise; componentsRefs: Record; - callsRegistry: Record; + callsRegistry: Record; errors: string[]; - skip: boolean; - only?: boolean; -}; + skip?: boolean; +} & ( + | { + decorator: TestDecorator.WARN | TestDecorator.FAILING; + warningMessage: string; + } + | { decorator: Exclude | null } +); export type TestSuite = { name: string; @@ -43,8 +62,8 @@ export type TestSuite = { afterAll?: () => void | Promise; beforeEach?: () => void | Promise; afterEach?: () => void | Promise; - skip: boolean; - only?: boolean; + skip?: boolean; + decorator?: DescribeDecorator | null; }; export enum ComparisonMode { diff --git a/app/src/examples/RuntimeTests/RuntimeTestsExample.tsx b/app/src/examples/RuntimeTests/RuntimeTestsExample.tsx index 67ddd361790..a9e45544a1d 100644 --- a/app/src/examples/RuntimeTests/RuntimeTestsExample.tsx +++ b/app/src/examples/RuntimeTests/RuntimeTestsExample.tsx @@ -4,13 +4,16 @@ import RuntimeTestsRunner from './ReanimatedRuntimeTestsRunner/RuntimeTestsRunne // load tests import './tests/Animations.test'; -import './tests/animations/withTiming/easing.test'; +import './tests/animations/withTiming/arrays.test'; import './tests/animations/withTiming/basic.test'; import './tests/animations/withTiming/colors.test'; -import './tests/animations/withTiming/arrays.test'; +import './tests/animations/withTiming/easing.test'; import './tests/animations/withTiming/transformMatrices.test'; +import './tests/animations/withSpring/variousConfig.test'; + import './tests/layoutAnimations/entering/enteringColors.test'; + import './tests/utilities/relativeCoords.test'; export default function RuntimeTestsExample() { diff --git a/app/src/examples/RuntimeTests/tests/animations/withSpring/variousConfig.test.tsx b/app/src/examples/RuntimeTests/tests/animations/withSpring/variousConfig.test.tsx new file mode 100644 index 00000000000..0a583a27041 --- /dev/null +++ b/app/src/examples/RuntimeTests/tests/animations/withSpring/variousConfig.test.tsx @@ -0,0 +1,141 @@ +import { useEffect } from 'react'; +import { View, StyleSheet } from 'react-native'; +import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated'; +import React from 'react'; +import { + describe, + test, + expect, + mockAnimationTimer, + recordAnimationUpdates, + render, + wait, +} from '../../../ReanimatedRuntimeTestsRunner/RuntimeTestsApi'; +import { Snapshots } from './withSpring.snapshot'; +import { SpringConfig } from '../../../../../../../lib/typescript/reanimated2/animation/springUtils'; + +const AnimatedComponent = ({ + animateFrom, + animateTo, + config, +}: { + animateFrom: number; + animateTo: number; + config: SpringConfig; +}) => { + const widthSV = useSharedValue(animateFrom); + + const style = useAnimatedStyle(() => { + return { + width: withSpring(widthSV.value, config), + }; + }); + + useEffect(() => { + widthSV.value = animateTo; + }, [widthSV, animateTo]); + + return ( + + + + ); +}; + +async function getSnapshotUpdates(animateFrom: number, animateTo: number, config: SpringConfig, waitTime = 2000) { + await mockAnimationTimer(); + const updatesContainer = await recordAnimationUpdates(); + await render(); + await wait(waitTime); + + const updates = updatesContainer.getUpdates(); + const nativeUpdates = await updatesContainer.getNativeSnapshots(); + + return [updates, nativeUpdates]; +} + +describe('WithSpring snapshots 📸, test various configs', () => { + describe('Empty configuration', () => { + test.each([ + [20, 300], + [300, 50], + [0, 150], + [150, 0], + ] as Array<[number, number]>)( + 'Empty config, from ${0} to ${1}', + async ([animateFrom, animateTo]: [number, number]) => { + const [updates, nativeUpdates] = await getSnapshotUpdates(animateFrom, animateTo, {}); + const snapshotName = `empty_${animateFrom}_${animateTo}`; + expect(updates).toMatchSnapshots(Snapshots[snapshotName as keyof typeof Snapshots]); + expect(updates).toMatchNativeSnapshots(nativeUpdates, true); + }, + ); + }); + + describe('Invalid configuration, test warning', () => { + test.warn( + 'Invalid mass and stiffness, config is { mass: -40, stiffness: -400 }', + '[Reanimated] Invalid spring config, stiffness must be grater than zero but got -400, mass must be grater than zero but got -40', + async () => { + await render(); + }, + ); + + test.warn.each([ + { mass: 0, stiffness: 5000 }, + { mass: 0 }, + { mass: -10 }, + { mass: -5, duration: 5000 }, + { mass: -5, damping: 50 }, + { mass: -20, stiffness: 20 }, + ])( + '%# Invalid mass, config is %p', + '[Reanimated] Invalid spring config, mass must be grater than zero but got ${mass}', + async config => { + await render(); + }, + ); + + test.warn.each([ + { stiffness: -20 }, + { stiffness: 0 }, + { damping: 20, stiffness: -20 }, + { mass: 20, stiffness: -20 }, + { mass: 20, stiffness: 0 }, + ])( + '%# Invalid stiffness, config is %p', + '[Reanimated] Invalid spring config, stiffness must be grater than zero but got ${stiffness}', + async config => { + await render(); + }, + ); + + test.warn.each([{ damping: -20 }, { damping: 0 }])( + '%# Invalid damping, config is %p', + '[Reanimated] Invalid spring config, damping must be grater than zero but got ${damping}', + async config => { + await render(); + }, + ); + + test.warn.each([{ duration: -20 }])( + '%# Invalid duration, config is %p', + "[Reanimated] Invalid spring config, duration can't be negative, got ${duration}", + async config => { + await render(); + }, + ); + }); +}); + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + }, + animatedBox: { + height: 80, + + backgroundColor: 'darkorange', + }, +}); diff --git a/app/src/examples/RuntimeTests/tests/animations/withSpring/withSpring.snapshot.ts b/app/src/examples/RuntimeTests/tests/animations/withSpring/withSpring.snapshot.ts new file mode 100644 index 00000000000..cfcc6dc5e6b --- /dev/null +++ b/app/src/examples/RuntimeTests/tests/animations/withSpring/withSpring.snapshot.ts @@ -0,0 +1,6 @@ +export const Snapshots = { + empty_20_300: [{"width": 20}, {"width": 23.393091479523378}, {"width": 32.81423920429438}, {"width": 47.149756445830405}, {"width": 65.33461046905282}, {"width": 86.37037052833975}, {"width": 109.33892989502539}, {"width": 133.41230282176576}, {"width": 157.85884551581262}, {"width": 182.04628299772315}, {"width": 205.44194266136975}, {"width": 227.61060202825664}, {"width": 248.21035425951496}, {"width": 266.98688211357853}, {"width": 283.76651084306576}, {"width": 298.4483845697049}, {"width": 310.9960804256286}, {"width": 321.4289415523001}, {"width": 329.8133751240586}, {"width": 336.2543259916007}, {"width": 340.8871012571039}, {"width": 343.86968688747186}, {"width": 345.3756649933707}, {"width": 345.5878101607786}, {"width": 344.69241560169286}, {"width": 342.87437515593376}, {"width": 340.3130254839324}, {"width": 337.1787342031952}, {"width": 333.6302042185025}, {"width": 329.81245198772206}, {"width": 325.85540780392}, {"width": 321.87307916736376}, {"width": 317.96321374101274}, {"width": 314.2073959794465}, {"width": 310.67151102884014}, {"width": 307.40651064358445}, {"width": 304.4494183838348}, {"width": 301.82451498553036}, {"width": 299.54464928072105}, {"width": 297.6126251584508}, {"width": 296.0226205817111}, {"width": 294.76160042262495}, {"width": 293.81069067765213}, {"width": 293.14648733248623}, {"width": 292.74227864125334}, {"width": 292.5691647684034}, {"width": 292.59706353792967}, {"width": 292.7955953873452}, {"width": 293.13484449599906}, {"width": 293.58599642847963}, {"width": 294.12185549853046}, {"width": 294.7172474243504}, {"width": 295.34931473034}, {"width": 295.9977137799712}, {"width": 296.6447233329546}, {"width": 297.27527514572245}, {"width": 297.8769174192344}, {"width": 298.4397218859187}, {"width": 298.95614506241293}, {"width": 299.4208537203265}, {"width": 299.830523985651}, {"width": 300.18362270856585}, {"width": 300.4801788861842}, {"width": 300.7215520049033}, {"width": 300.91020322646193}, {"width": 301.0494743987626}, {"width": 301.14337895131854}, {"width": 301.19640785435644}, {"width": 301.21335299496263}, {"width": 301.19914956454244}, {"width": 301.1587383673462}, {"width": 301.09694835503484}, {"width": 301.0183991696976}, {"width": 300.9274230375754}, {"width": 300.8280049961901}, {"width": 300.72374015520603}, {"width": 300.6178064814055}, {"width": 300.5129514548958}, {"width": 300.4114908606012}, {"width": 300.3153179492917}, {"width": 300.2259212186874}, {"width": 300.14440912035536}, {"width": 300.0715400851321}, {"width": 300.00775637189656}, {"width": 299.95322037533407}, {"width": 299.90785217201056}, {"width": 299.87136723533143}, {"width": 299.8433134040923}, {"width": 299.8231063422815}, {"width": 299.8100628761268}, {"width": 299.80343173527547}, {"width": 299.8024213562279}, {"width": 299.8062245260447}, {"width": 299.8140397517583}, {"width": 299.8250893351558}, {"width": 299.8386342133794}, {"width": 299.8539856932032}, {"width": 299.8705142612688}, {"width": 299.8876556946383}, {"width": 299.9049147265795}, {"width": 299.9218665425214}, {"width": 299.93815639168804}, {"width": 299.9534976021874}, {"width": 299.96766828247127}, {"width": 299.98050698126195}, {"width": 300}], + empty_300_50: [{"width": 300}, {"width": 296.9704540361398}, {"width": 288.5587149961657}, {"width": 275.75914603050853}, {"width": 259.52266922405994}, {"width": 240.7407405996966}, {"width": 220.233098308013}, {"width": 198.7390153377091}, {"width": 176.91174507516726}, {"width": 155.31581875203284}, {"width": 134.42683690949124}, {"width": 114.63339104619938}, {"width": 96.24075512543303}, {"width": 79.47599811287625}, {"width": 64.49418674726266}, {"width": 51.38537091990628}, {"width": 40.18207104854585}, {"width": 30.867016471160543}, {"width": 23.38091506780476}, {"width": 17.63006607892789}, {"width": 13.493659591871442}, {"width": 10.830636707614318}, {"width": 9.486013398776088}, {"width": 9.296598070733381}, {"width": 10.09605749848852}, {"width": 11.719307896487734}, {"width": 14.006227246488947}, {"width": 16.80470160429003}, {"width": 19.973031947765612}, {"width": 23.381739296676724}, {"width": 26.914814460785717}, {"width": 30.470465029139493}, {"width": 33.9614163026672}, {"width": 37.31482501835135}, {"width": 40.47186515282133}, {"width": 43.38704406822818}, {"width": 46.02730501443322}, {"width": 48.37096876291935}, {"width": 50.406563142213365}, {"width": 52.13158467995465}, {"width": 53.55123162347219}, {"width": 54.67714247979915}, {"width": 55.526169037810575}, {"width": 56.119207738851586}, {"width": 56.480108356023806}, {"width": 56.63467431392549}, {"width": 56.609764698277075}, {"width": 56.432504118441784}, {"width": 56.12960312857225}, {"width": 55.726788903143174}, {"width": 55.24834330488349}, {"width": 54.716743371115705}, {"width": 54.1523975621964}, {"width": 53.5734698393114}, {"width": 52.995782738433405}, {"width": 52.43279004846211}, {"width": 51.895609447112115}, {"width": 51.39310545900118}, {"width": 50.93201333713131}, {"width": 50.51709489256563}, {"width": 50.15131786995449}, {"width": 49.83605115306624}, {"width": 49.57126885162127}, {"width": 49.35575713847923}, {"width": 49.187318547801866}, {"width": 49.06296928681915}, {"width": 48.979125936322745}, {"width": 48.93177870146746}, {"width": 48.91664911164049}, {"width": 48.92933074594422}, {"width": 48.96541217201228}, {"width": 49.02058182586171}, {"width": 49.090715027055666}, {"width": 49.1719437164505}, {"width": 49.26070982483023}, {"width": 49.353803432851706}, {"width": 49.448387070173595}, {"width": 49.54200762955728}, {"width": 49.6325974458918}, {"width": 49.71846611670387}, {"width": 49.798284626172}, {"width": 49.87106328539702}, {"width": 49.93612492398925}, {"width": 49.99307466794953}, {"width": 50.04176752202319}, {"width": 50.082274846419196}, {"width": 50.11485068273985}, {"width": 50.1398987463462}, {"width": 50.15794076582014}, {"width": 50.169586717744}, {"width": 50.175507379218395}, {"width": 50.176409503368}, {"width": 50.173013816031606}, {"width": 50.16603593593017}, {"width": 50.15617023646811}, {"width": 50.14407659519702}, {"width": 50.13036991678292}, {"width": 50.11561226672433}, {"width": 50.100307415501575}, {"width": 50.084897565554094}, {"width": 50.06976201560597}, {"width": 50.05521750742144}, {"width": 50.041519998047015}, {"width": 50.02886760493642}, {"width": 50.01740448101614}, {"width": 50}], + empty_0_150: [{"width": 0}, {"width": 1.817727578316095}, {"width": 6.864771002300529}, {"width": 14.544512381694858}, {"width": 24.28639846556402}, {"width": 35.55555564018202}, {"width": 47.860141015192184}, {"width": 60.75659079737453}, {"width": 73.85295295489964}, {"width": 86.81050874878028}, {"width": 99.34389785430524}, {"width": 111.21996537228036}, {"width": 122.25554692474017}, {"width": 132.31440113227424}, {"width": 141.3034879516424}, {"width": 149.16877744805623}, {"width": 155.89075737087248}, {"width": 161.47979011730365}, {"width": 165.9714509593171}, {"width": 169.42196035264323}, {"width": 171.90380424487708}, {"width": 173.50161797543134}, {"width": 174.3083919607343}, {"width": 174.42204115755993}, {"width": 173.94236550090685}, {"width": 172.96841526210733}, {"width": 171.59626365210661}, {"width": 169.91717903742597}, {"width": 168.01618083134062}, {"width": 165.97095642199395}, {"width": 163.85111132352856}, {"width": 161.71772098251628}, {"width": 159.62315021839967}, {"width": 157.61110498898918}, {"width": 155.7168809083072}, {"width": 153.9677735590631}, {"width": 152.38361699134006}, {"width": 150.97741874224837}, {"width": 149.75606211467198}, {"width": 148.7210491920272}, {"width": 147.86926102591667}, {"width": 147.1937145121205}, {"width": 146.68429857731365}, {"width": 146.32847535668904}, {"width": 146.1119349863857}, {"width": 146.0191954116447}, {"width": 146.03414118103373}, {"width": 146.1404975289349}, {"width": 146.32223812285662}, {"width": 146.56392665811407}, {"width": 146.8509940170699}, {"width": 147.16995397733058}, {"width": 147.50856146268217}, {"width": 147.85591809641318}, {"width": 148.20253035693997}, {"width": 148.54032597092277}, {"width": 148.86263433173275}, {"width": 149.1641367245993}, {"width": 149.44079199772122}, {"width": 149.68974306446063}, {"width": 149.9092092780273}, {"width": 150.09836930816024}, {"width": 150.25723868902722}, {"width": 150.38654571691245}, {"width": 150.48760887131886}, {"width": 150.56221842790848}, {"width": 150.61252443820632}, {"width": 150.64093277911948}, {"width": 150.65001053301566}, {"width": 150.64240155243343}, {"width": 150.6207526967926}, {"width": 150.58765090448296}, {"width": 150.5455709837666}, {"width": 150.49683377012968}, {"width": 150.44357410510185}, {"width": 150.38771794028895}, {"width": 150.33096775789582}, {"width": 150.2747954222656}, {"width": 150.22044153246492}, {"width": 150.16892032997768}, {"width": 150.12102922429682}, {"width": 150.0773620287618}, {"width": 150.03832504560646}, {"width": 150}], + empty_150_0: [{"width": 150}, {"width": 148.1822724216839}, {"width": 143.13522899769947}, {"width": 135.45548761830514}, {"width": 125.71360153443598}, {"width": 114.44444435981798}, {"width": 102.13985898480782}, {"width": 89.24340920262547}, {"width": 76.14704704510036}, {"width": 63.18949125121973}, {"width": 50.65610214569477}, {"width": 38.78003462771965}, {"width": 27.744453075259834}, {"width": 17.685598867725762}, {"width": 8.696512048357606}, {"width": 0.8312225519437786}, {"width": -5.890757370872479}, {"width": -11.479790117303663}, {"width": -15.971450959317131}, {"width": -19.42196035264325}, {"width": -21.903804244877115}, {"width": -23.501617975431387}, {"width": -24.308391960734326}, {"width": -24.422041157559956}, {"width": -23.942365500906877}, {"width": -22.96841526210735}, {"width": -21.59626365210662}, {"width": -19.917179037425974}, {"width": -18.016180831340627}, {"width": -15.970956421993964}, {"width": -13.85111132352857}, {"width": -11.717720982516308}, {"width": -9.623150218399688}, {"width": -7.611104988989201}, {"width": -5.716880908307214}, {"width": -3.967773559063103}, {"width": -2.3836169913400775}, {"width": -0.9774187422484015}, {"width": 0.243937885328009}, {"width": 1.278950807972781}, {"width": 2.1307389740833065}, {"width": 2.806285487879483}, {"width": 3.315701422686338}, {"width": 3.6715246433109443}, {"width": 3.8880650136142787}, {"width": 3.980804588355289}, {"width": 3.9658588189662445}, {"width": 3.8595024710650705}, {"width": 3.677761877143348}, {"width": 3.4360733418859026}, {"width": 3.149005982930094}, {"width": 2.8300460226694235}, {"width": 2.491438537317839}, {"width": 2.1440819035868386}, {"width": 1.7974696430600396}, {"width": 1.4596740290772614}, {"width": 1.1373656682672661}, {"width": 0.8358632754007039}, {"width": 0.5592080022787829}, {"width": 0.3102569355393775}, {"width": 0.09079072197269265}, {"width": -0.09836930816025949}, {"width": -0.25723868902723984}, {"width": -0.38654571691246514}, {"width": -0.4876088713188821}, {"width": -0.5622184279085115}, {"width": -0.6125244382063537}, {"width": -0.6409327791195243}, {"width": -0.6500105330157058}, {"width": -0.6424015524334653}, {"width": -0.620752696792631}, {"width": -0.5876509044829706}, {"width": -0.5455709837666003}, {"width": -0.49683377012969926}, {"width": -0.44357410510186107}, {"width": -0.38771794028897383}, {"width": -0.3309677578958413}, {"width": -0.27479542226562914}, {"width": -0.2204415324649212}, {"width": -0.16892032997767473}, {"width": -0.12102922429680027}, {"width": -0.07736202876178572}, {"width": -0.03832504560644955}, {"width": 0}], +} \ No newline at end of file diff --git a/app/src/examples/RuntimeTests/tests/animations/withTiming/easing.test.tsx b/app/src/examples/RuntimeTests/tests/animations/withTiming/easing.test.tsx index ec924350b7e..af33fdedc5b 100644 --- a/app/src/examples/RuntimeTests/tests/animations/withTiming/easing.test.tsx +++ b/app/src/examples/RuntimeTests/tests/animations/withTiming/easing.test.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { View, StyleSheet } from 'react-native'; +import { View, StyleSheet, Easing as EasingRN } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, @@ -20,6 +20,7 @@ import { unmockAnimationTimer, } from '../../../ReanimatedRuntimeTestsRunner/RuntimeTestsApi'; import { EasingSnapshots } from './withTiming.snapshot'; +import { ErrorBoundary } from '../../../ReanimatedRuntimeTestsRunner/RuntimeTestsRunner'; const ActiveAnimatedComponent = ({ easing }: { easing: EasingFunction | EasingFunctionFactory | undefined }) => { const widthSV = useSharedValue(0); @@ -82,6 +83,38 @@ async function getSnapshotUpdates(easingFn: EasingFunction | EasingFunctionFacto } describe('withTiming snapshots 📸, test EASING', () => { + describe('Invalid easing', async () => { + test.failing( + 'Easing imported from react-native throws an error', + 'Error: [Reanimated] The easing function is not a worklet. Please make sure you import `Easing` from react-native-reanimated.', + async () => { + await render( + + + , + ); + await wait(1200); + }, + ); + + test.failing( + 'Easing must be a worklet, otherwise it throws an error', + 'Error: [Reanimated] The easing function is not a worklet. Please make sure you import `Easing` from react-native-reanimated.', + async () => { + await render( + + { + return 42; + }} + /> + , + ); + await wait(1200); + }, + ); + }); + test('No easing function', async () => { const [activeUpdates, activeNativeUpdates, passiveUpdates] = await getSnapshotUpdates(undefined); diff --git a/cspell.json b/cspell.json index 6898ac0bfdd..1baf6205571 100644 --- a/cspell.json +++ b/cspell.json @@ -36,6 +36,12 @@ "workletizes", "worklets" ], + "flagWords": [ + "ere", + "hte", + "truck", + "trucker" + ], "ignorePaths": [ "node_modules", "docs/src/components/Testimonials",