Skip to content

Commit

Permalink
Merge branch 'poc/custom-renderer' into v14
Browse files Browse the repository at this point in the history
# Conflicts:
#	.github/workflows/ci.yml
#	jest-setup.ts
#	package.json
#	src/__tests__/config.test.ts
#	src/__tests__/host-component-names.test.tsx
#	src/__tests__/render.test.tsx
#	src/act.ts
#	src/config.ts
#	src/fire-event.ts
#	src/helpers/__tests__/component-tree.test.tsx
#	src/helpers/accessibility.ts
#	src/helpers/component-tree.ts
#	src/helpers/debug.ts
#	src/helpers/host-component-names.tsx
#	src/helpers/matchers/match-label-text.ts
#	src/helpers/text-input.ts
#	src/queries/__tests__/accessibility-state.test.tsx
#	src/queries/__tests__/accessibility-value.test.tsx
#	src/queries/accessibility-state.ts
#	src/queries/accessibility-value.ts
#	src/render.tsx
#	src/user-event/press/press.ts
#	yarn.lock
  • Loading branch information
mdjastrzebski committed Nov 13, 2024
2 parents 9ed7549 + 8610930 commit e78659b
Show file tree
Hide file tree
Showing 84 changed files with 758 additions and 1,309 deletions.
16 changes: 1 addition & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
test:
needs: [install-cache-deps]
runs-on: ubuntu-latest
name: Test (concurrent)
name: Test
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -67,20 +67,6 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

test-legacy:
needs: [install-cache-deps]
runs-on: ubuntu-latest
name: Test (legacy)
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js and deps
uses: ./.github/actions/setup-deps

- name: Test in legacy mode
run: CONCURRENT_MODE=0 yarn test:ci

test-website:
runs-on: ubuntu-latest
name: Test Website
Expand Down
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"cSpell.words": ["labelledby", "Pressable", "RNTL", "Uncapitalize", "valuenow", "valuetext"]
"cSpell.words": [
"labelledby",
"Pressable",
"redent",
"RNTL",
"Uncapitalize",
"valuenow",
"valuetext"
]
}
3 changes: 0 additions & 3 deletions jest-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,4 @@ import './src/matchers/extend-expect';

beforeEach(() => {
resetToDefaults();
if (process.env.CONCURRENT_MODE === '0') {
configure({ concurrentRoot: false });
}
});
21 changes: 10 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@testing-library/react-native",
"version": "13.0.0-beta.0",
"version": "14.0.0-alpha.0",
"description": "Simple and complete React Native testing utilities that encourage good testing practices.",
"main": "build/index.js",
"types": "build/index.d.ts",
Expand Down Expand Up @@ -53,9 +53,9 @@
},
"peerDependencies": {
"jest": ">=29.0.0",
"react": ">=18.2.0",
"react-native": ">=0.71",
"react-test-renderer": ">=18.2.0"
"react": ">=19.0.0",
"react-native": ">=0.77",
"universal-test-renderer": "0.5.0"
},
"peerDependenciesMeta": {
"jest": {
Expand All @@ -71,12 +71,11 @@
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.26.0",
"@callstack/eslint-config": "^15.0.0",
"@react-native/babel-preset": "^0.76.1",
"@react-native/babel-preset": "0.77.0-nightly-20241107-0ca2ba082",
"@release-it/conventional-changelog": "^9.0.2",
"@relmify/jest-serializer-strip-ansi": "^1.0.2",
"@types/jest": "^29.5.14",
"@types/react": "^18.3.12",
"@types/react-test-renderer": "^18.3.0",
"babel-jest": "^29.7.0",
"babel-plugin-module-resolver": "^5.0.2",
"del-cli": "^6.0.0",
Expand All @@ -85,18 +84,18 @@
"flow-bin": "~0.170.0",
"jest": "^29.7.0",
"prettier": "^2.8.8",
"react": "18.3.1",
"react-native": "0.76.1",
"react-test-renderer": "18.3.1",
"react": "19.0.0-rc-fb9a90fa48-20240614",
"react-native": "0.77.0-nightly-20241107-0ca2ba082",
"release-it": "^17.10.0",
"strip-ansi": "^6.0.1",
"typescript": "^5.6.3"
"typescript": "^5.6.3",
"universal-test-renderer": "0.5.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"packageManager": "[email protected]",
"engines": {
"node": ">=18"
"node": ">=20"
}
}
5 changes: 3 additions & 2 deletions src/__tests__/act.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Text } from 'react-native';
import { act, fireEvent, render, screen } from '../';
import '../matchers/extend-expect';

