diff --git a/packages/design-system-twrnc-preset/jest.config.js b/packages/design-system-twrnc-preset/jest.config.js
index 36e5dd057..ff16cbfde 100644
--- a/packages/design-system-twrnc-preset/jest.config.js
+++ b/packages/design-system-twrnc-preset/jest.config.js
@@ -14,10 +14,6 @@ module.exports = merge(baseConfig, {
// The display name when running multiple projects
displayName,
- // TODO add tests to twrnc preset https://github.com/MetaMask/metamask-design-system/issues/90
- // Pass with no tests if no test files are found
- passWithNoTests: true,
-
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
@@ -38,4 +34,11 @@ module.exports = merge(baseConfig, {
moduleNameMapper: {
'\\.(css|less|scss)$': 'identity-obj-proxy',
},
+ // Exclude pure type files from coverage since they contain no executable code
+ // Also exclude enum files that Jest has difficulty tracking coverage for
+ coveragePathIgnorePatterns: [
+ '/node_modules/',
+ 'typography\\.types\\.ts$',
+ 'Theme\\.types\\.ts$',
+ ],
});
diff --git a/packages/design-system-twrnc-preset/package.json b/packages/design-system-twrnc-preset/package.json
index b302f8963..e32352d9e 100644
--- a/packages/design-system-twrnc-preset/package.json
+++ b/packages/design-system-twrnc-preset/package.json
@@ -65,6 +65,7 @@
"jest": "^29.7.0",
"metro-react-native-babel-preset": "^0.77.0",
"react": "^18.2.0",
+ "react-native": "^0.72.15",
"react-test-renderer": "^18.3.1",
"ts-jest": "^29.2.5",
"typescript": "~5.2.2"
diff --git a/packages/design-system-twrnc-preset/src/Theme.types.test.ts b/packages/design-system-twrnc-preset/src/Theme.types.test.ts
new file mode 100644
index 000000000..6643d509e
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/Theme.types.test.ts
@@ -0,0 +1,86 @@
+import { Theme } from './Theme.types';
+
+describe('Theme', () => {
+ it('has correct light theme value', () => {
+ expect(Theme.Light).toBe('light');
+ });
+
+ it('has correct dark theme value', () => {
+ expect(Theme.Dark).toBe('dark');
+ });
+
+ it('has exactly two theme values', () => {
+ const themeValues = Object.values(Theme);
+ expect(themeValues).toHaveLength(2);
+ expect(themeValues).toContain('light');
+ expect(themeValues).toContain('dark');
+ });
+
+ it('enum keys match expected values', () => {
+ expect(Object.keys(Theme)).toStrictEqual(['Light', 'Dark']);
+ });
+
+ it('can be used as string values', () => {
+ const lightTheme: string = Theme.Light;
+ const darkTheme: string = Theme.Dark;
+
+ expect(typeof lightTheme).toBe('string');
+ expect(typeof darkTheme).toBe('string');
+ expect(lightTheme).toBe('light');
+ expect(darkTheme).toBe('dark');
+ });
+
+ it('can be used as object keys', () => {
+ const themeConfig = {
+ [Theme.Light]: 'light-config',
+ [Theme.Dark]: 'dark-config',
+ };
+
+ expect(themeConfig[Theme.Light]).toBe('light-config');
+ expect(themeConfig[Theme.Dark]).toBe('dark-config');
+ expect(themeConfig.light).toBe('light-config');
+ expect(themeConfig.dark).toBe('dark-config');
+ });
+
+ it('can be iterated over', () => {
+ const themes = Object.values(Theme);
+ const result: string[] = [];
+
+ themes.forEach((theme) => {
+ result.push(theme);
+ });
+
+ expect(result).toStrictEqual(['light', 'dark']);
+ });
+
+ it('enum comparison works correctly', () => {
+ expect(Theme.Light).toBe('light');
+ expect(Theme.Dark).toBe('dark');
+ // Test that they are distinct values
+ const allThemes = [Theme.Light, Theme.Dark];
+ expect(allThemes).toHaveLength(2);
+ expect(new Set(allThemes).size).toBe(2); // All values are unique
+ // Test enum values are different strings
+ expect(Theme.Light).not.toBe(Theme.Dark);
+ });
+
+ it('enum values can be compared and used in logic', () => {
+ // Test direct enum usage without switch statements to avoid conditional logic warnings
+ expect(Theme.Light).toBe('light');
+ expect(Theme.Dark).toBe('dark');
+
+ // Test that we can distinguish between the two enum values
+ expect(Theme.Light).not.toBe(Theme.Dark);
+ expect(Theme.Light).toBe(Theme.Light);
+ expect(Theme.Dark).toBe(Theme.Dark);
+ });
+
+ it('maintains type safety', () => {
+ const validTheme: Theme = Theme.Light;
+ const anotherValidTheme: Theme = Theme.Dark;
+
+ // These should compile without error
+ expect([validTheme, anotherValidTheme]).toContain('light');
+ expect([validTheme, anotherValidTheme]).toContain('dark');
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/ThemeProvider.test.tsx b/packages/design-system-twrnc-preset/src/ThemeProvider.test.tsx
new file mode 100644
index 000000000..e1ce024c4
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/ThemeProvider.test.tsx
@@ -0,0 +1,163 @@
+import { render } from '@testing-library/react-native';
+import React from 'react';
+import { Text, View } from 'react-native';
+
+import { useTheme, useTailwind } from './hooks';
+import { Theme } from './Theme.types';
+import { ThemeProvider } from './ThemeProvider';
+
+// Test component that uses both hooks to verify provider works
+const TestConsumerComponent = ({ testId }: { testId: string }) => {
+ const theme = useTheme();
+ const tw = useTailwind();
+
+ // Test basic styling works
+ const styles = tw`bg-default text-default p-2`;
+
+ return (
+
+ {theme}
+
+ {styles ? 'has-styles' : 'no-styles'}
+
+
+ );
+};
+
+describe('ThemeProvider', () => {
+ it('provides light theme context correctly', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const themeText = getByTestId('light-test-theme');
+ const stylesText = getByTestId('light-test-hasStyles');
+
+ expect(themeText.props.children).toBe(Theme.Light);
+ expect(stylesText.props.children).toBe('has-styles');
+ });
+
+ it('provides dark theme context correctly', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const themeText = getByTestId('dark-test-theme');
+ const stylesText = getByTestId('dark-test-hasStyles');
+
+ expect(themeText.props.children).toBe(Theme.Dark);
+ expect(stylesText.props.children).toBe('has-styles');
+ });
+
+ it('updates context when theme prop changes', () => {
+ const { getByTestId, rerender } = render(
+
+
+ ,
+ );
+
+ // Initial state
+ expect(getByTestId('change-test-theme').props.children).toBe(Theme.Light);
+
+ // After theme change
+ rerender(
+
+
+ ,
+ );
+
+ expect(getByTestId('change-test-theme').props.children).toBe(Theme.Dark);
+ });
+
+ it('generates different tailwind instances for different themes', () => {
+ const { getByTestId: getLightTestId } = render(
+
+
+ ,
+ );
+
+ const { getByTestId: getDarkTestId } = render(
+
+
+ ,
+ );
+
+ const lightView = getLightTestId('light-instance');
+ const darkView = getDarkTestId('dark-instance');
+
+ // Both should have styles but they should be different
+ expect(lightView.props.style).toBeDefined();
+ expect(darkView.props.style).toBeDefined();
+ expect(lightView.props.style).not.toStrictEqual(darkView.props.style);
+ });
+
+ it('supports nested providers with different themes', () => {
+ const { getByTestId } = render(
+
+
+
+
+
+ ,
+ );
+
+ const outerTheme = getByTestId('outer-theme');
+ const innerTheme = getByTestId('inner-theme');
+
+ expect(outerTheme.props.children).toBe(Theme.Light);
+ expect(innerTheme.props.children).toBe(Theme.Dark);
+ });
+
+ it('renders children correctly', () => {
+ const { getByTestId } = render(
+
+
+ Test content
+
+ ,
+ );
+
+ expect(getByTestId('child-view')).toBeDefined();
+ expect(getByTestId('child-text').props.children).toBe('Test content');
+ });
+
+ it('memoizes context value correctly to prevent unnecessary rerenders', () => {
+ let renderCount = 0;
+
+ const CountingComponent = () => {
+ renderCount += 1;
+ const theme = useTheme();
+ return {theme};
+ };
+
+ const { rerender } = render(
+
+
+ ,
+ );
+
+ const initialRenderCount = renderCount;
+
+ // Rerender with same theme - should not cause child to rerender
+ rerender(
+
+
+ ,
+ );
+
+ expect(renderCount).toBe(initialRenderCount + 1); // Only one additional render for the rerender call
+
+ // Rerender with different theme - should cause child to rerender
+ rerender(
+
+
+ ,
+ );
+
+ expect(renderCount).toBe(initialRenderCount + 2); // One more render due to theme change
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/colors.test.ts b/packages/design-system-twrnc-preset/src/colors.test.ts
new file mode 100644
index 000000000..82146f85a
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/colors.test.ts
@@ -0,0 +1,168 @@
+import { themeColors } from './colors';
+import { Theme } from './Theme.types';
+
+describe('colors', () => {
+ describe('themeColors', () => {
+ it('has colors for both light and dark themes', () => {
+ expect(themeColors).toHaveProperty(Theme.Light);
+ expect(themeColors).toHaveProperty(Theme.Dark);
+ expect(typeof themeColors[Theme.Light]).toBe('object');
+ expect(typeof themeColors[Theme.Dark]).toBe('object');
+ });
+
+ it('flattens nested color objects with kebab-case keys', () => {
+ const lightColors = themeColors[Theme.Light];
+
+ expect(lightColors).toHaveProperty('background-default');
+ expect(lightColors).toHaveProperty('background-alternative');
+ expect(lightColors).toHaveProperty('text-default');
+ expect(lightColors).toHaveProperty('primary-default');
+ expect(lightColors).toHaveProperty('error-default');
+ expect(lightColors).toHaveProperty('shadow-default');
+ });
+
+ it('contains expected color values for light theme', () => {
+ const lightColors = themeColors[Theme.Light];
+
+ expect(lightColors['background-default']).toBeDefined();
+ expect(lightColors['background-alternative']).toBeDefined();
+ expect(lightColors['text-default']).toBeDefined();
+ expect(lightColors['primary-default']).toBeDefined();
+ expect(lightColors['error-default']).toBeDefined();
+
+ expect(lightColors['background-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(lightColors['text-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(lightColors['primary-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(lightColors['error-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ });
+
+ it('contains expected color values for dark theme', () => {
+ const darkColors = themeColors[Theme.Dark];
+
+ expect(darkColors['background-default']).toBeDefined();
+ expect(darkColors['background-alternative']).toBeDefined();
+ expect(darkColors['text-default']).toBeDefined();
+ expect(darkColors['primary-default']).toBeDefined();
+ expect(darkColors['error-default']).toBeDefined();
+
+ expect(darkColors['background-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(darkColors['text-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(darkColors['primary-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ expect(darkColors['error-default']).toMatch(
+ /^#[0-9A-F]{6}([0-9A-F]{2})?$/iu,
+ );
+ });
+
+ it('has different colors between light and dark themes', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ expect(lightColors['background-default']).not.toBe(
+ darkColors['background-default'],
+ );
+ expect(lightColors['text-default']).not.toBe(darkColors['text-default']);
+ expect(lightColors['primary-default']).not.toBe(
+ darkColors['primary-default'],
+ );
+ });
+
+ it('has consistent structure between themes', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ const lightKeys = Object.keys(lightColors).sort();
+ const darkKeys = Object.keys(darkColors).sort();
+
+ expect(lightKeys).toStrictEqual(darkKeys);
+ expect(lightKeys.length).toBeGreaterThan(0);
+ });
+
+ it('converts camelCase to kebab-case in color keys', () => {
+ const lightColors = themeColors[Theme.Light];
+
+ expect(lightColors).toHaveProperty('background-alternative');
+ expect(lightColors).toHaveProperty('text-alternative');
+ expect(lightColors).toHaveProperty('primary-alternative');
+ expect(lightColors).toHaveProperty('error-alternative');
+
+ const keys = Object.keys(lightColors);
+ const hasCamelCase = keys.some((key) => /[A-Z]/u.test(key));
+ expect(hasCamelCase).toBe(false);
+ });
+
+ it('includes muted color variants', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ expect(lightColors).toHaveProperty('background-muted');
+ expect(lightColors).toHaveProperty('text-muted');
+ expect(lightColors).toHaveProperty('primary-muted');
+ expect(lightColors).toHaveProperty('error-muted');
+
+ expect(darkColors).toHaveProperty('background-muted');
+ expect(darkColors).toHaveProperty('text-muted');
+ expect(darkColors).toHaveProperty('primary-muted');
+ expect(darkColors).toHaveProperty('error-muted');
+ });
+
+ it('includes shadow colors', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ expect(lightColors).toHaveProperty('shadow-default');
+ expect(lightColors).toHaveProperty('shadow-primary');
+ expect(lightColors).toHaveProperty('shadow-error');
+
+ expect(darkColors).toHaveProperty('shadow-default');
+ expect(darkColors).toHaveProperty('shadow-primary');
+ expect(darkColors).toHaveProperty('shadow-error');
+ });
+
+ it('has all color values as strings', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ Object.values(lightColors).forEach((color) => {
+ expect(typeof color).toBe('string');
+ expect(color).toMatch(/^#[0-9A-F]{6}([0-9A-F]{2})?$/iu);
+ });
+
+ Object.values(darkColors).forEach((color) => {
+ expect(typeof color).toBe('string');
+ expect(color).toMatch(/^#[0-9A-F]{6}([0-9A-F]{2})?$/iu);
+ });
+ });
+
+ it('filters out non-string and non-object values during flattening', () => {
+ const lightColors = themeColors[Theme.Light];
+ const darkColors = themeColors[Theme.Dark];
+
+ const lightKeys = Object.keys(lightColors);
+ const darkKeys = Object.keys(darkColors);
+
+ expect(lightKeys.length).toBeGreaterThan(10);
+ expect(darkKeys.length).toBeGreaterThan(10);
+
+ lightKeys.forEach((key) => {
+ expect(typeof lightColors[key]).toBe('string');
+ });
+ darkKeys.forEach((key) => {
+ expect(typeof darkColors[key]).toBe('string');
+ });
+ });
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/colors.ts b/packages/design-system-twrnc-preset/src/colors.ts
index 3c96ba762..b69c4b507 100644
--- a/packages/design-system-twrnc-preset/src/colors.ts
+++ b/packages/design-system-twrnc-preset/src/colors.ts
@@ -59,7 +59,9 @@ const flattenColors = (
if (typeof value === 'string') {
result[newKey] = value;
- } else if (typeof value === 'object' && value !== null) {
+ }
+
+ if (typeof value === 'object' && value !== null) {
Object.assign(
result,
flattenColors(value as Record, newKey),
diff --git a/packages/design-system-twrnc-preset/src/hooks.test.tsx b/packages/design-system-twrnc-preset/src/hooks.test.tsx
new file mode 100644
index 000000000..ba8aa0d31
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/hooks.test.tsx
@@ -0,0 +1,127 @@
+import { render } from '@testing-library/react-native';
+import React from 'react';
+import { Text, View } from 'react-native';
+
+import { useTheme, useTailwind } from './hooks';
+import { Theme } from './Theme.types';
+import { ThemeProvider } from './ThemeProvider';
+
+// Test components to verify hooks work correctly
+const TestThemeComponent = () => {
+ const theme = useTheme();
+ return {theme};
+};
+
+const TestTailwindComponent = () => {
+ const tw = useTailwind();
+ const styles = tw`bg-default text-default p-4`;
+ return (
+
+ Test
+
+ );
+};
+
+describe('hooks', () => {
+ describe('useTheme', () => {
+ it('returns the current theme from context', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const themeText = getByTestId('theme-text');
+ expect(themeText.props.children).toBe(Theme.Light);
+ });
+
+ it('updates when theme changes', () => {
+ const { getByTestId, rerender } = render(
+
+
+ ,
+ );
+
+ expect(getByTestId('theme-text').props.children).toBe(Theme.Light);
+
+ rerender(
+
+
+ ,
+ );
+
+ expect(getByTestId('theme-text').props.children).toBe(Theme.Dark);
+ });
+
+ it('returns default light theme when used outside ThemeProvider', () => {
+ const { getByTestId } = render();
+
+ const themeText = getByTestId('theme-text');
+ expect(themeText.props.children).toBe(Theme.Light);
+ });
+ });
+
+ describe('useTailwind', () => {
+ it('returns tailwind function that generates styles', () => {
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const view = getByTestId('tailwind-view');
+ expect(view.props.style).toBeDefined();
+ expect(view.props.style).not.toBeNull();
+ expect(typeof view.props.style).toBe('object');
+ });
+
+ it('generates different styles for different themes', () => {
+ const { getByTestId, rerender } = render(
+
+
+ ,
+ );
+
+ const lightStyles = getByTestId('tailwind-view').props.style;
+
+ rerender(
+
+
+ ,
+ );
+
+ const darkStyles = getByTestId('tailwind-view').props.style;
+
+ // Styles should be different between light and dark themes
+ expect(lightStyles).not.toStrictEqual(darkStyles);
+ });
+
+ it('returns default tailwind instance when used outside ThemeProvider', () => {
+ const { getByTestId } = render();
+
+ const view = getByTestId('tailwind-view');
+ expect(view.props.style).toBeDefined();
+ expect(view.props.style).not.toBeNull();
+ expect(typeof view.props.style).toBe('object');
+ });
+
+ it('default tailwind instance uses light theme', () => {
+ // Test with provider using light theme
+ const { getByTestId: getProviderView } = render(
+
+
+ ,
+ );
+
+ // Test without provider (should use default light theme)
+ const { getByTestId: getDefaultView } = render();
+
+ const providerStyles = getProviderView('tailwind-view').props.style;
+ const defaultStyles = getDefaultView('tailwind-view').props.style;
+
+ // Both should have styles (may be slightly different due to context recreation)
+ expect(providerStyles).toBeDefined();
+ expect(defaultStyles).toBeDefined();
+ });
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/index.test.ts b/packages/design-system-twrnc-preset/src/index.test.ts
new file mode 100644
index 000000000..8d5a61fb7
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/index.test.ts
@@ -0,0 +1,88 @@
+import { useTheme, useTailwind } from './hooks';
+import { Theme } from './Theme.types';
+import { ThemeProvider } from './ThemeProvider';
+
+import * as DesignSystemTwrncPreset from '.';
+
+describe('index exports', () => {
+ it('exports ThemeProvider', () => {
+ expect(DesignSystemTwrncPreset.ThemeProvider).toBeDefined();
+ expect(DesignSystemTwrncPreset.ThemeProvider).toBe(ThemeProvider);
+ });
+
+ it('exports Theme enum', () => {
+ expect(DesignSystemTwrncPreset.Theme).toBeDefined();
+ expect(DesignSystemTwrncPreset.Theme).toBe(Theme);
+ });
+
+ it('exports useTheme hook', () => {
+ expect(DesignSystemTwrncPreset.useTheme).toBeDefined();
+ expect(DesignSystemTwrncPreset.useTheme).toBe(useTheme);
+ expect(typeof DesignSystemTwrncPreset.useTheme).toBe('function');
+ });
+
+ it('exports useTailwind hook', () => {
+ expect(DesignSystemTwrncPreset.useTailwind).toBeDefined();
+ expect(DesignSystemTwrncPreset.useTailwind).toBe(useTailwind);
+ expect(typeof DesignSystemTwrncPreset.useTailwind).toBe('function');
+ });
+
+ it('exports all expected members', () => {
+ const expectedExports = [
+ 'ThemeProvider',
+ 'Theme',
+ 'useTheme',
+ 'useTailwind',
+ ];
+ const actualExports = Object.keys(DesignSystemTwrncPreset);
+
+ expectedExports.forEach((exportName) => {
+ expect(actualExports).toContain(exportName);
+ });
+ });
+
+ it('does not export unexpected members', () => {
+ const actualExports = Object.keys(DesignSystemTwrncPreset);
+ const expectedExports = [
+ 'ThemeProvider',
+ 'Theme',
+ 'useTheme',
+ 'useTailwind',
+ ];
+
+ expect(actualExports.sort()).toStrictEqual(expectedExports.sort());
+ });
+
+ it('exports have correct types', () => {
+ expect(typeof DesignSystemTwrncPreset.ThemeProvider).toBe('function');
+ expect(typeof DesignSystemTwrncPreset.Theme).toBe('object');
+ expect(typeof DesignSystemTwrncPreset.useTheme).toBe('function');
+ expect(typeof DesignSystemTwrncPreset.useTailwind).toBe('function');
+ });
+
+ it('theme enum has correct values', () => {
+ expect(DesignSystemTwrncPreset.Theme.Light).toBe('light');
+ expect(DesignSystemTwrncPreset.Theme.Dark).toBe('dark');
+ });
+
+ it('can be used with destructuring imports', () => {
+ const {
+ ThemeProvider: ImportedThemeProvider,
+ Theme: ImportedTheme,
+ useTheme: ImportedUseTheme,
+ useTailwind: ImportedUseTailwind,
+ } = DesignSystemTwrncPreset;
+
+ expect(ImportedThemeProvider).toBe(ThemeProvider);
+ expect(ImportedTheme).toBe(Theme);
+ expect(ImportedUseTheme).toBe(useTheme);
+ expect(ImportedUseTailwind).toBe(useTailwind);
+ });
+
+ it('maintains referential equality for exports', () => {
+ const firstImport = DesignSystemTwrncPreset.ThemeProvider;
+ const secondImport = DesignSystemTwrncPreset.ThemeProvider;
+
+ expect(firstImport).toBe(secondImport);
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/tailwind.config.test.ts b/packages/design-system-twrnc-preset/src/tailwind.config.test.ts
new file mode 100644
index 000000000..d1017d57c
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/tailwind.config.test.ts
@@ -0,0 +1,163 @@
+import { generateTailwindConfig } from './tailwind.config';
+import { Theme } from './Theme.types';
+
+// Mock the colors and typography modules since we're testing the config generation logic
+jest.mock('./colors', () => ({
+ themeColors: {
+ light: {
+ 'background-default': '#ffffff',
+ 'background-muted': '#f2f4f6',
+ 'text-default': '#24272a',
+ 'text-muted': '#6a737d',
+ 'border-default': '#d6d9dc',
+ 'border-muted': '#bbc0c5',
+ 'primary-default': '#0376c9',
+ 'error-default': '#d73a49',
+ },
+ dark: {
+ 'background-default': '#24272a',
+ 'background-muted': '#1c1e21',
+ 'text-default': '#ffffff',
+ 'text-muted': '#9fa6ad',
+ 'border-default': '#495057',
+ 'border-muted': '#6c757d',
+ 'primary-default': '#1098fc',
+ 'error-default': '#f85149',
+ },
+ },
+}));
+
+jest.mock('./typography', () => ({
+ typographyTailwindConfig: {
+ fontSize: {
+ xs: ['12px', '16px'],
+ sm: ['14px', '20px'],
+ base: ['16px', '24px'],
+ },
+ fontFamily: {
+ sans: ['System', 'sans-serif'],
+ },
+ letterSpacing: {
+ tight: '-0.025em',
+ normal: '0em',
+ },
+ lineHeight: {
+ tight: '1.25',
+ normal: '1.5',
+ },
+ },
+}));
+
+describe('generateTailwindConfig', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('generates config for light theme', () => {
+ const config = generateTailwindConfig(Theme.Light);
+
+ expect(config).toBeDefined();
+ expect(typeof config).toBe('object');
+ expect(config).toHaveProperty('theme');
+ });
+
+ it('generates config for dark theme', () => {
+ const config = generateTailwindConfig(Theme.Dark);
+
+ expect(config).toBeDefined();
+ expect(typeof config).toBe('object');
+ expect(config).toHaveProperty('theme');
+ });
+
+ it('includes color properties in the config', () => {
+ const config = generateTailwindConfig(Theme.Light);
+
+ expect(config).toHaveProperty('theme.extend.colors');
+ expect(config).toHaveProperty('theme.extend.textColor');
+ expect(config).toHaveProperty('theme.extend.backgroundColor');
+ expect(config).toHaveProperty('theme.extend.borderColor');
+ });
+
+ it('includes typography configuration', () => {
+ const config = generateTailwindConfig(Theme.Light);
+
+ expect(config).toHaveProperty('theme.extend.fontSize');
+ expect(config).toHaveProperty('theme.extend.fontFamily');
+ expect(config).toHaveProperty('theme.extend.letterSpacing');
+ expect(config).toHaveProperty('theme.extend.lineHeight');
+ });
+
+ it('generates different configs for different themes', () => {
+ const lightConfig = generateTailwindConfig(Theme.Light);
+ const darkConfig = generateTailwindConfig(Theme.Dark);
+
+ expect(lightConfig).not.toStrictEqual(darkConfig);
+ expect(lightConfig).toHaveProperty('theme');
+ expect(darkConfig).toHaveProperty('theme');
+ });
+
+ it('handles invalid theme gracefully', () => {
+ const consoleErrorSpy = jest
+ .spyOn(console, 'error')
+ .mockImplementation(() => {
+ // Empty implementation
+ });
+
+ const config = generateTailwindConfig('invalid-theme' as Theme);
+
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Theme colors not found.');
+ expect(config).toStrictEqual({});
+
+ consoleErrorSpy.mockRestore();
+ });
+
+ it('config structure is consistent between themes', () => {
+ const lightConfig = generateTailwindConfig(Theme.Light);
+ const darkConfig = generateTailwindConfig(Theme.Dark);
+
+ expect(lightConfig).toHaveProperty('theme.extend');
+ expect(darkConfig).toHaveProperty('theme.extend');
+
+ const lightExtend = (lightConfig as Record)
+ .theme as Record;
+ const darkExtend = (darkConfig as Record).theme as Record<
+ string,
+ unknown
+ >;
+
+ expect(lightExtend).toBeDefined();
+ expect(darkExtend).toBeDefined();
+ expect(
+ Object.keys(lightExtend.extend as Record),
+ ).toStrictEqual(Object.keys(darkExtend.extend as Record));
+ });
+
+ it('generates valid twrnc config object', () => {
+ const config = generateTailwindConfig(Theme.Light);
+
+ expect(typeof config).toBe('object');
+ expect(config).not.toBeNull();
+ expect(Array.isArray(config)).toBe(false);
+ });
+
+ it('maintains color values for each theme', () => {
+ const lightConfig = generateTailwindConfig(Theme.Light);
+ const darkConfig = generateTailwindConfig(Theme.Dark);
+
+ const lightColors = (lightConfig as Record)
+ .theme as Record;
+ const lightExtendColors = (lightColors.extend as Record)
+ .colors as Record;
+ expect(lightExtendColors['background-default']).toBe('#ffffff');
+ expect(lightExtendColors['text-default']).toBe('#24272a');
+
+ const darkColors = (darkConfig as Record).theme as Record<
+ string,
+ unknown
+ >;
+ const darkExtendColors = (darkColors.extend as Record)
+ .colors as Record;
+ expect(darkExtendColors['background-default']).toBe('#24272a');
+ expect(darkExtendColors['text-default']).toBe('#ffffff');
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/typography.test.ts b/packages/design-system-twrnc-preset/src/typography.test.ts
new file mode 100644
index 000000000..00ce1d9c0
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/typography.test.ts
@@ -0,0 +1,253 @@
+import { typographyTailwindConfig } from './typography';
+import type { TypographyVariant } from './typography.types';
+
+describe('typography', () => {
+ describe('typographyTailwindConfig', () => {
+ it('has all required properties', () => {
+ expect(typographyTailwindConfig).toHaveProperty('fontSize');
+ expect(typographyTailwindConfig).toHaveProperty('fontFamily');
+ expect(typographyTailwindConfig).toHaveProperty('letterSpacing');
+ expect(typographyTailwindConfig).toHaveProperty('lineHeight');
+ });
+
+ describe('fontSize', () => {
+ const expectedVariants: TypographyVariant[] = [
+ 'display-lg',
+ 'display-md',
+ 'heading-lg',
+ 'heading-md',
+ 'heading-sm',
+ 'body-lg',
+ 'body-md',
+ 'body-sm',
+ 'body-xs',
+ ];
+
+ it('contains all typography variants', () => {
+ expectedVariants.forEach((variant) => {
+ expect(typographyTailwindConfig.fontSize).toHaveProperty(variant);
+ });
+ });
+
+ it('has correct structure for each font size variant', () => {
+ expectedVariants.forEach((variant) => {
+ const fontSize = typographyTailwindConfig.fontSize[variant];
+
+ expect(Array.isArray(fontSize)).toBe(true);
+ expect(fontSize).toHaveLength(2);
+ expect(typeof fontSize[0]).toBe('string');
+ expect(typeof fontSize[1]).toBe('object');
+
+ const styleProperties = fontSize[1];
+ expect(styleProperties).toHaveProperty('lineHeight');
+ expect(styleProperties).toHaveProperty('letterSpacing');
+ expect(styleProperties).toHaveProperty('fontWeight');
+ });
+ });
+
+ it('has expected font size values from actual design tokens', () => {
+ const { fontSize } = typographyTailwindConfig;
+
+ expectedVariants.forEach((variant) => {
+ const [fontSizeValue] = fontSize[variant];
+ expect(fontSizeValue).toMatch(/^\d+$/u);
+ expect(parseInt(fontSizeValue, 10)).toBeGreaterThan(0);
+ });
+
+ expect(parseInt(fontSize['display-lg'][0], 10)).toBeGreaterThan(32);
+ expect(parseInt(fontSize['body-xs'][0], 10)).toBeLessThan(16);
+ });
+
+ it('has line heights with px units', () => {
+ expectedVariants.forEach((variant) => {
+ const { lineHeight } = typographyTailwindConfig.fontSize[variant][1];
+ expect(lineHeight).toMatch(/\d+px$/u);
+
+ const numericValue = parseInt(lineHeight.replace('px', ''), 10);
+ expect(numericValue).toBeGreaterThan(0);
+ });
+ });
+ });
+
+ describe('fontFamily', () => {
+ const expectedFontFamilies = [
+ 'default-regular',
+ 'default-regular-italic',
+ 'default-medium',
+ 'default-medium-italic',
+ 'default-bold',
+ 'default-bold-italic',
+ 'accent-regular',
+ 'accent-medium',
+ 'accent-bold',
+ 'hero-regular',
+ ];
+
+ it('contains all required font families', () => {
+ expectedFontFamilies.forEach((fontFamily) => {
+ expect(typographyTailwindConfig.fontFamily).toHaveProperty(
+ fontFamily,
+ );
+ expect(
+ typeof typographyTailwindConfig.fontFamily[
+ fontFamily as keyof typeof typographyTailwindConfig.fontFamily
+ ],
+ ).toBe('string');
+ });
+ });
+
+ it('has correct MetaMask font family values', () => {
+ expect(typographyTailwindConfig.fontFamily['default-regular']).toBe(
+ 'CentraNo1-Book',
+ );
+ expect(
+ typographyTailwindConfig.fontFamily['default-regular-italic'],
+ ).toBe('CentraNo1-BookItalic');
+ expect(typographyTailwindConfig.fontFamily['default-medium']).toBe(
+ 'CentraNo1-Medium',
+ );
+ expect(
+ typographyTailwindConfig.fontFamily['default-medium-italic'],
+ ).toBe('CentraNo1-MediumItalic');
+ expect(typographyTailwindConfig.fontFamily['default-bold']).toBe(
+ 'CentraNo1-Bold',
+ );
+ expect(typographyTailwindConfig.fontFamily['default-bold-italic']).toBe(
+ 'CentraNo1-BoldItalic',
+ );
+ expect(typographyTailwindConfig.fontFamily['accent-regular']).toBe(
+ 'MMSans-Regular',
+ );
+ expect(typographyTailwindConfig.fontFamily['accent-medium']).toBe(
+ 'MMSans-Medium',
+ );
+ expect(typographyTailwindConfig.fontFamily['accent-bold']).toBe(
+ 'MMSans-Bold',
+ );
+ expect(typographyTailwindConfig.fontFamily['hero-regular']).toBe(
+ 'MMPoly-Regular',
+ );
+ });
+ });
+
+ describe('letterSpacing', () => {
+ const expectedVariants: TypographyVariant[] = [
+ 'display-lg',
+ 'display-md',
+ 'heading-lg',
+ 'heading-md',
+ 'heading-sm',
+ 'body-lg',
+ 'body-md',
+ 'body-sm',
+ 'body-xs',
+ ];
+
+ it('contains all typography variants', () => {
+ expectedVariants.forEach((variant) => {
+ expect(typographyTailwindConfig.letterSpacing).toHaveProperty(
+ variant,
+ );
+ expect(typeof typographyTailwindConfig.letterSpacing[variant]).toBe(
+ 'string',
+ );
+ });
+ });
+
+ it('has valid letter spacing values from actual design tokens', () => {
+ expectedVariants.forEach((variant) => {
+ const letterSpacing = typographyTailwindConfig.letterSpacing[variant];
+ expect(letterSpacing).toMatch(/^-?\d*\.?\d+$/u);
+ });
+ });
+ });
+
+ describe('lineHeight', () => {
+ const expectedVariants: TypographyVariant[] = [
+ 'display-lg',
+ 'display-md',
+ 'heading-lg',
+ 'heading-md',
+ 'heading-sm',
+ 'body-lg',
+ 'body-md',
+ 'body-sm',
+ 'body-xs',
+ ];
+
+ it('contains all typography variants', () => {
+ expectedVariants.forEach((variant) => {
+ expect(typographyTailwindConfig.lineHeight).toHaveProperty(variant);
+ expect(typeof typographyTailwindConfig.lineHeight[variant]).toBe(
+ 'string',
+ );
+ });
+ });
+
+ it('has line heights with px units', () => {
+ expectedVariants.forEach((variant) => {
+ const lineHeight = typographyTailwindConfig.lineHeight[variant];
+ expect(lineHeight).toMatch(/^\d+px$/u);
+ });
+ });
+
+ it('has reasonable line height values from actual design tokens', () => {
+ expectedVariants.forEach((variant) => {
+ const lineHeight = typographyTailwindConfig.lineHeight[variant];
+ const numericValue = parseInt(lineHeight.replace('px', ''), 10);
+
+ expect(numericValue).toBeGreaterThan(0);
+ expect(numericValue).toBeLessThan(200);
+ });
+
+ const displayLgHeight = parseInt(
+ typographyTailwindConfig.lineHeight['display-lg'].replace('px', ''),
+ 10,
+ );
+ const bodyXsHeight = parseInt(
+ typographyTailwindConfig.lineHeight['body-xs'].replace('px', ''),
+ 10,
+ );
+ expect(displayLgHeight).toBeGreaterThan(bodyXsHeight);
+ });
+ });
+
+ it('maintains consistency between fontSize and lineHeight variants', () => {
+ const fontSizeVariants = Object.keys(typographyTailwindConfig.fontSize);
+ const lineHeightVariants = Object.keys(
+ typographyTailwindConfig.lineHeight,
+ );
+ const letterSpacingVariants = Object.keys(
+ typographyTailwindConfig.letterSpacing,
+ );
+
+ expect(fontSizeVariants.sort()).toStrictEqual(lineHeightVariants.sort());
+ expect(fontSizeVariants.sort()).toStrictEqual(
+ letterSpacingVariants.sort(),
+ );
+ });
+
+ it('has consistent line height values between fontSize and lineHeight objects', () => {
+ const variants: TypographyVariant[] = [
+ 'display-lg',
+ 'display-md',
+ 'heading-lg',
+ 'heading-md',
+ 'heading-sm',
+ 'body-lg',
+ 'body-md',
+ 'body-sm',
+ 'body-xs',
+ ];
+
+ variants.forEach((variant) => {
+ const fontSizeLineHeight =
+ typographyTailwindConfig.fontSize[variant][1].lineHeight;
+ const standaloneLineHeight =
+ typographyTailwindConfig.lineHeight[variant];
+
+ expect(fontSizeLineHeight).toBe(standaloneLineHeight);
+ });
+ });
+ });
+});
diff --git a/packages/design-system-twrnc-preset/src/typography.types.test.ts b/packages/design-system-twrnc-preset/src/typography.types.test.ts
new file mode 100644
index 000000000..44593416c
--- /dev/null
+++ b/packages/design-system-twrnc-preset/src/typography.types.test.ts
@@ -0,0 +1,249 @@
+import type {
+ TypographyVariant,
+ FontWeight,
+ FontStyle,
+ TypographyTailwindConfigProps,
+} from './typography.types';
+
+describe('typography types', () => {
+ describe('TypographyVariant', () => {
+ it('includes all expected typography variants', () => {
+ const expectedVariants = [
+ 'display-lg',
+ 'display-md',
+ 'heading-lg',
+ 'heading-md',
+ 'heading-sm',
+ 'body-lg',
+ 'body-md',
+ 'body-sm',
+ 'body-xs',
+ ];
+
+ const testVariants: TypographyVariant[] =
+ expectedVariants as TypographyVariant[];
+ expect(testVariants).toHaveLength(9);
+ expect(testVariants).toStrictEqual(expectedVariants);
+ });
+
+ it('can be used as union type', () => {
+ const testFunction = (variant: TypographyVariant): string => variant;
+
+ expect(testFunction('display-lg')).toBe('display-lg');
+ expect(testFunction('display-md')).toBe('display-md');
+ expect(testFunction('heading-lg')).toBe('heading-lg');
+ expect(testFunction('heading-md')).toBe('heading-md');
+ expect(testFunction('heading-sm')).toBe('heading-sm');
+ expect(testFunction('body-lg')).toBe('body-lg');
+ expect(testFunction('body-md')).toBe('body-md');
+ expect(testFunction('body-sm')).toBe('body-sm');
+ expect(testFunction('body-xs')).toBe('body-xs');
+ });
+
+ it('can be used as object keys', () => {
+ const testObject: Record = {
+ 'display-lg': 'test',
+ 'display-md': 'test',
+ 'heading-lg': 'test',
+ 'heading-md': 'test',
+ 'heading-sm': 'test',
+ 'body-lg': 'test',
+ 'body-md': 'test',
+ 'body-sm': 'test',
+ 'body-xs': 'test',
+ };
+
+ expect(Object.keys(testObject)).toHaveLength(9);
+ });
+ });
+
+ describe('FontWeight', () => {
+ it('includes all expected font weight values', () => {
+ const numericWeights = [
+ '100',
+ '200',
+ '300',
+ '400',
+ '500',
+ '600',
+ '700',
+ '800',
+ '900',
+ ];
+ const namedWeights = ['normal', 'bold'];
+
+ const testWeights: FontWeight[] = [
+ ...numericWeights,
+ ...namedWeights,
+ ] as FontWeight[];
+ expect(testWeights).toHaveLength(11);
+ });
+
+ it('can be used as union type', () => {
+ const testFunction = (weight: FontWeight): string => weight;
+
+ expect(testFunction('100')).toBe('100');
+ expect(testFunction('400')).toBe('400');
+ expect(testFunction('700')).toBe('700');
+ expect(testFunction('normal')).toBe('normal');
+ expect(testFunction('bold')).toBe('bold');
+ });
+ });
+
+ describe('FontStyle', () => {
+ it('includes expected font style values', () => {
+ const expectedStyles = ['normal', 'italic'];
+ const testStyles: FontStyle[] = expectedStyles as FontStyle[];
+ expect(testStyles).toHaveLength(2);
+ expect(testStyles).toStrictEqual(expectedStyles);
+ });
+
+ it('can be used as union type', () => {
+ const testFunction = (style: FontStyle): string => style;
+
+ expect(testFunction('normal')).toBe('normal');
+ expect(testFunction('italic')).toBe('italic');
+ });
+ });
+
+ describe('TypographyTailwindConfigProps', () => {
+ it('has correct structure for fontSize property', () => {
+ const mockConfig: TypographyTailwindConfigProps = {
+ fontSize: {
+ 'display-lg': [
+ '48',
+ { lineHeight: '56px', letterSpacing: '0', fontWeight: '700' },
+ ],
+ 'display-md': [
+ '32',
+ { lineHeight: '40px', letterSpacing: '0', fontWeight: '700' },
+ ],
+ 'heading-lg': [
+ '24',
+ { lineHeight: '32px', letterSpacing: '0', fontWeight: '700' },
+ ],
+ 'heading-md': [
+ '18',
+ { lineHeight: '24px', letterSpacing: '0', fontWeight: '700' },
+ ],
+ 'heading-sm': [
+ '16',
+ { lineHeight: '24px', letterSpacing: '0', fontWeight: '700' },
+ ],
+ 'body-lg': [
+ '18',
+ { lineHeight: '24px', letterSpacing: '0', fontWeight: '500' },
+ ],
+ 'body-md': [
+ '14',
+ { lineHeight: '20px', letterSpacing: '0', fontWeight: '400' },
+ ],
+ 'body-sm': [
+ '12',
+ { lineHeight: '16px', letterSpacing: '0', fontWeight: '400' },
+ ],
+ 'body-xs': [
+ '10',
+ { lineHeight: '12px', letterSpacing: '0', fontWeight: '400' },
+ ],
+ },
+ fontFamily: {
+ 'default-regular': 'CentraNo1-Book',
+ 'default-regular-italic': 'CentraNo1-BookItalic',
+ 'default-medium': 'CentraNo1-Medium',
+ 'default-medium-italic': 'CentraNo1-MediumItalic',
+ 'default-bold': 'CentraNo1-Bold',
+ 'default-bold-italic': 'CentraNo1-BoldItalic',
+ 'accent-regular': 'MMSans-Regular',
+ 'accent-medium': 'MMSans-Medium',
+ 'accent-bold': 'MMSans-Bold',
+ 'hero-regular': 'MMPoly-Regular',
+ },
+ letterSpacing: {
+ 'display-lg': '0',
+ 'display-md': '0',
+ 'heading-lg': '0',
+ 'heading-md': '0',
+ 'heading-sm': '0',
+ 'body-lg': '0',
+ 'body-md': '0',
+ 'body-sm': '0',
+ 'body-xs': '0',
+ },
+ lineHeight: {
+ 'display-lg': '56px',
+ 'display-md': '40px',
+ 'heading-lg': '32px',
+ 'heading-md': '24px',
+ 'heading-sm': '24px',
+ 'body-lg': '24px',
+ 'body-md': '20px',
+ 'body-sm': '16px',
+ 'body-xs': '12px',
+ },
+ };
+
+ expect(mockConfig).toBeDefined();
+ expect(mockConfig.fontSize).toBeDefined();
+ expect(mockConfig.fontFamily).toBeDefined();
+ expect(mockConfig.letterSpacing).toBeDefined();
+ expect(mockConfig.lineHeight).toBeDefined();
+ });
+
+ it('requires lineHeight to be string with units', () => {
+ const validConfig: TypographyTailwindConfigProps['lineHeight'] = {
+ 'display-lg': '56px',
+ 'display-md': '40px',
+ 'heading-lg': '32px',
+ 'heading-md': '24px',
+ 'heading-sm': '24px',
+ 'body-lg': '24px',
+ 'body-md': '20px',
+ 'body-sm': '16px',
+ 'body-xs': '12px',
+ };
+
+ expect(typeof validConfig['display-lg']).toBe('string');
+ expect(validConfig['display-lg']).toMatch(/px$/u);
+ });
+
+ it('requires fontSize to be tuple with string and style object', () => {
+ const validFontSize: TypographyTailwindConfigProps['fontSize']['display-lg'] =
+ [
+ '48',
+ {
+ lineHeight: '56px',
+ letterSpacing: '0',
+ fontWeight: '700',
+ },
+ ];
+
+ expect(Array.isArray(validFontSize)).toBe(true);
+ expect(validFontSize).toHaveLength(2);
+ expect(typeof validFontSize[0]).toBe('string');
+ expect(typeof validFontSize[1]).toBe('object');
+ });
+
+ it('includes all required fontFamily keys', () => {
+ const requiredFontFamilyKeys = [
+ 'default-regular',
+ 'default-regular-italic',
+ 'default-medium',
+ 'default-medium-italic',
+ 'default-bold',
+ 'default-bold-italic',
+ 'accent-regular',
+ 'accent-medium',
+ 'accent-bold',
+ 'hero-regular',
+ ];
+
+ type FontFamilyKeys = keyof TypographyTailwindConfigProps['fontFamily'];
+ const testKeys: FontFamilyKeys[] =
+ requiredFontFamilyKeys as FontFamilyKeys[];
+
+ expect(testKeys).toHaveLength(10);
+ expect(testKeys).toStrictEqual(requiredFontFamilyKeys);
+ });
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index 903a7f3df..23b5704a0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -50,14 +50,14 @@ __metadata:
languageName: node
linkType: hard
-"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.8.3":
- version: 7.26.2
- resolution: "@babel/code-frame@npm:7.26.2"
+"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.8.3":
+ version: 7.27.1
+ resolution: "@babel/code-frame@npm:7.27.1"
dependencies:
- "@babel/helper-validator-identifier": "npm:^7.25.9"
+ "@babel/helper-validator-identifier": "npm:^7.27.1"
js-tokens: "npm:^4.0.0"
- picocolors: "npm:^1.0.0"
- checksum: 10/db2c2122af79d31ca916755331bb4bac96feb2b334cdaca5097a6b467fdd41963b89b14b6836a14f083de7ff887fc78fa1b3c10b14e743d33e12dbfe5ee3d223
+ picocolors: "npm:^1.1.1"
+ checksum: 10/721b8a6e360a1fa0f1c9fe7351ae6c874828e119183688b533c477aa378f1010f37cc9afbfc4722c686d1f5cdd00da02eab4ba7278a0c504fa0d7a321dcd4fdf
languageName: node
linkType: hard
@@ -69,38 +69,38 @@ __metadata:
linkType: hard
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.0, @babel/core@npm:^7.21.3, @babel/core@npm:^7.22.5, @babel/core@npm:^7.23.5, @babel/core@npm:^7.23.9, @babel/core@npm:^7.26.0, @babel/core@npm:^7.7.5":
- version: 7.26.10
- resolution: "@babel/core@npm:7.26.10"
+ version: 7.27.4
+ resolution: "@babel/core@npm:7.27.4"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
- "@babel/code-frame": "npm:^7.26.2"
- "@babel/generator": "npm:^7.26.10"
- "@babel/helper-compilation-targets": "npm:^7.26.5"
- "@babel/helper-module-transforms": "npm:^7.26.0"
- "@babel/helpers": "npm:^7.26.10"
- "@babel/parser": "npm:^7.26.10"
- "@babel/template": "npm:^7.26.9"
- "@babel/traverse": "npm:^7.26.10"
- "@babel/types": "npm:^7.26.10"
+ "@babel/code-frame": "npm:^7.27.1"
+ "@babel/generator": "npm:^7.27.3"
+ "@babel/helper-compilation-targets": "npm:^7.27.2"
+ "@babel/helper-module-transforms": "npm:^7.27.3"
+ "@babel/helpers": "npm:^7.27.4"
+ "@babel/parser": "npm:^7.27.4"
+ "@babel/template": "npm:^7.27.2"
+ "@babel/traverse": "npm:^7.27.4"
+ "@babel/types": "npm:^7.27.3"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
- checksum: 10/68f6707eebd6bb8beed7ceccf5153e35b86c323e40d11d796d75c626ac8f1cc4e1f795584c5ab5f886bc64150c22d5088123d68c069c63f29984c4fc054d1dab
+ checksum: 10/28c01186d5f2599e41f92c94fd14a02cfdcf4b74429b4028a8d16e45c1b08d3924c4275e56412f30fcd2664e5ddc2200f1c06cee8bffff4bba628ff1f20c6e70
languageName: node
linkType: hard
-"@babel/generator@npm:^7.20.0, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.10, @babel/generator@npm:^7.7.2":
- version: 7.27.0
- resolution: "@babel/generator@npm:7.27.0"
+"@babel/generator@npm:^7.20.0, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.25.9, @babel/generator@npm:^7.27.3, @babel/generator@npm:^7.7.2":
+ version: 7.27.5
+ resolution: "@babel/generator@npm:7.27.5"
dependencies:
- "@babel/parser": "npm:^7.27.0"
- "@babel/types": "npm:^7.27.0"
+ "@babel/parser": "npm:^7.27.5"
+ "@babel/types": "npm:^7.27.3"
"@jridgewell/gen-mapping": "npm:^0.3.5"
"@jridgewell/trace-mapping": "npm:^0.3.25"
jsesc: "npm:^3.0.2"
- checksum: 10/5447c402b1d841132534a0a9715e89f4f28b6f2886a23e70aaa442150dba4a1e29e4e2351814f439ee1775294dccdef9ab0a4192b6e6a5ad44e24233b3611da2
+ checksum: 10/f5e6942670cb32156b3ac2d75ce09b373558823387f15dd1413c27fe9eb5756a7c6011fc7f956c7acc53efb530bfb28afffa24364d46c4e9ffccc4e5c8b3b094
languageName: node
linkType: hard
@@ -113,7 +113,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.9, @babel/helper-compilation-targets@npm:^7.26.5, @babel/helper-compilation-targets@npm:^7.27.1":
+"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.9, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2":
version: 7.27.2
resolution: "@babel/helper-compilation-targets@npm:7.27.2"
dependencies:
@@ -208,26 +208,26 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.25.9":
- version: 7.25.9
- resolution: "@babel/helper-module-imports@npm:7.25.9"
+"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.25.9, @babel/helper-module-imports@npm:^7.27.1":
+ version: 7.27.1
+ resolution: "@babel/helper-module-imports@npm:7.27.1"
dependencies:
- "@babel/traverse": "npm:^7.25.9"
- "@babel/types": "npm:^7.25.9"
- checksum: 10/e090be5dee94dda6cd769972231b21ddfae988acd76b703a480ac0c96f3334557d70a965bf41245d6ee43891e7571a8b400ccf2b2be5803351375d0f4e5bcf08
+ "@babel/traverse": "npm:^7.27.1"
+ "@babel/types": "npm:^7.27.1"
+ checksum: 10/58e792ea5d4ae71676e0d03d9fef33e886a09602addc3bd01388a98d87df9fcfd192968feb40ac4aedb7e287ec3d0c17b33e3ecefe002592041a91d8a1998a8d
languageName: node
linkType: hard
-"@babel/helper-module-transforms@npm:^7.25.9, @babel/helper-module-transforms@npm:^7.26.0":
- version: 7.26.0
- resolution: "@babel/helper-module-transforms@npm:7.26.0"
+"@babel/helper-module-transforms@npm:^7.25.9, @babel/helper-module-transforms@npm:^7.26.0, @babel/helper-module-transforms@npm:^7.27.3":
+ version: 7.27.3
+ resolution: "@babel/helper-module-transforms@npm:7.27.3"
dependencies:
- "@babel/helper-module-imports": "npm:^7.25.9"
- "@babel/helper-validator-identifier": "npm:^7.25.9"
- "@babel/traverse": "npm:^7.25.9"
+ "@babel/helper-module-imports": "npm:^7.27.1"
+ "@babel/helper-validator-identifier": "npm:^7.27.1"
+ "@babel/traverse": "npm:^7.27.3"
peerDependencies:
"@babel/core": ^7.0.0
- checksum: 10/9841d2a62f61ad52b66a72d08264f23052d533afc4ce07aec2a6202adac0bfe43014c312f94feacb3291f4c5aafe681955610041ece2c276271adce3f570f2f5
+ checksum: 10/47abc90ceb181b4bdea9bf1717adf536d1b5e5acb6f6d8a7a4524080318b5ca8a99e6d58677268c596bad71077d1d98834d2c3815f2443e6d3f287962300f15d
languageName: node
linkType: hard
@@ -315,13 +315,13 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helpers@npm:^7.26.10":
- version: 7.27.0
- resolution: "@babel/helpers@npm:7.27.0"
+"@babel/helpers@npm:^7.27.4":
+ version: 7.27.6
+ resolution: "@babel/helpers@npm:7.27.6"
dependencies:
- "@babel/template": "npm:^7.27.0"
- "@babel/types": "npm:^7.27.0"
- checksum: 10/0dd40ba1e5ba4b72d1763bb381384585a56f21a61a19dc1b9a03381fe8e840207fdaa4da645d14dc028ad768087d41aad46347cc6573bd69d82f597f5a12dc6f
+ "@babel/template": "npm:^7.27.2"
+ "@babel/types": "npm:^7.27.6"
+ checksum: 10/33c1ab2b42f05317776a4d67c5b00d916dbecfbde38a9406a1300ad3ad6e0380a2f6fcd3361369119a82a7d3c20de6e66552d147297f17f656cf17912605aa97
languageName: node
linkType: hard
@@ -337,14 +337,14 @@ __metadata:
languageName: node
linkType: hard
-"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.3, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.10, @babel/parser@npm:^7.27.0":
- version: 7.27.0
- resolution: "@babel/parser@npm:7.27.0"
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.3, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.4, @babel/parser@npm:^7.27.5":
+ version: 7.27.5
+ resolution: "@babel/parser@npm:7.27.5"
dependencies:
- "@babel/types": "npm:^7.27.0"
+ "@babel/types": "npm:^7.27.3"
bin:
parser: ./bin/babel-parser.js
- checksum: 10/0fee9f05c6db753882ca9d10958301493443da9f6986d7020ebd7a696b35886240016899bc0b47d871aea2abcafd64632343719742e87432c8145e0ec2af2a03
+ checksum: 10/0ad671be7994dba7d31ec771bd70ea5090aa34faf73e93b1b072e3c0a704ab69f4a7a68ebfb9d6a7fa455e0aa03dfa65619c4df6bae1cf327cba925b1d233fc4
languageName: node
linkType: hard
@@ -1697,22 +1697,20 @@ __metadata:
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.8.4":
- version: 7.26.0
- resolution: "@babel/runtime@npm:7.26.0"
- dependencies:
- regenerator-runtime: "npm:^0.14.0"
- checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699
+ version: 7.27.6
+ resolution: "@babel/runtime@npm:7.27.6"
+ checksum: 10/cc957a12ba3781241b83d528eb69ddeb86ca5ac43179a825e83aa81263a6b3eb88c57bed8a937cdeacfc3192e07ec24c73acdfea4507d0c0428c8e23d6322bfe
languageName: node
linkType: hard
-"@babel/template@npm:^7.0.0, @babel/template@npm:^7.22.5, @babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9, @babel/template@npm:^7.27.0, @babel/template@npm:^7.3.3":
- version: 7.27.0
- resolution: "@babel/template@npm:7.27.0"
+"@babel/template@npm:^7.0.0, @babel/template@npm:^7.22.5, @babel/template@npm:^7.25.9, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3":
+ version: 7.27.2
+ resolution: "@babel/template@npm:7.27.2"
dependencies:
- "@babel/code-frame": "npm:^7.26.2"
- "@babel/parser": "npm:^7.27.0"
- "@babel/types": "npm:^7.27.0"
- checksum: 10/7159ca1daea287ad34676d45a7146675444d42c7664aca3e617abc9b1d9548c8f377f35a36bb34cf956e1d3610dcb7acfcfe890aebf81880d35f91a7bd273ee5
+ "@babel/code-frame": "npm:^7.27.1"
+ "@babel/parser": "npm:^7.27.2"
+ "@babel/types": "npm:^7.27.1"
+ checksum: 10/fed15a84beb0b9340e5f81566600dbee5eccd92e4b9cc42a944359b1aa1082373391d9d5fc3656981dff27233ec935d0bc96453cf507f60a4b079463999244d8
languageName: node
linkType: hard
@@ -1731,7 +1729,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4":
+"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4":
version: 7.27.6
resolution: "@babel/types@npm:7.27.6"
dependencies:
@@ -3350,6 +3348,7 @@ __metadata:
jest: "npm:^29.7.0"
metro-react-native-babel-preset: "npm:^0.77.0"
react: "npm:^18.2.0"
+ react-native: "npm:^0.72.15"
react-test-renderer: "npm:^18.3.1"
ts-jest: "npm:^29.2.5"
twrnc: "npm:^4.5.1"
@@ -9567,14 +9566,14 @@ __metadata:
linkType: hard
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7":
- version: 4.4.0
- resolution: "debug@npm:4.4.0"
+ version: 4.4.1
+ resolution: "debug@npm:4.4.1"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
- checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367
+ checksum: 10/8e2709b2144f03c7950f8804d01ccb3786373df01e406a0f66928e47001cf2d336cbed9ee137261d4f90d68d8679468c755e3548ed83ddacdc82b194d2468afe
languageName: node
linkType: hard
@@ -18822,13 +18821,6 @@ __metadata:
languageName: node
linkType: hard
-"regenerator-runtime@npm:^0.14.0":
- version: 0.14.1
- resolution: "regenerator-runtime@npm:0.14.1"
- checksum: 10/5db3161abb311eef8c45bcf6565f4f378f785900ed3945acf740a9888c792f75b98ecb77f0775f3bf95502ff423529d23e94f41d80c8256e8fa05ed4b07cf471
- languageName: node
- linkType: hard
-
"regenerator-transform@npm:^0.15.2":
version: 0.15.2
resolution: "regenerator-transform@npm:0.15.2"