{"_id":"@astacinco/rn-testing","name":"@astacinco/rn-testing","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@astacinco/rn-testing","version":"0.1.0","description":"Testing utilities for React Native with theme support","main":"src/index.ts","types":"src/index.ts","repository":{"type":"git","url":"git+https://github.com/jrudydev/rn-toolkit.git","directory":"packages/testing"},"publishConfig":{"access":"public"},"author":{"name":"Spark Labs","email":"hello@sparklabs.dev"},"homepage":"https://github.com/jrudydev/rn-toolkit#readme","scripts":{"build":"tsc","test":"jest","lint":"eslint src --ext .ts,.tsx"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.72.0","@testing-library/react-native":">=12.0.0"},"dependencies":{"@astacinco/rn-theming":"*"},"devDependencies":{"@testing-library/react-native":"^12.4.3","@types/react":"^18.2.48","react":"18.2.0","react-native":"0.73.4","react-test-renderer":"18.2.0","typescript":"^5.3.3"},"keywords":["react-native","testing","theme","snapshot","test-utils"],"license":"MIT","_id":"@astacinco/rn-testing@0.1.0","gitHead":"6fa965625ad344ec91902e42e12b2000624373ea","bugs":{"url":"https://github.com/jrudydev/rn-toolkit/issues"},"_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-lury+4MO3/WSFOMXb9Lfk12e5wm6QsO4HCESdaSFTz1kusR8UbdoqkL4dhYPs/Im2p9SHfZ6dzJhmHLC0RVa3A==","shasum":"e3d1de0b9a56923f095d8261cca8de4e19890a30","tarball":"https://registry.npmjs.org/@astacinco/rn-testing/-/rn-testing-0.1.0.tgz","fileCount":13,"unpackedSize":33746,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF+OWg8DtyzKQidu8fZYyOzSYWzaNfnESIB5H60zBkapAiEAhAJPzVaz18D9Gyv7qXVFGJr9om9IbNcV3r6y+KH/bkM="}]},"_npmUser":{"name":"sparklabs343","email":"gomez.rudy@gmail.com"},"directories":{},"maintainers":[{"name":"sparklabs343","email":"gomez.rudy@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rn-testing_0.1.0_1772793183885_0.9505464920724058"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-06T10:33:03.812Z","0.1.0":"2026-03-06T10:33:04.044Z","modified":"2026-03-06T10:33:04.887Z"},"maintainers":[{"name":"sparklabs343","email":"gomez.rudy@gmail.com"}],"description":"Testing utilities for React Native with theme support","homepage":"https://github.com/jrudydev/rn-toolkit#readme","keywords":["react-native","testing","theme","snapshot","test-utils"],"repository":{"type":"git","url":"git+https://github.com/jrudydev/rn-toolkit.git","directory":"packages/testing"},"author":{"name":"Spark Labs","email":"hello@sparklabs.dev"},"bugs":{"url":"https://github.com/jrudydev/rn-toolkit/issues"},"license":"MIT","readme":"# @astacinco/rn-testing\n\nTesting utilities for React Native with automatic theme support.\n\n## Installation\n\n```bash\nnpm install @astacinco/rn-testing --save-dev\n```\n\n## Features\n\n- `renderWithTheme()` - Render components with ThemeProvider\n- `renderWithProviders()` - Render with all providers (theme, navigation, etc.)\n- `createThemeSnapshot()` - Snapshot both light and dark modes automatically\n- Mock utilities for navigation and SDUI\n\n## Quick Start\n\n```typescript\nimport { renderWithTheme, createThemeSnapshot } from '@astacinco/rn-testing';\nimport { MyComponent } from './MyComponent';\n\ndescribe('MyComponent', () => {\n  it('renders in light mode', () => {\n    const { getByTestId } = renderWithTheme(<MyComponent />, 'light');\n    expect(getByTestId('my-component')).toBeTruthy();\n  });\n\n  it('renders in dark mode', () => {\n    const { getByTestId } = renderWithTheme(<MyComponent />, 'dark');\n    expect(getByTestId('my-component')).toBeTruthy();\n  });\n\n  it('matches snapshots for both themes', () => {\n    createThemeSnapshot(<MyComponent />);\n  });\n});\n```\n\n## API Reference\n\n### renderWithTheme(component, mode?)\n\nRenders a component wrapped in ThemeProvider.\n\n```typescript\nconst { getByTestId, toJSON } = renderWithTheme(\n  <Button label=\"Click me\" />,\n  'dark' // optional, defaults to 'light'\n);\n```\n\n**Parameters:**\n- `component` - React element to render\n- `mode` - Theme mode: `'light'` | `'dark'` (default: `'light'`)\n\n**Returns:** All utilities from `@testing-library/react-native`\n\n### renderWithProviders(component, options?)\n\nRenders with all providers configured.\n\n```typescript\nconst result = renderWithProviders(<Screen />, {\n  theme: 'dark',\n  initialRoute: '/home',\n});\n```\n\n**Options:**\n- `theme` - Theme mode: `'light'` | `'dark'`\n- `initialRoute` - Initial route for navigation testing\n\n### createThemeSnapshot(component)\n\nCreates snapshots for both light and dark themes.\n\n```typescript\ncreateThemeSnapshot(<Card title=\"Hello\" />);\n// Creates two snapshots:\n// - \"matches light theme snapshot\"\n// - \"matches dark theme snapshot\"\n```\n\n## Mocks\n\n### mockNavigation\n\n```typescript\nimport { mockNavigation } from '@astacinco/rn-testing';\n\nconst { navigate, back, currentRoute } = mockNavigation();\n\n// Simulate navigation\nnavigate('/profile/123');\nexpect(currentRoute()).toBe('/profile/123');\n\nback();\nexpect(currentRoute()).toBe('/home');\n```\n\n### mockSDUISchema\n\n```typescript\nimport { mockSDUISchema } from '@astacinco/rn-testing';\n\nconst schema = mockSDUISchema({\n  type: 'screen',\n  children: [\n    { type: 'text', props: { content: 'Hello' } },\n  ],\n});\n```\n\n## Best Practices\n\n### Always Test Both Themes\n\n```typescript\ndescribe('ProfileCard', () => {\n  it('renders correctly in light mode', () => {\n    const { toJSON } = renderWithTheme(<ProfileCard />, 'light');\n    expect(toJSON()).toMatchSnapshot();\n  });\n\n  it('renders correctly in dark mode', () => {\n    const { toJSON } = renderWithTheme(<ProfileCard />, 'dark');\n    expect(toJSON()).toMatchSnapshot();\n  });\n});\n```\n\n### Or Use createThemeSnapshot\n\n```typescript\ndescribe('ProfileCard', () => {\n  createThemeSnapshot(<ProfileCard user={mockUser} />);\n});\n```\n\n### Naming Convention\n\nUse descriptive test names:\n```typescript\nit('renders_title_whenUserProvided', () => { ... });\nit('shows_placeholder_whenNoAvatar', () => { ... });\n```\n\n---\n\n## DSL (Fluent Testing API) - PAID TIER\n\nThe DSL provides a fluent, chainable API for expressive test writing with significant boilerplate reduction.\n\n### Entry Point\n\n```typescript\nimport { dsl } from '@astacinco/rn-testing';\n\ndsl.component(<Button />)    // Component testing\ndsl.matrix(<Button />)       // Variant matrix testing\ndsl.hook(() => useAuth())    // Hook testing\ndsl.sdui(schema)             // SDUI testing\n```\n\n### Component Testing\n\n```typescript\n// Render with assertions\ndsl.component(<Button label=\"Click\" onPress={fn} />)\n   .inTheme('dark')\n   .render()\n   .assert()\n   .visible('button')\n   .text('Click')\n   .enabled('button')\n   .done();\n\n// Quick snapshot both themes\ndsl.component(<Card><Text>Hello</Text></Card>)\n   .snapshot('Card-basic');\n\n// With props\ndsl.component(<Button label=\"Test\" onPress={fn} />)\n   .withProps({ variant: 'primary', size: 'lg' })\n   .inDarkMode()\n   .render()\n   .assert()\n   .visible('button')\n   .done();\n\n// With additional providers\ndsl.component(<ProtectedScreen />)\n   .withProvider(AuthProvider, { user: mockUser })\n   .render();\n```\n\n### Matrix Testing (All Combinations)\n\nGenerate multiple tests from variant combinations automatically:\n\n```typescript\n// Tests: 4 variants × 3 sizes × 2 themes = 24 snapshots!\ndsl.matrix(<Button label=\"Test\" onPress={fn} />)\n   .withVariants('variant', ['primary', 'secondary', 'outline', 'ghost'])\n   .withVariants('size', ['sm', 'md', 'lg'])\n   .inThemes(['light', 'dark'])\n   .snapshotAll();\n\n// Filter specific combinations\ndsl.matrix(<Card />)\n   .withVariants('variant', ['elevated', 'outlined', 'filled'])\n   .withVariants('padding', ['sm', 'md', 'lg'])\n   .filter(({ variant, padding }) => {\n     // Skip elevated with large padding\n     return !(variant === 'elevated' && padding === 'lg');\n   })\n   .snapshotAll();\n\n// Get combinations for custom testing\nconst combinations = dsl.matrix(<Button label=\"Test\" onPress={fn} />)\n   .withVariants('variant', ['primary', 'secondary'])\n   .withVariants('disabled', [true, false])\n   .getCombinations();\n\ncombinations.forEach(({ props, name }) => {\n   it(name, () => { /* custom test logic */ });\n});\n```\n\n### Hook Testing\n\n```typescript\n// Basic hook testing\nconst result = dsl.hook(() => useCounter())\n   .withTheme('light')\n   .render();\n\nresult.expectValue('count', 0)\n      .expectFunction('increment', 'decrement');\n\n// With providers\nconst authResult = await dsl.hook(() => useAuth())\n   .withProvider(AuthProvider, { adapter: mockAdapter })\n   .renderAndWait();\n\nauthResult.expectDefined('user', 'isLoading')\n          .expectFunction('signIn', 'signOut');\n\n// Wait for specific values\nawait dsl.hook(() => useAsync())\n   .renderUntil('isLoading', false);\n\n// Both themes\nconst { light, dark } = dsl.hook(() => useThemeAware())\n   .renderBothThemes();\n```\n\n### SDUI Testing\n\n```typescript\n// Test existing schema\ndsl.sdui(profileSchema)\n   .withRenderer(SDUIRenderer)\n   .withActionHandler(mockHandler)\n   .render()\n   .expectNodeCount('button', 2)\n   .expectNodeCount('text', 5);\n\n// Build schema programmatically\ndsl.emptySDUI()\n   .addNode(SDUIBuilder.text('Hello World', 'greeting'))\n   .addNode(SDUIBuilder.button('Click Me', SDUIBuilder.navigate('/home')))\n   .withRenderer(SDUIRenderer)\n   .render();\n\n// Static node builders\nconst textNode = SDUIBuilder.text('Welcome', 'welcome-text');\nconst buttonNode = SDUIBuilder.button('Submit', SDUIBuilder.api('/submit', 'POST'));\nconst cardNode = SDUIBuilder.card([textNode, buttonNode]);\nconst listNode = SDUIBuilder.list('users', SDUIBuilder.navigate('/user/:id'));\n```\n\n### Assertion Chain\n\nAll assertion methods are chainable:\n\n```typescript\ndsl.component(<Form />)\n   .render()\n   .assert()\n   .visible('form')\n   .visible('email-input')\n   .visible('password-input')\n   .text('Submit')\n   .enabled('submit-button')\n   .count('input', 2)\n   .accessible('submit-button')\n   .and  // alias for chaining\n   .notVisible('error-message')\n   .noText('Invalid')\n   .done();\n```\n\n### Theme Assertions\n\n```typescript\nimport { expectThemeDifference, expectColorDifference, expectAccessibility } from '@astacinco/rn-testing';\n\n// Assert component looks different in each theme\nexpectThemeDifference(<Card title=\"Test\" />);\n\n// Assert specific color changes\nexpectColorDifference(<Button label=\"Click\" />, 'backgroundColor', {\n  light: '#FFFFFF',\n  dark: '#1A1A1A',\n});\n\n// Check accessibility\nexpectAccessibility(<Button label=\"Submit\" />, ['hasAccessibleLabel', 'hasRole']);\n```\n\n### Provider Presets\n\nCreate reusable provider configurations:\n\n```typescript\nimport { createProviderConfig, combineProviders, createWrapperFromConfigs } from '@astacinco/rn-testing';\n\n// Create preset\nconst authPreset = createProviderConfig(AuthProvider, {\n  adapter: new NoOpAdapter(),\n});\n\nconst analyticsPreset = createProviderConfig(AnalyticsProvider, {\n  adapter: new ConsoleAdapter(),\n});\n\n// Combine presets\nconst allProviders = combineProviders(authPreset, analyticsPreset);\n\n// Use with hooks\ndsl.hook(() => useAuth())\n   .withProviders(allProviders)\n   .render();\n\n// Or create wrapper for renderHook\nconst wrapper = createWrapperFromConfigs(allProviders);\nconst result = renderHook(() => useAuth(), { wrapper });\n```\n\n### Value Proposition\n\n| Feature | Boilerplate Saved |\n|---------|-------------------|\n| Matrix Testing | 20+ tests from 3 lines |\n| Theme Snapshots | Auto-generates both themes |\n| Fluent Assertions | Readable, chainable checks |\n| Provider Presets | No manual wrapper creation |\n| SDUI Testing | First-class schema support |\n","readmeFilename":"README.md","_rev":"1-a52301b4057b081f0f1cd1104f0f9ab5"}