diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f01df01..2bdbb41 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -17,6 +17,43 @@ jobs: - run: npm install - run: npm test - run: npm run lint + - if: github.ref == 'refs/heads/master' && github.event_name == 'push' + run: npm run perf:baseline + - if: github.ref == 'refs/heads/master' && github.event_name == 'push' + uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + with: + path: .reassure + key: reassure-baseline-${{ github.sha }} + + performance: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - run: npm install + - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + id: cache-reassure + with: + path: .reassure + key: reassure-baseline-${{ github.sha }} + restore-keys: reassure-baseline- + # TODO uncomment after merge to master and remove cache-hit == 'true' below + # fail-on-cache-miss: true + - if: steps.cache-reassure.outputs.cache-hit == 'true' + run: npm run perf:compare + - if: steps.cache-reassure.outputs.cache-hit == 'true' + uses: hCaptcha/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + id: find_comment + with: + issue-number: ${{ github.event.pull_request.number }} + body-includes: Performance Comparison Report + - if: steps.cache-reassure.outputs.cache-hit == 'true' + uses: hCaptcha/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + with: + edit-mode: replace + body-path: .reassure/output.md + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} test: needs: build diff --git a/.gitignore b/.gitignore index 53f2598..f78c883 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules/* yarn.lock + +# Reassure performance testing files +.reassure/ diff --git a/__tests__/ConfirmHcaptcha.perf-test.js b/__tests__/ConfirmHcaptcha.perf-test.js new file mode 100644 index 0000000..1f9ee72 --- /dev/null +++ b/__tests__/ConfirmHcaptcha.perf-test.js @@ -0,0 +1,209 @@ +import React from 'react'; +import { measureRenders } from 'reassure'; +import { waitFor } from '@testing-library/react-native'; +import ConfirmHcaptcha from '../index'; + +describe('ConfirmHcaptcha Performance Tests', () => { + test('ConfirmHcaptcha initial render performance', async () => { + await measureRenders( + {}} + /> + ); + }); + + test('ConfirmHcaptcha render with all props', async () => { + await measureRenders( + {}} + /> + ); + }); + + test('ConfirmHcaptcha modal show/hide performance', async () => { + const TestComponent = () => { + const [show, setShow] = React.useState(false); + + return ( + <> + {}} + /> + + + ); + }; + + await measureRenders(, { + scenario: async () => { + // Simulate showing and hiding the modal + await waitFor(() => {}, { timeout: 100 }); + }, + }); + }); + + test('ConfirmHcaptcha render count stability - multiple renders', async () => { + // This test ensures render count doesn't increase with multiple renders + const Component = () => ( + {}} + /> + ); + + await measureRenders(, { + runs: 20, // More runs to detect render count issues + warmupRuns: 3, + }); + }); + + test('ConfirmHcaptcha minimal configuration', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha with safe area view', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha without safe area view', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha passive mode', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha light theme performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha dark theme performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('ConfirmHcaptcha contrast theme performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); +}); diff --git a/__tests__/Hcaptcha.perf-test.js b/__tests__/Hcaptcha.perf-test.js new file mode 100644 index 0000000..99f5b8a --- /dev/null +++ b/__tests__/Hcaptcha.perf-test.js @@ -0,0 +1,131 @@ +import React from 'react'; +import { measureRenders } from 'reassure'; +import { waitFor } from '@testing-library/react-native'; +import Hcaptcha from '../Hcaptcha'; + +describe('Hcaptcha Performance Tests', () => { + test('Hcaptcha initial render performance', async () => { + await measureRenders( + {}} + /> + ); + }); + + test('Hcaptcha render with all props', async () => { + await measureRenders( + {}} + /> + ); + }); + + test('Hcaptcha render with theme object', async () => { + const customTheme = { + palette: { + mode: 'dark', + primary: { main: '#26C6DA' }, + warn: { main: '#FF8A80' }, + text: { + heading: '#FAFAFA', + body: '#E0E0E0', + }, + }, + }; + + await measureRenders( + {}} + /> + ); + }); + + test('Hcaptcha render count stability - multiple renders', async () => { + // This test ensures render count doesn't increase with multiple renders + const Component = () => ( + {}} + /> + ); + + await measureRenders(, { + runs: 20, // More runs to detect render count issues + warmupRuns: 3, + }); + }); + + test('Hcaptcha invisible size performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('Hcaptcha compact size performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); + + test('Hcaptcha normal size performance', async () => { + await measureRenders( + {}} + />, + { + scenario: async () => { + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + }); +}); diff --git a/__tests__/Hcaptcha.test.js b/__tests__/Hcaptcha.test.js index 1d6990e..b237daa 100644 --- a/__tests__/Hcaptcha.test.js +++ b/__tests__/Hcaptcha.test.js @@ -94,54 +94,54 @@ describe('Hcaptcha snapshot tests', () => { describe('Theme', () => { const theme = { - "palette": { - "mode": "dark", - "primary": { - "main": "#26C6DA" + 'palette': { + 'mode': 'dark', + 'primary': { + 'main': '#26C6DA', }, - "warn": { - "main": "#FF8A80" + 'warn': { + 'main': '#FF8A80', + }, + 'text': { + 'heading': '#FAFAFA', + 'body': '#E0E0E0', }, - "text": { - "heading": "#FAFAFA", - "body": "#E0E0E0" - } }, - "component": { - "checkbox": { - "main": { - "fill": "#333333", - "border": "#F5F5F5" + 'component': { + 'checkbox': { + 'main': { + 'fill': '#333333', + 'border': '#F5F5F5', + }, + 'hover': { + 'fill': '#222222', }, - "hover": { - "fill": "#222222" - } }, - "modal": { - "main": { - "fill": "#222222" + 'modal': { + 'main': { + 'fill': '#222222', }, - "hover": { - "fill": "#333333" + 'hover': { + 'fill': '#333333', + }, + 'focus': { + 'outline': '#80DEEA', }, - "focus": { - "outline": "#80DEEA" - } }, - "textarea": { - "main": { - "fill": "#4F4F4F", - "border": "#828282" + 'textarea': { + 'main': { + 'fill': '#4F4F4F', + 'border': '#828282', }, - "focus": { - "fill": "#4F4F4F", - "outline": "#80DEEA" + 'focus': { + 'fill': '#4F4F4F', + 'outline': '#80DEEA', }, - "disabled": { - "fill": "#828282" - } - } - } + 'disabled': { + 'fill': '#828282', + }, + }, + }, }; [ @@ -168,4 +168,4 @@ describe('Hcaptcha snapshot tests', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/__tests__/render-count.perf-test.js b/__tests__/render-count.perf-test.js new file mode 100644 index 0000000..b091a3f --- /dev/null +++ b/__tests__/render-count.perf-test.js @@ -0,0 +1,199 @@ +import React from 'react'; +import { measureRenders } from 'reassure'; +import { waitFor } from '@testing-library/react-native'; +import Hcaptcha from '../Hcaptcha'; +import ConfirmHcaptcha from '../index'; + +describe('Render Count Monitoring Tests', () => { + // These tests specifically focus on ensuring render counts don't increase + // They serve as a baseline for performance optimization + + test('Hcaptcha render count baseline - should not increase', async () => { + // This establishes the baseline render count for Hcaptcha componen + const result = await measureRenders( + {}} + />, + { + runs: 15, + warmupRuns: 3, + removeOutliers: true, + } + ); + + // This test will fail if render count increases - only care about render coun + expect(result.meanCount).toBe(1); + }); + + test('ConfirmHcaptcha render count baseline - should not increase', async () => { + // This establishes the baseline render count for ConfirmHcaptcha componen + const result = await measureRenders( + {}} + />, + { + runs: 15, + warmupRuns: 3, + removeOutliers: true, + } + ); + + // This test will fail if render count increases - only care about render coun + expect(result.meanCount).toBe(1); + }); + + test('Hcaptcha prop changes should not cause excessive re-renders', async () => { + // Test that changing props doesn't cause unnecessary re-renders + const TestComponent = () => { + const [theme, setTheme] = React.useState('light'); + const [size, setSize] = React.useState('invisible'); + + React.useEffect(() => { + // Simulate prop changes + const timer = setTimeout(() => { + setTheme('dark'); + }, 100); + + const timer2 = setTimeout(() => { + setSize('compact'); + }, 200); + + return () => { + clearTimeout(timer); + clearTimeout(timer2); + }; + }, []); + + return ( + {}} + /> + ); + }; + + const result = await measureRenders(, { + runs: 10, + warmupRuns: 2, + }); + + // Should not render more than necessary for prop changes - only care about render coun + expect(result.meanCount).toBeLessThanOrEqual(3); + }); + + test('ConfirmHcaptcha modal state changes render count', async () => { + // Test modal show/hide doesn't cause excessive re-renders + const TestComponent = () => { + const [show, setShow] = React.useState(false); + + return ( + <> + {}} + /> + + + ); + }; + + const result = await measureRenders(, { + runs: 10, + warmupRuns: 2, + scenario: async () => { + // Simulate modal interactions + await waitFor(() => {}, { timeout: 200 }); + }, + }); + + // Modal state changes should be efficient - only care about render coun + expect(result.meanCount).toBeLessThanOrEqual(2); + }); + + test('Complex Hcaptcha configuration render count', async () => { + // Test with complex configuration to ensure no render count increase + const complexProps = { + siteKey: '00000000-0000-0000-0000-000000000000', + url: 'https://hcaptcha.com', + size: 'normal', + languageCode: 'en', + showLoading: true, + closableLoading: true, + loadingIndicatorColor: '#123456', + backgroundColor: 'rgba(0.1, 0.1, 0.1, 0.4)', + theme: { + palette: { + mode: 'dark', + primary: { main: '#26C6DA' }, + warn: { main: '#FF8A80' }, + }, + }, + rqdata: '{"complex": "data"}', + sentry: true, + jsSrc: 'https://js.hcaptcha.com/1/api.js', + endpoint: 'https://api.hcaptcha.com', + reportapi: 'https://accounts.hcaptcha.com', + assethost: 'https://newassets.hcaptcha.com', + imghost: 'https://imgs.hcaptcha.com', + host: 'complex-host', + debug: { complex: true, nested: { data: 'test' } }, + orientation: 'portrait', + phonePrefix: '44', + phoneNumber: '+441234567890', + onMessage: () => {}, + }; + + const result = await measureRenders(, { + runs: 12, + warmupRuns: 3, + }); + + // Complex configuration should not increase render count - only care about render coun + expect(result.meanCount).toBe(1); + }); + + test('Memory leak prevention - multiple mount/unmount cycles', async () => { + // Test that multiple mount/unmount cycles don't cause render count to increase + const TestComponent = ({ shouldRender }) => { + if (!shouldRender) {return null;} + + return ( + {}} + /> + ); + }; + + const result = await measureRenders( + , + { + runs: 8, + warmupRuns: 2, + scenario: async () => { + // Simulate mount/unmount cycles + await waitFor(() => {}, { timeout: 100 }); + }, + } + ); + + // Mount/unmount should not cause render count to increase - only care about render coun + expect(result.meanCount).toBe(1); + }); +}); diff --git a/package-lock.json b/package-lock.json index 2e6ec4b..6110bfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,8 @@ "react": "*", "react-native": "*", "react-native-modal": "*", - "react-native-webview": "*" + "react-native-webview": "*", + "reassure": "^1.4.0" }, "peerDependencies": { "react": "*", @@ -2022,6 +2023,68 @@ "dev": true, "license": "MIT" }, + "node_modules/@callstack/reassure-cli": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@callstack/reassure-cli/-/reassure-cli-1.4.0.tgz", + "integrity": "sha512-yX2b51qxi2+j2dbQud+UDZIsyd/kpHt1hXpI/E/l95Ku9XPB285tziED/639ew1LiUZk/7Q/G0KA9ccBxZGxIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@callstack/reassure-compare": "1.4.0", + "@callstack/reassure-logger": "1.4.0", + "chalk": "4.1.2", + "simple-git": "^3.27.0", + "yargs": "^17.7.2" + }, + "bin": { + "reassure": "lib/commonjs/bin.js" + } + }, + "node_modules/@callstack/reassure-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@callstack/reassure-compare/-/reassure-compare-1.4.0.tgz", + "integrity": "sha512-k7mJVvMepGT2jL4E+Gv42uwQllTtHZlUoWG7rDnnYVUCGmR6miICpqHAMOnKqM+T+jK9k5kFepSyqSWCkRxU2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@callstack/reassure-logger": "1.4.0", + "ts-markdown-builder": "0.4.1", + "ts-regex-builder": "^1.8.2", + "zod": "^3.24.2" + } + }, + "node_modules/@callstack/reassure-danger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@callstack/reassure-danger/-/reassure-danger-1.4.0.tgz", + "integrity": "sha512-Hh0G3atml45ChjOLDhHAmmwKZzlFXnAHDc8/OkvZjxx3nrzR8PSqq6tsdO1E43hr8PbUfyCcNiuSFn3AQPDM5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@callstack/reassure-logger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@callstack/reassure-logger/-/reassure-logger-1.4.0.tgz", + "integrity": "sha512-Ob1Na48fJz98XMiVbgTaQP8cTPUBgOWPv3j+c74qZIdNE7ufyd/qZrhZrhwhEoDNAqP1EcVumFVvSXUR/Z/p7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2" + } + }, + "node_modules/@callstack/reassure-measure": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@callstack/reassure-measure/-/reassure-measure-1.4.0.tgz", + "integrity": "sha512-RzqFRl00aJORoL4U2zWro+wpXjtRbC3SUxFqyq3e+nOgqcqryPHiWgjD5lvO4yI6nxiab7n3Suj+Gr7s1ISNRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@callstack/reassure-logger": "1.4.0", + "mathjs": "^13.2.3", + "pretty-format": "^29.7.0" + }, + "peerDependencies": { + "react": ">=18.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz", @@ -2606,6 +2669,23 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -4333,6 +4413,20 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "license": "MIT" }, + "node_modules/complex.js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz", + "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4553,6 +4647,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -4955,6 +5056,13 @@ "dev": true, "license": "MIT" }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5977,6 +6085,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -7133,6 +7255,13 @@ "node": ">= 0.4" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true, + "license": "MIT" + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -8101,6 +8230,30 @@ "node": ">= 0.4" } }, + "node_modules/mathjs": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-13.2.3.tgz", + "integrity": "sha512-I67Op0JU7gGykFK64bJexkSAmX498x0oybxfVXn1rroEMZTmfxppORhnk8mEUnPrbTfabDKCqvm18vJKMk2UJQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.25.7", + "complex.js": "^2.2.5", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "^4.3.7", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.2.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", @@ -9471,6 +9624,23 @@ "dev": true, "license": "BSD" }, + "node_modules/reassure": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/reassure/-/reassure-1.4.0.tgz", + "integrity": "sha512-eFTzcm+T3hp/wBic/7Gzssi5y0Jq0Sgx+CCnqttUM44b+D9ZUuH3p9CVzMBPnC5qWr0ps5kKSD1OW98Vgn5hPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@callstack/reassure-cli": "1.4.0", + "@callstack/reassure-compare": "1.4.0", + "@callstack/reassure-danger": "1.4.0", + "@callstack/reassure-measure": "1.4.0", + "import-local": "^3.2.0" + }, + "bin": { + "reassure": "lib/commonjs/bin/reassure.js" + } + }, "node_modules/recast": { "version": "0.23.11", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", @@ -9801,6 +9971,13 @@ "dev": true, "license": "MIT" }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "dev": true, + "license": "MIT" + }, "node_modules/selfsigned": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", @@ -10112,6 +10289,22 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -10501,6 +10694,13 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -10558,6 +10758,26 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-markdown-builder": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/ts-markdown-builder/-/ts-markdown-builder-0.4.1.tgz", + "integrity": "sha512-t3Xqhmgjmu9LtTqklZ9Y9E6v3lIMn9Ope0+eBCBbR844KFOXKyXBm4RSpRKxJXmkCLpnJMIvH1oXvU9H+okFeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/ts-regex-builder": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/ts-regex-builder/-/ts-regex-builder-1.8.2.tgz", + "integrity": "sha512-Y8HovHFheDKm/jgLIWSO8o81xA/I9O5AGc3/vNG1sVSskatOifr3SQzAsatBXGLjL3nYhQif1MpwQRS5GF8ADg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -10701,6 +10921,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz", + "integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -11087,6 +11317,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 632d3c6..6b59846 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,11 @@ "main": "index.js", "scripts": { "prepare": "husky", - "test": "jest", + "test": "jest --testPathIgnorePatterns=\\.perf-test\\.js$", "lint": "eslint .", - "example": "node __scripts__/generate-example.js" + "example": "node __scripts__/generate-example.js", + "perf:baseline": "reassure --baseline", + "perf:compare": "reassure --compare" }, "jest": { "preset": "react-native", @@ -58,7 +60,8 @@ "react": "*", "react-native": "*", "react-native-modal": "*", - "react-native-webview": "*" + "react-native-webview": "*", + "reassure": "^1.4.0" }, "dependencies": { "@babel/core": "^7.26.5", diff --git a/reassure.config.js b/reassure.config.js new file mode 100644 index 0000000..d69d1de --- /dev/null +++ b/reassure.config.js @@ -0,0 +1,14 @@ +import { configure } from 'reassure'; + +// Use environment variables for CI optimization +const runs = process.env.REASSURE_RUNS ? parseInt(process.env.REASSURE_RUNS, 10) : 10; +const warmupRuns = process.env.REASSURE_WARMUP_RUNS ? parseInt(process.env.REASSURE_WARMUP_RUNS, 10) : 2; + +configure({ + testingLibrary: 'react-native', + runs, + warmupRuns, + outputFile: '.reassure/current.perf', + verbose: !process.env.REASSURE_SILENT, // Use silent mode if REASSURE_SILENT is set + testMatch: ['**/*.perf-test.[jt]s?(x)'], +});