From e374712693434118f1a6987554dd2a89284a34ba Mon Sep 17 00:00:00 2001 From: johelder Date: Wed, 28 May 2025 00:15:41 -0300 Subject: [PATCH 1/6] feat: add testing-library plugin and configure for test files - add @testing-library/react-native and eslint-plugin-testing-library as dev dependencies - configure eslint to use testing-library plugin for test files - update react-native-builder-bob to latest version - add react-test-renderer as dev dependency --- eslint.config.mjs | 15 +- package.json | 7 +- src/__tests__/text-input-otp.test.tsx | 31 +++ src/components/text-input-otp-slot.tsx | 4 +- src/components/text-input.tsx | 1 + yarn.lock | 327 ++++++++++++++++++++++++- 6 files changed, 365 insertions(+), 20 deletions(-) create mode 100644 src/__tests__/text-input-otp.test.tsx diff --git a/eslint.config.mjs b/eslint.config.mjs index 84f2a4d..95943e2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -16,7 +16,14 @@ const compat = new FlatCompat({ export default defineConfig([ { - extends: fixupConfigRules(compat.extends('@react-native', 'prettier')), + files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], + extends: fixupConfigRules( + compat.extends( + '@react-native', + 'prettier', + 'plugin:testing-library/react' + ) + ), plugins: { prettier }, rules: { 'react/react-in-jsx-scope': 'off', @@ -30,12 +37,10 @@ export default defineConfig([ useTabs: false, }, ], + 'testing-library/prefer-screen-queries': 'off', }, }, { - ignores: [ - 'node_modules/', - 'lib/' - ], + ignores: ['node_modules/', 'lib/'], }, ]); diff --git a/package.json b/package.json index 4fe4424..8a1be0e 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@evilmartians/lefthook": "^1.5.0", "@react-native/eslint-config": "^0.78.0", "@release-it/conventional-changelog": "^9.0.2", + "@testing-library/react-native": "^13.2.0", "@types/jest": "^29.5.5", "@types/react": "^19.0.12", "commitlint": "^19.6.1", @@ -95,11 +96,13 @@ "eslint": "^9.22.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-testing-library": "^7.2.2", "jest": "^29.7.0", "prettier": "^3.0.3", "react": "18.3.1", "react-native": "0.76.8", - "react-native-builder-bob": "^0.39.0", + "react-native-builder-bob": "^0.40.11", + "react-test-renderer": "18.3.1", "release-it": "^17.10.0", "typescript": "^5.2.2" }, @@ -176,6 +179,6 @@ "create-react-native-library": { "languages": "js", "type": "library", - "version": "0.49.0" + "version": "0.50.3" } } diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx new file mode 100644 index 0000000..8ef2190 --- /dev/null +++ b/src/__tests__/text-input-otp.test.tsx @@ -0,0 +1,31 @@ +import { fireEvent, render as TLRender } from '@testing-library/react-native'; +import { TextInputOTP, TextInputOTPSlot } from '../components'; +import type { TextInputOTPProps } from '../types'; + +function render(props?: Partial) { + return TLRender( + + + + + + + + + ); +} + +describe('TextInputOTP Component', () => { + beforeAll(() => { + jest.resetAllMocks(); + }); + + it('should call onFilled with the complete code when all digits are filled', () => { + const CODE = '123456'; + const mockedOnFilled = jest.fn(); + const view = render({ onFilled: mockedOnFilled }); + fireEvent.changeText(view.getByTestId('hidden-text-input'), CODE); + + expect(mockedOnFilled).toHaveBeenCalledWith(CODE); + }); +}); diff --git a/src/components/text-input-otp-slot.tsx b/src/components/text-input-otp-slot.tsx index a595593..f18dcf4 100644 --- a/src/components/text-input-otp-slot.tsx +++ b/src/components/text-input-otp-slot.tsx @@ -3,7 +3,7 @@ import { Pressable, Text, StyleSheet } from 'react-native'; import { Caret } from './caret'; import { useTextInputOTP } from '../hooks/use-text-input-otp'; import { useSlotBorderStyles } from '../hooks/use-slot-border-styles'; -import { SLOT_HEIGHT, SLOT_WIDTH } from '../constants'; +import { DEFAULT_DARK_COLOR, SLOT_HEIGHT, SLOT_WIDTH } from '../constants'; import type { TextInputOTPSlotInternalProps, TextInputOTPSlotProps, @@ -60,7 +60,7 @@ const styles = StyleSheet.create({ alignItems: 'center', }, slotText: { - color: '#030712', + color: DEFAULT_DARK_COLOR, fontSize: 14, fontWeight: 'bold', }, diff --git a/src/components/text-input.tsx b/src/components/text-input.tsx index ee11032..dcc367b 100644 --- a/src/components/text-input.tsx +++ b/src/components/text-input.tsx @@ -19,6 +19,7 @@ export const TextInput = forwardRef< return ( =8.0.0 + checksum: b177e3b75c0b8d0e5d71f1c532edb7e40b31313db61f0c879f9bf19c3abb2783c6c372b5deb2396dab4432f2946b9972122ac682e77010376c029dfd0149c681 + languageName: node + linkType: hard + "@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" @@ -3253,6 +3310,26 @@ __metadata: languageName: node linkType: hard +"@testing-library/react-native@npm:^13.2.0": + version: 13.2.0 + resolution: "@testing-library/react-native@npm:13.2.0" + dependencies: + chalk: ^4.1.2 + jest-matcher-utils: ^29.7.0 + pretty-format: ^29.7.0 + redent: ^3.0.0 + peerDependencies: + jest: ">=29.0.0" + react: ">=18.2.0" + react-native: ">=0.71" + react-test-renderer: ">=18.2.0" + peerDependenciesMeta: + jest: + optional: true + checksum: fa2f59353b27a5afea72c04296e94e597b7e20f09f7e69d5dd1693da15bdeabfdd1ba655b1e194dbf5727b9dfa92de5af06ec77484b3392aab96dab4f7c05e44 + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -3480,6 +3557,17 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/project-service@npm:8.33.0" + dependencies: + "@typescript-eslint/tsconfig-utils": ^8.33.0 + "@typescript-eslint/types": ^8.33.0 + debug: ^4.3.4 + checksum: efb90f2fea9d6e0af9eeec34161002862e1f8a7a63dcecac4d57d81fa80319da06f0a34ad897ea72d66e975af8076026f16e1a7b111a46c2a0d49f0401794058 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/scope-manager@npm:5.62.0" @@ -3500,6 +3588,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.33.0, @typescript-eslint/scope-manager@npm:^8.15.0": + version: 8.33.0 + resolution: "@typescript-eslint/scope-manager@npm:8.33.0" + dependencies: + "@typescript-eslint/types": 8.33.0 + "@typescript-eslint/visitor-keys": 8.33.0 + checksum: e5a82f102a9e9671bcbc64493e1f0788cc251c40516c12cb904ab5b9aa36b62c3c86b9e2ee2b1f267591764bddd1c6744d3b1144b84da9cbb71e9f879f068519 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.33.0, @typescript-eslint/tsconfig-utils@npm:^8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.33.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 5bb139be996a16f65c012c083e4c0dc2ddafd1295940203e6c2a1ac9fa0718b1a91f74354f162d3d9614b013e062863414d4478c57ffbf78dfd7cb4f5701abde + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:7.18.0": version: 7.18.0 resolution: "@typescript-eslint/type-utils@npm:7.18.0" @@ -3531,6 +3638,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.33.0, @typescript-eslint/types@npm:^8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/types@npm:8.33.0" + checksum: 3fa8c4598960c93e4f002d0d62c39072617b58808af88237b87d26a506576fd33cf5822505128575cf3c817257d7ee08a696f015369f6958303c2e73a1c83fc5 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" @@ -3568,6 +3682,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.33.0" + dependencies: + "@typescript-eslint/project-service": 8.33.0 + "@typescript-eslint/tsconfig-utils": 8.33.0 + "@typescript-eslint/types": 8.33.0 + "@typescript-eslint/visitor-keys": 8.33.0 + debug: ^4.3.4 + fast-glob: ^3.3.2 + is-glob: ^4.0.3 + minimatch: ^9.0.4 + semver: ^7.6.0 + ts-api-utils: ^2.1.0 + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 365de7456d593c4728f81e01c318b76f7e954246c4703e01a27532e756a2854d859ccbf13562bcadba34f02ed202cd04dd88ea841b9e38a8a70ce4bc9e5fc731 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:7.18.0": version: 7.18.0 resolution: "@typescript-eslint/utils@npm:7.18.0" @@ -3600,6 +3734,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^8.15.0": + version: 8.33.0 + resolution: "@typescript-eslint/utils@npm:8.33.0" + dependencies: + "@eslint-community/eslint-utils": ^4.7.0 + "@typescript-eslint/scope-manager": 8.33.0 + "@typescript-eslint/types": 8.33.0 + "@typescript-eslint/typescript-estree": 8.33.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: e654ceac6afaada23a6841c23da848db89bb1980f96af900cdd5f14805508ed7c012bdd3747c8ce038d0f2747db4ea71e9a3165d3e2745fad61a09c224a419ce + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" @@ -3620,6 +3769,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.33.0" + dependencies: + "@typescript-eslint/types": 8.33.0 + eslint-visitor-keys: ^4.2.0 + checksum: c92eacedd97c9baa742e747d5ff7251616456621fcbb5ebf09a1981718778fbdb30a19dbe36e75af437780345bd0617ebcb5ff57b458a442a60b5d5430f8f836 + languageName: node + linkType: hard + "@urql/core@npm:^5.0.6, @urql/core@npm:^5.1.1": version: 5.1.1 resolution: "@urql/core@npm:5.1.1" @@ -3895,6 +4054,16 @@ __metadata: languageName: node linkType: hard +"arktype@npm:^2.1.15": + version: 2.1.20 + resolution: "arktype@npm:2.1.20" + dependencies: + "@ark/schema": 0.46.0 + "@ark/util": 0.46.0 + checksum: 5c02dda98606b83b35bbc66934259e3f30c4b4486c32e470e199da533c0af568951502173d7d7a5e64a2e53667eb36d10d772ce46c0bff204fab759430614c9b + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" @@ -4212,6 +4381,15 @@ __metadata: languageName: node linkType: hard +"babel-plugin-syntax-hermes-parser@npm:^0.28.0": + version: 0.28.1 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.28.1" + dependencies: + hermes-parser: 0.28.1 + checksum: 2cbc921e663463480ead9ccc8bb229a5196032367ba2b5ccb18a44faa3afa84b4dc493297749983b9a837a3d76b0b123664aecc06f9122618c3246f03e076a9d + languageName: node + linkType: hard + "babel-plugin-transform-flow-enums@npm:^0.0.2": version: 0.0.2 resolution: "babel-plugin-transform-flow-enums@npm:0.0.2" @@ -6061,7 +6239,7 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:5.0.0": +"escape-string-regexp@npm:5.0.0, escape-string-regexp@npm:^5.0.0": version: 5.0.0 resolution: "escape-string-regexp@npm:5.0.0" checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e @@ -6247,6 +6425,18 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-testing-library@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-plugin-testing-library@npm:7.2.2" + dependencies: + "@typescript-eslint/scope-manager": ^8.15.0 + "@typescript-eslint/utils": ^8.15.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: 3848ffd48cade9a5fb1785eeb89b45595b3adc32c8bdc819b234af01526766580bc01fb735ce2409385ab29724d98e8c3e09282260215e6357e81c035898231b + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -6680,7 +6870,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2, fast-glob@npm:^3.3.3": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -7548,6 +7738,13 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.28.1": + version: 0.28.1 + resolution: "hermes-estree@npm:0.28.1" + checksum: 4f7b4e0491352012a6cb799315a0aae16abdcc894335e901552ee6c64732d0cf06f0913c579036f9f452b7c4ad9bb0b6ab14e510c13bd7e5997385f77633ab00 + languageName: node + linkType: hard + "hermes-parser@npm:0.23.1": version: 0.23.1 resolution: "hermes-parser@npm:0.23.1" @@ -7566,6 +7763,15 @@ __metadata: languageName: node linkType: hard +"hermes-parser@npm:0.28.1": + version: 0.28.1 + resolution: "hermes-parser@npm:0.28.1" + dependencies: + hermes-estree: 0.28.1 + checksum: 0d95280d527e1ad46e8caacd56b24d07e4aec39704de86cf164600f2c4fb00f406dd74a37b2103433ef7ec388a549072da20438e224bd47def21f973c36aab7d + languageName: node + linkType: hard + "hosted-git-info@npm:^4.0.1": version: 4.1.0 resolution: "hosted-git-info@npm:4.1.0" @@ -10304,7 +10510,7 @@ __metadata: languageName: node linkType: hard -"min-indent@npm:^1.0.1": +"min-indent@npm:^1.0.0, min-indent@npm:^1.0.1": version: 1.0.1 resolution: "min-indent@npm:1.0.1" checksum: bfc6dd03c5eaf623a4963ebd94d087f6f4bbbfd8c41329a7f09706b0cb66969c4ddd336abeb587bc44bc6f08e13bf90f0b374f9d71f9f01e04adc2cd6f083ef1 @@ -11730,6 +11936,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.3.1": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: e20fe84c86ff172fc8d898251b7cc2c43645d108bf96d0b8edf39b98f9a2cae97b40520ee7ed8ee0085ccc94736c4886294456033304151c3f94978cec03df21 + languageName: node + linkType: hard + "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -11737,13 +11950,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: e20fe84c86ff172fc8d898251b7cc2c43645d108bf96d0b8edf39b98f9a2cae97b40520ee7ed8ee0085ccc94736c4886294456033304151c3f94978cec03df21 - languageName: node - linkType: hard - "react-native-builder-bob@npm:^0.39.0": version: 0.39.0 resolution: "react-native-builder-bob@npm:0.39.0" @@ -11775,6 +11981,39 @@ __metadata: languageName: node linkType: hard +"react-native-builder-bob@npm:^0.40.11": + version: 0.40.11 + resolution: "react-native-builder-bob@npm:0.40.11" + dependencies: + "@babel/core": ^7.25.2 + "@babel/plugin-transform-flow-strip-types": ^7.26.5 + "@babel/plugin-transform-strict-mode": ^7.24.7 + "@babel/preset-env": ^7.25.2 + "@babel/preset-react": ^7.24.7 + "@babel/preset-typescript": ^7.24.7 + arktype: ^2.1.15 + babel-plugin-syntax-hermes-parser: ^0.28.0 + browserslist: ^4.20.4 + cross-spawn: ^7.0.3 + dedent: ^0.7.0 + del: ^6.1.1 + escape-string-regexp: ^4.0.0 + fs-extra: ^10.1.0 + glob: ^8.0.3 + is-git-dirty: ^2.0.1 + json5: ^2.2.1 + kleur: ^4.1.4 + metro-config: ^0.80.9 + prompts: ^2.4.2 + react-native-monorepo-config: ^0.1.8 + which: ^2.0.2 + yargs: ^17.5.1 + bin: + bob: bin/bob + checksum: a165c284bb9a938238c7fee526399b06d831dc6ce01762db466f79f768708536f34f0c01e01f49fae732bc160d6198a5499200fdb254b317ce04b69e1c814fdc + languageName: node + linkType: hard + "react-native-input-code-otp-example@workspace:example": version: 0.0.0-use.local resolution: "react-native-input-code-otp-example@workspace:example" @@ -11802,6 +12041,7 @@ __metadata: "@evilmartians/lefthook": ^1.5.0 "@react-native/eslint-config": ^0.78.0 "@release-it/conventional-changelog": ^9.0.2 + "@testing-library/react-native": ^13.2.0 "@types/jest": ^29.5.5 "@types/react": ^19.0.12 commitlint: ^19.6.1 @@ -11809,11 +12049,13 @@ __metadata: eslint: ^9.22.0 eslint-config-prettier: ^10.1.1 eslint-plugin-prettier: ^5.2.3 + eslint-plugin-testing-library: ^7.2.2 jest: ^29.7.0 prettier: ^3.0.3 react: 18.3.1 react-native: 0.76.8 - react-native-builder-bob: ^0.39.0 + react-native-builder-bob: ^0.40.11 + react-test-renderer: 18.3.1 release-it: ^17.10.0 typescript: ^5.2.2 peerDependencies: @@ -11822,6 +12064,16 @@ __metadata: languageName: unknown linkType: soft +"react-native-monorepo-config@npm:^0.1.8": + version: 0.1.9 + resolution: "react-native-monorepo-config@npm:0.1.9" + dependencies: + escape-string-regexp: ^5.0.0 + fast-glob: ^3.3.3 + checksum: 6356c362c517c49e17d54ee764c3566ba71491fa0d755618ecf2ca548348668e84fe448c24066645983acbc2bd4c0ed47594f9b3ec9dcc0558c0fd9594d2391e + languageName: node + linkType: hard + "react-native-web@npm:~0.19.13": version: 0.19.13 resolution: "react-native-web@npm:0.19.13" @@ -11902,6 +12154,31 @@ __metadata: languageName: node linkType: hard +"react-shallow-renderer@npm:^16.15.0": + version: 16.15.0 + resolution: "react-shallow-renderer@npm:16.15.0" + dependencies: + object-assign: ^4.1.1 + react-is: ^16.12.0 || ^17.0.0 || ^18.0.0 + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 6052c7e3e9627485120ebd8257f128aad8f56386fe8d42374b7743eac1be457c33506d153c7886b4e32923c0c352d402ab805ef9ca02dbcd8393b2bdeb6e5af8 + languageName: node + linkType: hard + +"react-test-renderer@npm:18.3.1": + version: 18.3.1 + resolution: "react-test-renderer@npm:18.3.1" + dependencies: + react-is: ^18.3.1 + react-shallow-renderer: ^16.15.0 + scheduler: ^0.23.2 + peerDependencies: + react: ^18.3.1 + checksum: e8e58e738835fab3801afb63f6bfe0fcf6e68ea39619fae5bdf47feefc36b1e4acb48c9dd139c7533611466eff1dfce6ffdda4b317e06aee663dda7d91438f26 + languageName: node + linkType: hard + "react@npm:18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -12012,6 +12289,16 @@ __metadata: languageName: node linkType: hard +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: ^4.0.0 + strip-indent: ^3.0.0 + checksum: fa1ef20404a2d399235e83cc80bd55a956642e37dd197b4b612ba7327bf87fa32745aeb4a1634b2bab25467164ab4ed9c15be2c307923dd08b0fe7c52431ae6b + languageName: node + linkType: hard + "redent@npm:^4.0.0": version: 4.0.0 resolution: "redent@npm:4.0.0" @@ -13236,6 +13523,15 @@ __metadata: languageName: node linkType: hard +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: ^1.0.0 + checksum: 18f045d57d9d0d90cd16f72b2313d6364fd2cb4bf85b9f593523ad431c8720011a4d5f08b6591c9d580f446e78855c5334a30fb91aa1560f5d9f95ed1b4a0530 + languageName: node + linkType: hard + "strip-indent@npm:^4.0.0": version: 4.0.0 resolution: "strip-indent@npm:4.0.0" @@ -13579,6 +13875,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "ts-api-utils@npm:2.1.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 5b1ef89105654d93d67582308bd8dfe4bbf6874fccbcaa729b08fbb00a940fd4c691ca6d0d2b18c3c70878d9a7e503421b7cc473dbc3d0d54258b86401d4b15d + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" From dbbf7ca869872bb68c8408fd37fe5f4808c8df3e Mon Sep 17 00:00:00 2001 From: johelder Date: Sun, 15 Jun 2025 17:29:19 -0300 Subject: [PATCH 2/6] test: add test for caretHidden prop - add test case to ensure caret is not rendered when caretHidden is true - add testID to Caret component for easier testing --- src/__tests__/text-input-otp.test.tsx | 6 +++++- src/components/caret.tsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx index 8ef2190..d51538d 100644 --- a/src/__tests__/text-input-otp.test.tsx +++ b/src/__tests__/text-input-otp.test.tsx @@ -25,7 +25,11 @@ describe('TextInputOTP Component', () => { const mockedOnFilled = jest.fn(); const view = render({ onFilled: mockedOnFilled }); fireEvent.changeText(view.getByTestId('hidden-text-input'), CODE); - expect(mockedOnFilled).toHaveBeenCalledWith(CODE); }); + + it('should not render the animated caret when caretHidden prop is true', () => { + const view = render({ caretHidden: true }); + expect(view.queryByTestId('caret')).toBeNull(); + }); }); diff --git a/src/components/caret.tsx b/src/components/caret.tsx index 202ab33..a41354f 100644 --- a/src/components/caret.tsx +++ b/src/components/caret.tsx @@ -26,6 +26,7 @@ export function Caret() { return ( Date: Sun, 15 Jun 2025 18:47:29 -0300 Subject: [PATCH 3/6] test(text-input-otp): add test for maxLength prop and improve existing tests - refactor render function to destructure props with default maxLength - add test to verify slots rendered match maxLength prop - add testID to TextInputOTPSlot component for easier testing - improve existing test descriptions for clarity --- src/__tests__/text-input-otp.test.tsx | 13 ++++++++++--- src/components/text-input-otp-slot.tsx | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx index d51538d..9a37fde 100644 --- a/src/__tests__/text-input-otp.test.tsx +++ b/src/__tests__/text-input-otp.test.tsx @@ -2,9 +2,9 @@ import { fireEvent, render as TLRender } from '@testing-library/react-native'; import { TextInputOTP, TextInputOTPSlot } from '../components'; import type { TextInputOTPProps } from '../types'; -function render(props?: Partial) { +function render({ maxLength = 6, ...rest }: Partial) { return TLRender( - + @@ -28,8 +28,15 @@ describe('TextInputOTP Component', () => { expect(mockedOnFilled).toHaveBeenCalledWith(CODE); }); - it('should not render the animated caret when caretHidden prop is true', () => { + it('should not render the animated caret when the caretHidden prop is true', () => { const view = render({ caretHidden: true }); expect(view.queryByTestId('caret')).toBeNull(); }); + + it('should render the slots only up to the number defined by the maxLength prop', async () => { + const MAX_LENGTH = 6; + const view = render({ maxLength: MAX_LENGTH, caretHidden: true }); + const slots = view.getAllByTestId('text-input-otp-slot'); + expect(slots).toHaveLength(MAX_LENGTH); + }); }); diff --git a/src/components/text-input-otp-slot.tsx b/src/components/text-input-otp-slot.tsx index f18dcf4..00a8054 100644 --- a/src/components/text-input-otp-slot.tsx +++ b/src/components/text-input-otp-slot.tsx @@ -26,6 +26,7 @@ function TextInputOTPSlotComponent({ return ( Date: Sun, 15 Jun 2025 18:48:03 -0300 Subject: [PATCH 4/6] test: remove async from test case removed unnecessary async keyword from the test case that checks if the component renders slots up to the maxLength prop --- src/__tests__/text-input-otp.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx index 9a37fde..12f969c 100644 --- a/src/__tests__/text-input-otp.test.tsx +++ b/src/__tests__/text-input-otp.test.tsx @@ -33,7 +33,7 @@ describe('TextInputOTP Component', () => { expect(view.queryByTestId('caret')).toBeNull(); }); - it('should render the slots only up to the number defined by the maxLength prop', async () => { + it('should render the slots only up to the number defined by the maxLength prop', () => { const MAX_LENGTH = 6; const view = render({ maxLength: MAX_LENGTH, caretHidden: true }); const slots = view.getAllByTestId('text-input-otp-slot'); From 371889e79eda98e5afb171115f3fcd0cebffb70d Mon Sep 17 00:00:00 2001 From: johelder Date: Sun, 15 Jun 2025 19:22:00 -0300 Subject: [PATCH 5/6] test(text-input-otp): add test for setValue method and update existing tests - add test case for setValue method - update renderTextInputOTP function - import act and createRef - add onFilled callback in setValue method --- src/__tests__/text-input-otp.test.tsx | 38 ++++++++++++++++++++++----- src/hooks/use-text-input-otp.tsx | 1 + 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx index 12f969c..7ecf54b 100644 --- a/src/__tests__/text-input-otp.test.tsx +++ b/src/__tests__/text-input-otp.test.tsx @@ -1,9 +1,13 @@ -import { fireEvent, render as TLRender } from '@testing-library/react-native'; +import { act, fireEvent, render } from '@testing-library/react-native'; import { TextInputOTP, TextInputOTPSlot } from '../components'; -import type { TextInputOTPProps } from '../types'; +import type { TextInputOTPProps, TextInputOTPRef } from '../types'; +import { createRef } from 'react'; -function render({ maxLength = 6, ...rest }: Partial) { - return TLRender( +function renderTextInputOTP({ + maxLength = 6, + ...rest +}: Partial) { + return render( @@ -23,20 +27,40 @@ describe('TextInputOTP Component', () => { it('should call onFilled with the complete code when all digits are filled', () => { const CODE = '123456'; const mockedOnFilled = jest.fn(); - const view = render({ onFilled: mockedOnFilled }); + const view = renderTextInputOTP({ onFilled: mockedOnFilled }); fireEvent.changeText(view.getByTestId('hidden-text-input'), CODE); expect(mockedOnFilled).toHaveBeenCalledWith(CODE); }); it('should not render the animated caret when the caretHidden prop is true', () => { - const view = render({ caretHidden: true }); + const view = renderTextInputOTP({ caretHidden: true }); expect(view.queryByTestId('caret')).toBeNull(); }); it('should render the slots only up to the number defined by the maxLength prop', () => { const MAX_LENGTH = 6; - const view = render({ maxLength: MAX_LENGTH, caretHidden: true }); + const view = renderTextInputOTP({ + maxLength: MAX_LENGTH, + caretHidden: true, + }); const slots = view.getAllByTestId('text-input-otp-slot'); expect(slots).toHaveLength(MAX_LENGTH); }); + + it('should call onFilled with the complete code when setValue is called programmatically', () => { + const CODE = '123'; + const mockedOnFilled = jest.fn(); + const ref = createRef(); + render( + + + + + + ); + + act(() => ref.current?.setValue(CODE)); + + expect(mockedOnFilled).toHaveBeenCalledWith(CODE); + }); }); diff --git a/src/hooks/use-text-input-otp.tsx b/src/hooks/use-text-input-otp.tsx index a3ab63b..25e6efa 100644 --- a/src/hooks/use-text-input-otp.tsx +++ b/src/hooks/use-text-input-otp.tsx @@ -70,6 +70,7 @@ export function TextInputOTPProvider({ text.length > maxLength ? text.slice(0, maxLength) : text; setCode(filledCode); setCurrentIndex(maxLength - 1); + onFilled?.(filledCode); }, [maxLength] ); From 317e6169d1ec01aa5fcf4dff9542cff0f48af5cb Mon Sep 17 00:00:00 2001 From: johelder Date: Sun, 15 Jun 2025 19:56:11 -0300 Subject: [PATCH 6/6] test(text-input-otp): add test for clear function - add test case to verify that the clear function clears input text when called programmatically - ensure that the cleared text is not visible in the component after calling clear --- src/__tests__/text-input-otp.test.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/__tests__/text-input-otp.test.tsx b/src/__tests__/text-input-otp.test.tsx index 7ecf54b..decd2b9 100644 --- a/src/__tests__/text-input-otp.test.tsx +++ b/src/__tests__/text-input-otp.test.tsx @@ -58,9 +58,22 @@ describe('TextInputOTP Component', () => { ); - act(() => ref.current?.setValue(CODE)); - expect(mockedOnFilled).toHaveBeenCalledWith(CODE); }); + + it('should call clear the input text when clear function is called programmatically', () => { + const CODE = '1'; + const ref = createRef(); + const view = render( + + + + + + ); + fireEvent.changeText(view.getByTestId('hidden-text-input'), CODE); + act(() => ref.current?.clear()); + expect(view.queryByText(CODE)).toBeFalsy(); + }); });