- Create __mocks__/ with react-native, expo-router, expo-constants, expo-status-bar, react-native-mmkv, @testing-library/react-native - Update vitest.config.ts with resolve aliases for all native modules - Add AuthScreen smoke test (3 tests) - Add HomeScreen smoke test (2 tests) - Add @types/react-test-renderer dev dependency - Total: 32 passing tests (27 store + 5 component)
79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
/**
|
|
* Lightweight @testing-library/react-native mock for Vitest.
|
|
* Uses react-test-renderer under the hood with simple query helpers.
|
|
*/
|
|
import React from 'react';
|
|
import renderer, { type ReactTestRendererJSON } from 'react-test-renderer';
|
|
|
|
function collectTexts(node: ReactTestRendererJSON | ReactTestRendererJSON[] | string | null): string[] {
|
|
if (node === null) return [];
|
|
if (typeof node === 'string') return [node];
|
|
if (Array.isArray(node)) return node.flatMap(collectTexts);
|
|
|
|
const texts: string[] = [];
|
|
if (node.children) {
|
|
for (const child of node.children) {
|
|
texts.push(...collectTexts(child));
|
|
}
|
|
}
|
|
return texts;
|
|
}
|
|
|
|
function collectNodes(node: ReactTestRendererJSON | ReactTestRendererJSON[] | null): ReactTestRendererJSON[] {
|
|
if (node === null) return [];
|
|
if (Array.isArray(node)) return node.flatMap(collectNodes);
|
|
|
|
const nodes: ReactTestRendererJSON[] = [node];
|
|
if (node.children) {
|
|
for (const child of node.children) {
|
|
if (typeof child !== 'string') {
|
|
nodes.push(...collectNodes(child));
|
|
}
|
|
}
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
let currentTree: ReturnType<typeof renderer.create> | null = null;
|
|
|
|
export function render(element: React.ReactElement) {
|
|
currentTree = renderer.create(element);
|
|
return { unmount: () => currentTree?.unmount() };
|
|
}
|
|
|
|
export const screen = {
|
|
getByText(text: string) {
|
|
const json = currentTree?.toJSON();
|
|
const allTexts = collectTexts(json ?? null);
|
|
const found = allTexts.find((t) => t.includes(text));
|
|
if (!found) throw new Error(`Unable to find text: "${text}"`);
|
|
return { type: 'Text', children: [found] };
|
|
},
|
|
|
|
getByPlaceholderText(placeholder: string) {
|
|
const json = currentTree?.toJSON();
|
|
const allNodes = collectNodes(json ?? null);
|
|
const found = allNodes.find(
|
|
(n) => n.props?.placeholder === placeholder || n.props?.placeholderTextColor != null && n.props?.placeholder === placeholder,
|
|
);
|
|
if (!found) throw new Error(`Unable to find placeholder: "${placeholder}"`);
|
|
return found;
|
|
},
|
|
|
|
queryByText(text: string) {
|
|
try {
|
|
return screen.getByText(text);
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
};
|
|
|
|
export function fireEvent(element: ReactTestRendererJSON, event: string, ...args: unknown[]) {
|
|
const handler = element.props?.[`on${event.charAt(0).toUpperCase()}${event.slice(1)}`];
|
|
if (typeof handler === 'function') handler(...args);
|
|
}
|
|
|
|
fireEvent.press = (element: ReactTestRendererJSON) => fireEvent(element, 'press');
|
|
fireEvent.changeText = (element: ReactTestRendererJSON, text: string) => fireEvent(element, 'changeText', text);
|