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

breaking: Migrate codebase to typescript #101

Merged
merged 28 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5a7af8a
refactor: Migrate codebase to typescript
Ne3l Aug 15, 2022
7059af2
fix test
Ne3l Aug 15, 2022
b7545bb
use node14
Ne3l Aug 15, 2022
225d0e2
fix rebase
Ne3l Sep 11, 2022
e25a27c
Add feedback
Ne3l Sep 11, 2022
b6e868a
fix test
Ne3l Sep 11, 2022
17c5539
use StyleSheet
Ne3l Sep 11, 2022
101a212
fix rebase to-have-styles
Ne3l Sep 18, 2022
5252b75
fix tests
Ne3l Sep 18, 2022
59c41a8
Simplify code
Ne3l Sep 18, 2022
87184c0
Apply feedback
Ne3l Sep 18, 2022
a105a87
fix rebase d38fc801b0bd01934166cd50634cdb9530a64a4a
Ne3l Sep 26, 2022
470b57d
use RNTL tsconfig
Ne3l Sep 26, 2022
34c2d0b
change ts-ignore with ts-expect-error.
Ne3l Sep 26, 2022
487079f
Fix test
Ne3l Sep 26, 2022
9928fb2
refactor: tweak export matcher types
mdjastrzebski Sep 28, 2022
785a4e7
refactor: cleanup toHaveStyle
mdjastrzebski Sep 28, 2022
9a4c58a
chore: vlaidate types on CI
mdjastrzebski Sep 28, 2022
7dd9d6d
refactor: disable expected type errors
mdjastrzebski Sep 28, 2022
c738e45
refactor: re-apply types to toHaveStyle from main branch
mdjastrzebski Sep 28, 2022
cf7ea72
refactor: apply type assertions
mdjastrzebski Sep 28, 2022
0aa0f9d
refactor: reorder imports
mdjastrzebski Sep 28, 2022
071b059
refactor: re-order
mdjastrzebski Sep 28, 2022
bce1a22
refactor: restore `getType()`
mdjastrzebski Sep 28, 2022
fef4cfc
refactor: review logic changes
mdjastrzebski Sep 28, 2022
821615b
refactor: restore output to dist folder
mdjastrzebski Sep 28, 2022
5cf066e
fix: not.toBeEnabled == toBeDisabled.
mdjastrzebski Sep 29, 2022
bb67f0f
refactor: fix undefined StyleSheet.flatten() result handling
mdjastrzebski Sep 29, 2022
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
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:
- name: ▶️ Run linter
run: yarn lint

- name: ▶️ Run typecheck
run: yarn typecheck

- name: ▶️ Run tests with coverage
run: yarn test:coverage

Expand Down
5 changes: 3 additions & 2 deletions extend-expect.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';
import type { ReactTestInstance } from 'react-test-renderer';

