Skip to content

Commit

Permalink
Introduce Import buttons to run tests more easily (#6216)
Browse files Browse the repository at this point in the history
## Summary


![image](https://github.com/user-attachments/assets/304f53ba-5a9c-47ad-a5ff-1d0480175644)

## Android Header Fix
This PR should fix problem with additional value added to "top" property
on Android. It fixes test cases similar to: withSequence -> Cascade of
callbacks -> Test that all callbacks have been called a correct number
of times, this test case includes snapshot including top property.


## Test plan
I've run all tests on paper, on both IOS and Anroid and didn't notice
any unexpected failures.
Several failures of layout transition tests were expected, as their
timing was updated after test creation.
  • Loading branch information
Latropos authored Jul 16, 2024
1 parent 7276afd commit b68d361
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { View, Button, StyleSheet, Text } from 'react-native';
import { View, TouchableOpacity, StyleSheet, Text } from 'react-native';
import type { ReactNode } from 'react';
import React, { useEffect, useState } from 'react';
import { runTests, configure } from './RuntimeTestsApi';
import { RenderLock } from './SyncUIRunner';

interface ImportButton {
testSuiteName: string;
importTest: () => void;
skipByDefault?: boolean;
}

let renderLock: RenderLock = new RenderLock();
export class ErrorBoundary extends React.Component<
{ children: React.JSX.Element | Array<React.JSX.Element> },
Expand All @@ -21,30 +27,86 @@ export class ErrorBoundary extends React.Component<

render() {
if (this.state.hasError) {
return <Text>Something went wrong.</Text>;
return <Text>Unable to render component</Text>;
}

return this.props.children;
}
}

export default function RuntimeTestsRunner() {
function ImportButtons({ importButtons }: { importButtons: Array<ImportButton> }) {
const [importedTests, setImportedTests] = useState<Array<string>>([]);
const [importedAll, setImportedAll] = useState(false);

const handleImportAllClick = () => {
setImportedAll(true);
const newImportedTests = importedTests;
for (const button of importButtons) {
if (!button.skipByDefault) {
button.importTest();
if (!importedTests.includes(button.testSuiteName)) {
newImportedTests.push(button.testSuiteName);
}
}
}
setImportedTests(newImportedTests);
};

const handleImportClick = (button: ImportButton) => {
button.importTest();
if (!importedTests.includes(button.testSuiteName)) {
setImportedTests([...importedTests, button.testSuiteName]);
}
};
return (
<View>
<TouchableOpacity
onPress={handleImportAllClick}
style={[styles.importButton, styles.importAllButton, importedAll ? styles.importButtonImported : {}]}>
<Text style={styles.buttonText}>Import all reanimated tests</Text>
</TouchableOpacity>

<View style={styles.importButtonsFrame}>
{importButtons.map(importButton => {
const { testSuiteName } = importButton;
return (
<TouchableOpacity
key={testSuiteName}
onPress={() => handleImportClick(importButton)}
style={[styles.importButton, importedTests.includes(testSuiteName) ? styles.importButtonImported : {}]}>
<Text style={styles.buttonText}>{testSuiteName}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
}

export default function RuntimeTestsRunner({ importButtons }: { importButtons: Array<ImportButton> }) {
const [component, setComponent] = useState<ReactNode | null>(null);
const [started, setStarted] = useState<boolean>(false);
useEffect(() => {
if (renderLock) {
renderLock.unlock();
}
}, [component]);
return (
<View style={styles.container}>
<Button
title="Run tests"
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onPress={async () => {
renderLock = configure({ render: setComponent });
await runTests();
}}
/>
{started ? null : <ImportButtons importButtons={importButtons} />}
{started ? null : (
<TouchableOpacity
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onPress={async () => {
setStarted(true);
renderLock = configure({ render: setComponent });
await runTests();
}}
style={styles.button}>
<Text style={styles.buttonTextWhite}>Run tests</Text>
</TouchableOpacity>
)}

{/* Don't render anything if component is undefined to prevent blinking */}
{component || null}
</View>
Expand All @@ -56,4 +118,42 @@ const styles = StyleSheet.create({
flex: 1,
flexDirection: 'column',
},
importAllButton: {
marginHorizontal: 20,
marginTop: 20,
},
importButtonsFrame: {
borderRadius: 10,
backgroundColor: 'lightblue',
margin: 20,
paddingHorizontal: 40,
paddingVertical: 10,
},
importButton: {
height: 40,
borderWidth: 2,
marginVertical: 5,
borderRadius: 10,
backgroundColor: 'white',
borderColor: 'navy',
justifyContent: 'center',
alignItems: 'center',
},
importButtonImported: {
backgroundColor: 'pink',
},
button: {
height: 40,
backgroundColor: 'navy',
justifyContent: 'center',
alignItems: 'center',
},
buttonText: {
fontSize: 20,
color: 'navy',
},
buttonTextWhite: {
fontSize: 20,
color: 'white',
},
});
101 changes: 76 additions & 25 deletions apps/common-app/src/examples/RuntimeTests/RuntimeTestsExample.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,81 @@
import React from 'react';
import RuntimeTestsRunner from './ReanimatedRuntimeTestsRunner/RuntimeTestsRunner';

// load tests
import './tests/TestsOfTestingFramework.test';

import './tests/animations';

import './tests/core/cancelAnimation.test';

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

import './tests/layoutAnimations/entering/enteringColors.test';
import './tests/layoutAnimations/entering/predefinedEntering.test';

import './tests/layoutAnimations/exiting/predefinedExiting.test';

import './tests/layoutAnimations/layout/predefinedLayoutPosition.test';

import './tests/advancedAPI/useFrameCallback.test';
// import './tests/advancedAPI/measure.test'; crash on Android

import './tests/core/useSharedValue.test';
import './tests/core/useAnimatedStyle/reuseAnimatedStyle.test';
import './tests/core/useDerivedValue/basic.test';
import './tests/core/useDerivedValue/chain.test';
import { describe } from './ReanimatedRuntimeTestsRunner/RuntimeTestsApi';

export default function RuntimeTestsExample() {
return <RuntimeTestsRunner />;
return (
<RuntimeTestsRunner
importButtons={[
{
skipByDefault: true,
testSuiteName: 'Tests of testing framework',
importTest: () => {
require('./tests/TestsOfTestingFramework.test');
},
},
{
testSuiteName: 'animations',
importTest: () => {
describe('*****withTiming***** ⏰', () => {
require('./tests/animations/withTiming/arrays.test');
require('./tests/animations/withTiming/basic.test');
require('./tests/animations/withTiming/objects.test');
require('./tests/animations/withTiming/colors.test');
require('./tests/animations/withTiming/easing.test');
require('./tests/animations/withTiming/transformMatrices.test');
});
describe('*****withSpring*****', () => {
require('./tests/animations/withSpring/variousConfig.test');
});
describe('*****withDecay*****', () => {
require('./tests/animations/withDecay/basic.test');
});
describe('*****withSequence*****', () => {
require('./tests/animations/withSequence/callbackCascade.test');
require('./tests/animations/withSequence/cancelAnimation.test');
require('./tests/animations/withSequence/numbers.test');
require('./tests/animations/withSequence/arrays.test');
require('./tests/animations/withSequence/colors.test');
});
describe('*****withDelay*****', () => {
require('./tests/animations/withDelay/keepSnapshot.test');
require('./tests/animations/withDelay/addDelays.test');
});
},
},
{
testSuiteName: 'core',
importTest: () => {
require('./tests/core/cancelAnimation.test');
require('./tests/core/useSharedValue.test');
require('./tests/core/useAnimatedStyle/reuseAnimatedStyle.test');
require('./tests/core/useDerivedValue/basic.test');
require('./tests/core/useDerivedValue/chain.test');
},
},
{
testSuiteName: 'utilities',
importTest: () => {
require('./tests/utilities/relativeCoords.test');
},
},
{
testSuiteName: 'layoutAnimations',
importTest: () => {
require('./tests/layoutAnimations/entering/enteringColors.test');
require('./tests/layoutAnimations/entering/predefinedEntering.test');
require('./tests/layoutAnimations/exiting/predefinedExiting.test');
require('./tests/layoutAnimations/layout/predefinedLayoutPosition.test');
},
},
{
testSuiteName: 'advancedAPI',
importTest: () => {
require('./tests/advancedAPI/useFrameCallback.test');
// require('./tests/advancedAPI/measure.test'); // crash on Android
},
},
]}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function WarningComponent() {
return <View />;
}

describe.skip('Tests of Test Framework', () => {
describe('Tests of Test Framework', () => {
test('withTiming - expect error', async () => {
await render(<AnimatedComponent />);
const component = getTestComponent('BrownComponent');
Expand Down

This file was deleted.

0 comments on commit b68d361

Please sign in to comment.