type UseEffectProps = { callback(): void };
const UseEffect = ({ callback }: UseEffectProps) => {
Expand Down Expand Up @@ -34,9 +35,9 @@ test('fireEvent should trigger useState', () => {
render(<Counter />);
const counter = screen.getByText(/Total count/i);

expect(counter.props.children).toEqual('Total count: 0');
expect(counter).toHaveTextContent('Total count: 0');
fireEvent.press(counter);
expect(counter.props.children).toEqual('Total count: 1');
expect(counter).toHaveTextContent('Total count: 1');
});

test('should be able to not await act', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/auto-cleanup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ afterEach(() => {

// This just verifies that by importing RNTL in an environment which supports afterEach (like jest)
// we'll get automatic cleanup between tests.
test('component is mounted, but not umounted before test ends', () => {
test('component is mounted, but not unmounted before test ends', () => {
const fn = jest.fn();
render(<Test onUnmount={fn} />);
expect(isMounted).toEqual(true);
expect(fn).not.toHaveBeenCalled();
});

test('component is automatically umounted after first test ends', () => {
test('component is automatically unmounted after first test ends', () => {
expect(isMounted).toEqual(false);
});

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ test('configure() overrides existing config values', () => {
asyncUtilTimeout: 5000,
defaultDebugOptions: { message: 'debug message' },
defaultIncludeHiddenElements: false,
concurrentRoot: true,
});
});

Expand Down
76 changes: 7 additions & 69 deletions src/__tests__/fire-event.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,12 @@ const WithoutEventComponent = (_props: WithoutEventComponentProps) => (
</View>
);

type CustomEventComponentProps = {
onCustomEvent: () => void;
};
const CustomEventComponent = ({ onCustomEvent }: CustomEventComponentProps) => (
<TouchableOpacity onPress={onCustomEvent}>
<Text>Custom event component</Text>
</TouchableOpacity>
);

type MyCustomButtonProps = {
handlePress: () => void;
text: string;
};
const MyCustomButton = ({ handlePress, text }: MyCustomButtonProps) => (
<OnPressComponent onPress={handlePress} text={text} />
);

type CustomEventComponentWithCustomNameProps = {
handlePress: () => void;
};
const CustomEventComponentWithCustomName = ({
handlePress,
}: CustomEventComponentWithCustomNameProps) => (
<MyCustomButton handlePress={handlePress} text="Custom component" />
);

describe('fireEvent', () => {
test('should invoke specified event', () => {
const onPressMock = jest.fn();
render(<OnPressComponent onPress={onPressMock} text="Press me" />);

fireEvent(screen.getByText('Press me'), 'press');
fireEvent.press(screen.getByText('Press me'));

expect(onPressMock).toHaveBeenCalled();
});
Expand All @@ -70,7 +44,7 @@ describe('fireEvent', () => {
const text = 'New press text';
render(<OnPressComponent onPress={onPressMock} text={text} />);

fireEvent(screen.getByText(text), 'press');
fireEvent.press(screen.getByText(text));
expect(onPressMock).toHaveBeenCalled();
});

Expand All @@ -83,26 +57,11 @@ describe('fireEvent', () => {
fireEvent(screen.getByText('Without event'), 'press');
expect(onPressMock).not.toHaveBeenCalled();
});

test('should invoke event with custom name', () => {
const handlerMock = jest.fn();
const EVENT_DATA = 'event data';

render(
<View>
<CustomEventComponent onCustomEvent={handlerMock} />
</View>,
);

fireEvent(screen.getByText('Custom event component'), 'customEvent', EVENT_DATA);

expect(handlerMock).toHaveBeenCalledWith(EVENT_DATA);
});
});

test('fireEvent.press', () => {
const onPressMock = jest.fn();
const text = 'Fireevent press';
const text = 'FireEvent press';
const eventData = {
nativeEvent: {
pageX: 20,
Expand All @@ -113,7 +72,8 @@ test('fireEvent.press', () => {

fireEvent.press(screen.getByText(text), eventData);

expect(onPressMock).toHaveBeenCalledWith(eventData);
expect(onPressMock).toHaveBeenCalledTimes(1);
expect(onPressMock.mock.calls[0][0].nativeEvent).toMatchObject(eventData.nativeEvent);
});

test('fireEvent.scroll', () => {
Expand Down Expand Up @@ -161,26 +121,6 @@ it('sets native state value for unmanaged text inputs', () => {
expect(input).toHaveDisplayValue('abc');
});

test('custom component with custom event name', () => {
const handlePress = jest.fn();

render(<CustomEventComponentWithCustomName handlePress={handlePress} />);

fireEvent(screen.getByText('Custom component'), 'handlePress');

expect(handlePress).toHaveBeenCalled();
});

test('event with multiple handler parameters', () => {
const handlePress = jest.fn();

render(<CustomEventComponentWithCustomName handlePress={handlePress} />);

fireEvent(screen.getByText('Custom component'), 'handlePress', 'param1', 'param2');

expect(handlePress).toHaveBeenCalledWith('param1', 'param2');
});

test('should not fire on disabled TouchableOpacity', () => {
const handlePress = jest.fn();
render(
Expand Down Expand Up @@ -250,8 +190,7 @@ test('should fire inside View with pointerEvents="box-none"', () => {
);

fireEvent.press(screen.getByText('Trigger'));
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).toHaveBeenCalledTimes(2);
expect(onPress).toHaveBeenCalledTimes(1);
});

test('should fire inside View with pointerEvents="auto"', () => {
Expand All @@ -265,8 +204,7 @@ test('should fire inside View with pointerEvents="auto"', () => {
);

fireEvent.press(screen.getByText('Trigger'));
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).toHaveBeenCalledTimes(2);
expect(onPress).toHaveBeenCalledTimes(1);
});

test('should not fire deeply inside View with pointerEvents="box-only"', () => {
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/react-native-animated.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Animated, ViewStyle } from 'react-native';
import { Animated, Text, ViewStyle } from 'react-native';
import { act, render, screen } from '..';

type AnimatedViewProps = {
Expand Down Expand Up @@ -44,19 +44,19 @@ describe('AnimatedView', () => {
it('should use native driver when useNativeDriver is true', async () => {
render(
<AnimatedView fadeInDuration={250} useNativeDriver={true}>
Test
<Text>Test</Text>
</AnimatedView>,
);
expect(screen.root).toHaveStyle({ opacity: 0 });

await act(() => jest.advanceTimersByTime(250));
expect(screen.root).toHaveStyle({ opacity: 1 });
// expect(screen.root).toHaveStyle({ opacity: 1 });
});

it('should not use native driver when useNativeDriver is false', async () => {
render(
<AnimatedView fadeInDuration={250} useNativeDriver={false}>
Test
<Text>Test</Text>
</AnimatedView>,
);
expect(screen.root).toHaveStyle({ opacity: 0 });
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/render-debug.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable jest/no-conditional-expect */
/* eslint-disable no-console */
import * as React from 'react';
import { Pressable, Text, TextInput, View } from 'react-native';
Expand Down
19 changes: 1 addition & 18 deletions src/__tests__/render-hook.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable jest/no-conditional-expect */
import React, { ReactNode } from 'react';
import TestRenderer from 'react-test-renderer';
import { renderHook } from '../pure';

test('gives committed result', () => {
Expand Down Expand Up @@ -85,20 +85,3 @@ test('props type is inferred correctly when initial props is explicitly undefine

expect(result.current).toBe(6);
});

/**
* This test makes sure that calling renderHook does
* not try to detect host component names in any form.
* But since there are numerous methods that could trigger that
* we check the count of renders using React Test Renderers.
*/
test('does render only once', () => {
jest.spyOn(TestRenderer, 'create');

renderHook(() => {
const [state, setState] = React.useState(1);
return [state, setState];
});

expect(TestRenderer.create).toHaveBeenCalledTimes(1);
});
Loading

0 comments on commit e78659b

Please sign in to comment.