declare global {
Expand All @@ -7,10 +8,10 @@ declare global {
toBeDisabled(): R;
toContainElement(element: ReactTestInstance | null): R;
toBeEmptyElement(): R;
toHaveProp(attr: string, value?: any): R;
toHaveProp(attr: string, value?: unknown): R;
toHaveTextContent(text: string | RegExp, options?: { normalizeWhitespace: boolean }): R;
toBeEnabled(): R;
toHaveStyle(style: object[] | object): R;
toHaveStyle(style: StyleProp<ViewStyle | TextStyle | ImageStyle>): R;

/** @deprecated This function has been renamed to `toBeEmptyElement`. */
toBeEmpty(): R;
Expand Down
6 changes: 2 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const ignores = ['/node_modules/', '/__tests__/helpers/', '__mocks__'];

module.exports = {
preset: '@testing-library/react-native',
testMatch: ['**/__tests__/**/*.+(js|jsx|ts|tsx)'],
testPathIgnorePatterns: [...ignores],
setupFilesAfterEnv: ['<rootDir>/setup-tests.js'],
setupFilesAfterEnv: ['<rootDir>/setup-tests.ts'],
snapshotSerializers: ['@relmify/jest-serializer-strip-ansi/always'],
collectCoverageFrom: ['src/**/*.+(js|jsx|ts|tsx)'],
testPathIgnorePatterns: ['/node_modules/', '/__tests__/helpers/', '/dist/', '__mocks__'],
};
21 changes: 15 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
"readme:toc": "doctoc README.md --maxlevel 3 --title '## Table of Contents'",
"test": "jest --colors",
"lint": "eslint .",
"prepublishOnly": "rm -rf dist; babel src --out-dir dist --ignore 'src/__tests__/*'",
"prepublishOnly": "rm -rf dist && tsc -p tsconfig.prod.json",
"semantic-release": "semantic-release",
"test:coverage": "jest --coverage --colors",
"test:watch": "jest --watch --coverage"
"test:watch": "jest --watch --coverage",
"typecheck": "tsc -noEmit"
},
"files": [
"dist",
Expand All @@ -32,7 +33,7 @@
"chalk": "^4.1.2",
"jest-diff": "^29.0.1",
"jest-matcher-utils": "^29.0.1",
"pretty-format": "^29.0.1",
"pretty-format": "^29.0.3",
"redent": "^3.0.0"
},
"devDependencies": {
Expand All @@ -42,18 +43,23 @@
"@callstack/eslint-config": "^13.0.1",
"@relmify/jest-serializer-strip-ansi": "^1.0.2",
"@testing-library/react-native": "^11.0.0",
"@types/jest": "^29.0.1",
"@types/react": "^18.0.19",
"@types/react-native": "^0.69.8",
"@types/react-test-renderer": "^18.0.0",
"commitizen": "^4.2.5",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.21.0",
"husky": "^8.0.1",
"jest": "^29.0.1",
"metro-react-native-babel-preset": "^0.70.3",
"metro-react-native-babel-preset": "^0.72.0",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"react": "18.0.0",
"react-native": "^0.69.4",
"react-test-renderer": "18.0.0",
"semantic-release": "^19.0.3"
"semantic-release": "^19.0.3",
"typescript": "^4.8.3"
},
"peerDependencies": {
"react": ">=16.0.0",
Expand All @@ -79,11 +85,14 @@
"react-native-a11y/has-valid-accessibility-descriptors": "off",
"react-native-a11y/has-accessibility-hint": "off",
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": "off",
"react-native-a11y/has-valid-accessibility-value": "off"
"react-native-a11y/has-valid-accessibility-value": "off",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-explicit-any": "error"
}
},
"eslintIgnore": [
"node_modules/",
"dist/",
"lib/"
],
"release": {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('.toBeDisabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled prop for element ${name}`, () => {
const { queryByTestId } = render(
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
<Component disabled testID={name}>
<TextInput />
</Component>,
Expand All @@ -38,6 +39,7 @@ describe('.toBeDisabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityState for element ${name}`, () => {
const { queryByTestId } = render(
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
<Component accessibilityState={{ disabled: true }} testID={name}>
<TextInput />
</Component>,
Expand All @@ -53,6 +55,7 @@ describe('.toBeEnabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled prop for element ${name} when undefined`, () => {
const { queryByTestId } = render(
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
<Component testID={name}>
<TextInput />
</Component>,
Expand All @@ -66,6 +69,7 @@ describe('.toBeEnabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityState for element ${name} when false`, () => {
const { queryByTestId } = render(
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
<Component accessibilityState={{ disabled: false }} testID={name}>
<TextInput />
</Component>,
Expand Down Expand Up @@ -103,14 +107,14 @@ describe('for .toBeEnabled/Disabled Button', () => {
});

test('Errors when matcher misses', () => {
const { queryByTestId, queryByTitle } = render(
const { queryByTestId, queryByText } = render(
<View testID="view">
<Button testID="enabled" title="enabled" />
<Button disabled testID="disabled" title="disabled" />
</View>,
);

expect(() => expect(queryByTestId('enabled')).toBeDisabled()).toThrow();
expect(() => expect(queryByTitle('disabled')).toBeEnabled()).toThrow();
expect(() => expect(queryByText('disabled')).toBeEnabled()).toThrow();
});
});
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,21 @@ test('.toContainElement negative test cases', () => {
expect(() => expect(nonExistantElement).toContainElement(grandparent)).toThrow();
expect(() => expect(grandparent).toContainElement(nonExistantElement)).toThrow();
expect(() => expect(nonExistantElement).toContainElement(nonExistantElement)).toThrow();

// @ts-expect-error intentionally passing incorrect type
expect(() => expect(nonExistantElement).toContainElement(fakeElement)).toThrow();
expect(() => expect(fakeElement).toContainElement(nonExistantElement)).toThrow();
expect(() => expect(fakeElement).not.toContainElement(nonExistantElement)).toThrow();
expect(() => expect(fakeElement).toContainElement(grandparent)).toThrow();

// @ts-expect-error intentionally passing incorrect type
expect(() => expect(grandparent).toContainElement(fakeElement)).toThrow();

// @ts-expect-error intentionally passing incorrect type
expect(() => expect(fakeElement).toContainElement(fakeElement)).toThrow();
expect(() => expect(grandparent).not.toContainElement(child)).toThrow();
expect(() => expect(grandparent).not.toContainElement(textElement)).toThrow();

// @ts-expect-error intentionally passing incorrect type
expect(() => expect(grandparent).not.toContainElement(undefined)).toThrow();
});
10 changes: 5 additions & 5 deletions src/__tests__/to-have-prop.js → src/__tests__/to-have-prop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { render } from '@testing-library/react-native';

test('.toHaveProp', () => {
const { queryByTestId } = render(
<View accessibilityLabel={null} testID="view">
<Text allowFontScaling={false} testID="text" editable={false}>
<View style={null} testID="view">
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
<Text allowFontScaling={false} testID="text" ellipsizeMode="head">
text
</Text>
<Button disabled testID="button" title="ok" />
</View>,
);

expect(queryByTestId('button')).toHaveProp('accessibilityState', { disabled: true });
expect(queryByTestId('text')).toHaveProp('editable', false);
expect(queryByTestId('text')).toHaveProp('ellipsizeMode', 'head');
expect(queryByTestId('text')).toHaveProp('allowFontScaling', false);

expect(queryByTestId('button')).not.toHaveProp('accessibilityStates');
expect(queryByTestId('button')).not.toHaveProp('editable', false);
expect(queryByTestId('button')).not.toHaveProp('ellipsizeMode', undefined);
expect(queryByTestId('button')).not.toHaveProp('allowFontScaling', false);
expect(queryByTestId('text')).not.toHaveProp('style');

Expand All @@ -30,5 +30,5 @@ test('.toHaveProp', () => {
expect(queryByTestId('text')).toHaveProp('allowFontScaling', 'wrongValue'),
).toThrow();

expect(queryByTestId('view')).toHaveProp('accessibilityLabel', null);
expect(queryByTestId('view')).toHaveProp('style', null);
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { render } from '@testing-library/react-native';

describe('.toHaveStyle', () => {
test('handles positive test cases', () => {
const styles = StyleSheet.create({ container: { color: 'white' } });
const styles = StyleSheet.create({ container: { borderBottomColor: 'white' } });
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
const { getByTestId } = render(
<View
testID="container"
Expand All @@ -29,7 +29,7 @@ describe('.toHaveStyle', () => {
expect(container).toHaveStyle([{ backgroundColor: 'blue' }, { height: '100%' }]);
expect(container).toHaveStyle({ backgroundColor: 'blue' });
expect(container).toHaveStyle({ height: '100%' });
expect(container).toHaveStyle({ color: 'white' });
expect(container).toHaveStyle({ borderBottomColor: 'white' });
expect(container).toHaveStyle({ width: '50%' });
expect(container).toHaveStyle([[{ width: '50%' }]]);
expect(container).toHaveStyle({ transform: [{ scale: 2 }, { rotate: '45deg' }] });
Expand All @@ -41,7 +41,7 @@ describe('.toHaveStyle', () => {
testID="container"
style={{
backgroundColor: 'blue',
color: 'black',
borderBottomColor: 'black',
height: '100%',
transform: [{ scale: 2 }, { rotate: '45deg' }],
}}
Expand All @@ -54,8 +54,8 @@ describe('.toHaveStyle', () => {
expect(() =>
expect(container).toHaveStyle({ backgroundColor: 'blue', transform: [{ scale: 1 }] }),
).toThrowErrorMatchingSnapshot();
expect(() => expect(container).toHaveStyle({ fontWeight: 'bold' })).toThrow();
expect(() => expect(container).not.toHaveStyle({ color: 'black' })).toThrow();
expect(container).not.toHaveStyle({ fontWeight: 'bold' });
expect(container).not.toHaveStyle({ color: 'black' });
expect(container).not.toHaveStyle({ transform: [{ rotate: '45deg' }, { scale: 2 }] });
expect(container).not.toHaveStyle({ transform: [{ rotate: '45deg' }] });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ describe('.toHaveTextContent', () => {
const { queryByTestId } = render(<Text testID="count-value">2</Text>);

expect(queryByTestId('count-value')).toHaveTextContent('2');
expect(queryByTestId('count-value')).toHaveTextContent(2);
expect(queryByTestId('count-value')).toHaveTextContent(/2/);
expect(queryByTestId('count-value')).not.toHaveTextContent('21');
});
Expand Down Expand Up @@ -73,7 +72,7 @@ describe('.toHaveTextContent', () => {
});

test('can handle multiple levels with no explicit children prop', () => {
const NoChildren = ({ text }) => <Text>{text}</Text>;
const NoChildren = ({ text }: { text: string }) => <Text>{text}</Text>;
const answer = 'Answer';
const { container } = render(
<View>
Expand Down
36 changes: 0 additions & 36 deletions src/__tests__/utils.js

This file was deleted.

39 changes: 39 additions & 0 deletions src/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { checkReactElement, isEmpty } from '../utils';

describe('checkReactElement', () => {
test('it does not throw an error for valid native primitives', () => {
expect(() => {
// @ts-expect-error Argument of type '{ type: "text"; }' is not assignable to parameter of type 'ReactTestInstance'. Type '{ type: "text"; }' is missing the following properties from type 'ReactTestInstance': instance, props, parent, children, and 6 more.ts(2345)
checkReactElement({ type: 'Text' }, () => {}, null);
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
}).not.toThrow();
});

test('ReactTestInstance does not throw', () => {
expect(() => {
// @ts-expect-error Argument of type '{ _fiber: {}; }' is not assignable to parameter of type 'ReactTestInstance'. Object literal may only specify known properties, and '_fiber' does not exist in type 'ReactTestInstance'.ts(2345)
checkReactElement({ _fiber: {} }, () => {}, null);
}).not.toThrow();
});

test('it does throw an error for invalid native primitives', () => {
expect(() => {
// @ts-expect-error Argument of type '{ type: "button"; }' is not assignable to parameter of type 'ReactTestInstance'. Type '{ type: "button"; }' is missing the following properties from type 'ReactTestInstance': instance, props, parent, children, and 6 more.ts(2345)
checkReactElement({ type: 'Button' }, () => {}, null);
}).toThrow();
});
});

test('isEmpty', () => {
expect(isEmpty(null)).toEqual(true);
expect(isEmpty(undefined)).toEqual(true);
expect(isEmpty('')).toEqual(true);
expect(isEmpty(' ')).toEqual(false);
expect(isEmpty([])).toEqual(true);
expect(isEmpty([[]])).toEqual(false);
expect(isEmpty({})).toEqual(true);
expect(isEmpty({ x: 0 })).toEqual(false);
expect(isEmpty(0)).toEqual(true);
expect(isEmpty(1)).toEqual(false);
expect(isEmpty(NaN)).toEqual(true);
expect(isEmpty([''])).toEqual(false);
});
3 changes: 0 additions & 3 deletions src/extend-expect.js

This file was deleted.

14 changes: 7 additions & 7 deletions src/index.js → src/extend-expect.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { toBeDisabled, toBeEnabled } from './to-be-disabled';
import { toBeEmptyElement, toBeEmpty } from './to-be-empty-element';
import { toHaveProp } from './to-have-prop';
import { toHaveTextContent } from './to-have-text-content';
import { toContainElement } from './to-contain-element';
import { toHaveProp } from './to-have-prop';
import { toHaveStyle } from './to-have-style';
import { toHaveTextContent } from './to-have-text-content';

export {
expect.extend({
toBeDisabled,
toContainElement,
toBeEnabled,
toBeEmptyElement,
toBeEmpty, // Deprecated
toContainElement,
toHaveProp,
toHaveTextContent,
toBeEnabled,
toHaveStyle,
};
toHaveTextContent,
});
Loading