import { describe, expect, it } from "vitest"; import { transform } from "../../../../test/helpers/babelTransform.js"; describe("program visitor - Program.exit behavior", () => { describe("StyleSheet injection", () => { it("should inject StyleSheet.create when className is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should have StyleSheet import expect(output).toContain("StyleSheet"); // Should inject StyleSheet.create expect(output).toContain("StyleSheet.create"); expect(output).toContain("_twStyles"); // Should have the styles expect(output).toMatch(/margin:\s*16/); expect(output).toMatch(/padding:\s*8/); expect(output).toMatch(/backgroundColor:/); }); it("should not inject StyleSheet when no className is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should not add StyleSheet import expect(output).not.toContain("StyleSheet"); expect(output).not.toContain("_twStyles"); }); it("should inject styles at top of file after imports", () => { const input = ` import React from 'react'; import { View } from 'react-native'; function MyComponent() { return ; } function AnotherComponent() { return ; } `; const output = transform(input, undefined, true); // StyleSheet.create should come before component definitions const styleSheetIndex = output.indexOf("StyleSheet.create"); const firstComponentIndex = output.indexOf("function MyComponent"); expect(styleSheetIndex).toBeGreaterThan(0); expect(styleSheetIndex).toBeLessThan(firstComponentIndex); }); }); describe("tw import removal", () => { it("should remove tw import when tw tagged template is used", () => { const input = ` import { tw } from '@mgcrea/react-native-tailwind'; import { View } from 'react-native'; function MyComponent() { const styles = tw\`m-4 p-2\`; return ; } `; const output = transform(input, undefined, true); // Should not have tw import anymore expect(output).not.toMatch(/import.*tw.*from ['"]@mgcrea\/react-native-tailwind['"]/); // Should have StyleSheet instead expect(output).toContain("StyleSheet"); }); it("should remove twStyle import when twStyle call is used", () => { const input = ` import { twStyle } from '@mgcrea/react-native-tailwind'; import { View } from 'react-native'; function MyComponent() { const styles = twStyle('m-4 p-2'); return ; } `; const output = transform(input, undefined, true); // Should not have twStyle import anymore expect(output).not.toMatch(/import.*twStyle.*from ['"]@mgcrea\/react-native-tailwind['"]/); // Should have StyleSheet instead expect(output).toContain("StyleSheet"); }); }); describe("hook injection - color scheme", () => { it("should inject useColorScheme hook when dark: modifier is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should import useColorScheme expect(output).toContain("useColorScheme"); // Should inject hook call expect(output).toContain("_twColorScheme"); expect(output).toContain("useColorScheme()"); // Should use in conditional expect(output).toMatch(/_twColorScheme\s*===\s*['"]dark['"]/); }); it("should inject useColorScheme hook when light: modifier is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should import and inject hook expect(output).toContain("useColorScheme"); expect(output).toContain("_twColorScheme"); // Should use in conditional expect(output).toMatch(/_twColorScheme\s*===\s*['"]light['"]/); }); it("should inject hook only once for multiple color scheme modifiers", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ( <> ); } `; const output = transform(input, undefined, true); // Count hook injections (should be exactly 1) const hookMatches = output.match(/_twColorScheme\s*=\s*useColorScheme\(\)/g) ?? []; expect(hookMatches.length).toBe(1); }); it("should use custom color scheme hook when configured", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform( input, { colorScheme: { importFrom: "@react-navigation/native", importName: "useTheme", }, }, true, ); // Should import from custom source expect(output).toContain("@react-navigation/native"); expect(output).toContain("useTheme"); // Should use custom hook expect(output).toContain("useTheme()"); }); }); describe("hook injection - window dimensions", () => { it("should inject useWindowDimensions hook when w-screen is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should import useWindowDimensions expect(output).toContain("useWindowDimensions"); // Should inject hook call expect(output).toContain("_twDimensions"); expect(output).toContain("useWindowDimensions()"); // Should use in inline style expect(output).toMatch(/width:\s*_twDimensions\.width/); }); it("should inject useWindowDimensions hook when h-screen is used", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ; } `; const output = transform(input, undefined, true); // Should import and inject hook expect(output).toContain("useWindowDimensions"); expect(output).toContain("_twDimensions"); // Should use in inline style expect(output).toMatch(/height:\s*_twDimensions\.height/); }); it("should inject hook only once for multiple w-screen/h-screen uses", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ( <> ); } `; const output = transform(input, undefined, true); // Count hook injections (should be exactly 1) const hookMatches = output.match(/_twDimensions\s*=\s*useWindowDimensions\(\)/g) ?? []; expect(hookMatches.length).toBe(1); }); }); describe("combined scenarios", () => { it("should inject color scheme and platform imports together", () => { const input = ` import { View } from 'react-native'; function MyComponent() { return ( ); } `; const output = transform(input, undefined, true); // Should have all imports expect(output).toContain("StyleSheet"); expect(output).toContain("Platform"); expect(output).toContain("useColorScheme"); // Should have color scheme hook expect(output).toContain("_twColorScheme"); expect(output).toContain("useColorScheme()"); // Should have platform select expect(output).toContain("Platform.select"); }); it("should inject window dimensions and color scheme hooks in separate components", () => { const input = ` import { View } from 'react-native'; function ComponentA() { return ; } function ComponentB() { return ; } `; const output = transform(input, undefined, true); // Should have both hooks expect(output).toContain("useColorScheme"); expect(output).toContain("useWindowDimensions"); // Should have both hook calls expect(output).toContain("_twColorScheme"); expect(output).toContain("_twDimensions"); // Should have StyleSheet expect(output).toContain("StyleSheet.create"); }); }); });