Skip to content

Commit

Permalink
Merge pull request #70 from white-label-loyalty/feature/testing
Browse files Browse the repository at this point in the history
Feature/testing
  • Loading branch information
iamgraeme authored Jan 27, 2025
2 parents f0ba1a3 + e3f8ca7 commit 99b81b5
Show file tree
Hide file tree
Showing 86 changed files with 4,838 additions and 353 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run tests
run: yarn test

- name: Type check
run: yarn type-check

- name: Build
run: yarn build
12 changes: 12 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ on:
types: [created]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.18.0
- run: yarn
- run: yarn test
- run: yarn build

publish-npm:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
32 changes: 32 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = api => {
const isTest = api.env('test');

return {
presets: [
['module:metro-react-native-babel-preset', { modules: false }]
],
plugins: [
['module-resolver', {
alias: {
'^react-native$': 'react-native-web'
}
}]
],
env: {
test: {
presets: [
['@babel/preset-env', { targets: { node: 'current' }, modules: 'commonjs' }],
'@babel/preset-typescript',
['@babel/preset-react', { runtime: 'automatic' }]
]
},
production: {
presets: [
['@babel/preset-env', { modules: false }],
'@babel/preset-typescript',
['@babel/preset-react', { runtime: 'automatic' }]
]
}
}
};
};
14 changes: 14 additions & 0 deletions babel.config.rollup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
presets: [
['@babel/preset-env', { modules: false }],
'@babel/preset-typescript',
['@babel/preset-react', { runtime: 'automatic' }]
],
plugins: [
['module-resolver', {
alias: {
'^react-native$': 'react-native-web'
}
}]
]
};
30 changes: 30 additions & 0 deletions bin/create-component
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,36 @@ Default.args = {
storyContent
);

// Create spec file
const specContent = `import React from 'react';
import { render } from '@testing-library/react-native';
import ${componentName} from './index';
describe('${componentName}', () => {
it('renders correctly', () => {
const { getByTestId } = render(<${componentName} />);
// Add your test assertions here
});
it('handles props correctly', () => {
// Add tests for your component props
});
it('handles user interactions', () => {
// Add tests for user interactions
});
it('handles edge cases', () => {
// Add tests for edge cases
});
});
`;

fs.writeFileSync(
path.join(componentDir, `${componentName}.spec.tsx`),
specContent
);

// Update index file
const indexFile = path.join(componentsDir, folderName, 'index.ts');
fs.mkdirSync(path.dirname(indexFile), { recursive: true });
Expand Down
25 changes: 25 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'd.ts'],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
'^.+\\.svg$': 'jest-transform-stub',
},
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'^react-native$': 'react-native-web',
'\\.(jpg|jpeg|png|gif|webp|svg)$': 'jest-transform-stub',
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
testPathIgnorePatterns: ['/node_modules/', '/dist/', '\\.d\\.ts$'],
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|react-native-web|@react-native|@react-native-community|@react-navigation)/)',
],
globals: {
'ts-jest': {
babelConfig: true,
},
},
};
22 changes: 22 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import '@testing-library/jest-dom';
import '@testing-library/jest-native/extend-expect';

// Suppress specific console warnings during tests
const originalConsoleWarn = console.warn;
console.warn = (...args) => {
// Add any warning messages you want to filter here
const warningsToSuppress = [
'accessibilityLabel',
'aria-label',
'style.resizeMode is deprecated'
];

// Check if the warning message includes any of the suppressed warnings
const shouldSuppress = warningsToSuppress.some(warning =>
args.some(arg => typeof arg === 'string' && arg.includes(warning))
);

if (!shouldSuppress) {
originalConsoleWarn(...args);
}
};
10 changes: 10 additions & 0 deletions lib/components/__test__/test-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '@testing-library/jest-dom';

declare global {
namespace jest {
interface Matchers<R> {
toBeInTheDocument(): R;
toBeEmptyDOMElement(): R;
}
}
}
30 changes: 30 additions & 0 deletions lib/components/__test__/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { render } from '@testing-library/react';
import React from 'react';
import { WllSdkProvider } from '../../context/WllSdkContext';
import { defaultTheme } from '../../utils/styling';

const defaultConfig = {
apiKey: 'test-api-key',
userToken: 'test-user-token',
};

const AllTheProviders = ({ children }: { children: React.ReactNode }) => {
return (
<WllSdkProvider
config={defaultConfig}
theme={defaultTheme}
navigationConfig={{}}
>
{children}
</WllSdkProvider>
);
};

const customRender = (ui: React.ReactElement, options = {}) =>
render(ui, { wrapper: AllTheProviders, ...options });

// re-export everything
export * from '@testing-library/react';

// override render method
export { customRender as render };
31 changes: 24 additions & 7 deletions lib/components/atoms/BaseBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { createContext, useContext } from 'react';
import { Pressable, StyleSheet } from 'react-native';
import { Pressable, StyleProp, StyleSheet, ViewStyle } from 'react-native';
import { MAX_WIDTH } from '../../../constants';
import { useWllSdk } from '../../../context/WllSdkContext';
import { useHandleTilePress } from '../../../hooks/useHandleTilePress';
Expand All @@ -18,31 +18,48 @@ export const useBannerContext = () => {
type BaseBannerProps = {
tile: Tile;
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
onPress?: () => void;
testID?: string;
disabled?: boolean;
accessibilityLabel?: string;
};

const BaseBanner = ({ tile, children }: BaseBannerProps): JSX.Element => {
const BaseBanner: React.FC<BaseBannerProps> = ({
tile,
children,
style,
testID,
accessibilityLabel,
}): JSX.Element => {
const { theme } = useWllSdk();
const { ctaLink, ctaLinkTarget, title } =
const { ctaLink, ctaLinkTarget, title, ctaText } =
tile.configuration as BannerTileConfig;

const handlePress = useHandleTilePress(tile, ctaLink, ctaLinkTarget);
const hasCTA = Boolean(ctaText);

return (
<BannerContext.Provider value={tile}>
<Pressable
testID={testID || 'banner-tile'}
style={({ pressed }) => [
styles.container,
style,
{
backgroundColor: theme.surface,
borderRadius: theme.sizes.borderRadiusLg,
opacity: pressed ? 0.7 : 1,
},
]}
onPress={handlePress}
disabled={!ctaLink}
onPress={hasCTA ? undefined : handlePress}
disabled={!ctaLink || hasCTA}
accessible={true}
accessibilityRole="button"
accessibilityLabel={`${title}${ctaLink ? ' - Click to open' : ''}`}
role={hasCTA ? 'article' : 'button'}
accessibilityLabel={
accessibilityLabel ||
`${title}${!hasCTA && ctaLink ? ' - Click to open' : ''}`
}
>
{children}
</Pressable>
Expand Down
1 change: 0 additions & 1 deletion lib/components/atoms/BaseTile/base-tile-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const BaseTileHeader = ({
textAlign: isHalfSize && 'center',
},
]}
accessibilityRole="header"
numberOfLines={1}
>
{children}
Expand Down
2 changes: 1 addition & 1 deletion lib/components/atoms/BaseTile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const BaseTileContainer = ({
tile.type !== 'REWARD' && tile.type !== 'REWARD_CATEGORY' && !ctaLink
}
accessible
accessibilityRole="button"
role="button"
accessibilityLabel={`${title}${ctaLink ? ' - Click to open' : ''}`}
>
{children}
Expand Down
11 changes: 6 additions & 5 deletions lib/components/atoms/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { Pressable, Text } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
import { Variant } from '../../../types/theme';
import { createVariantSystem } from '../../../utils/variant';
Expand Down Expand Up @@ -60,24 +60,25 @@ const Button = ({
const styles = useButtonDynamicStyles();

return (
<TouchableOpacity
style={[
<Pressable
style={({ pressed }) => [
styles.button,
buttonStyle,
{ borderRadius: theme.sizes.borderRadiusButton },
disabled && { opacity: 0.5 },
pressed && { opacity: 0.7 },
]}
onPress={onPress}
disabled={disabled}
accessible={true}
accessibilityRole="button"
role="button"
accessibilityLabel={accessibilityLabel || title}
accessibilityHint={accessibilityHint}
accessibilityState={{ disabled }}
testID={testID}
>
<Text style={[styles.text, textStyle]}>{title}</Text>
</TouchableOpacity>
</Pressable>
);
};

Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/Icon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import * as LucideIcons from 'lucide-react';
import * as React from 'react';
import { View, ViewProps } from 'react-native';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/Indicator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { useResponsive } from '../../../hooks/useResponsive';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/LoadingIndicator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import * as React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/Primatives/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
export { Column } from './Column';
export { FullFlex } from './FullFlex';
export { Layout } from './Layout';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/ProgressBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import * as React from 'react';
import { Animated, StyleSheet, View, ViewStyle } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/ProgressiveImage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import React, { useRef } from 'react';
import {
Animated,
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/Skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import * as React from 'react';
import { Animated, StyleSheet, View, ViewStyle } from 'react-native';
import { MAX_WIDTH } from '../../../constants';
Expand Down
1 change: 1 addition & 0 deletions lib/components/atoms/SkeletonTile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import * as React from 'react';
import { Animated, StyleSheet, ViewStyle } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
Expand Down
3 changes: 2 additions & 1 deletion lib/components/atoms/TileContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* istanbul ignore file */
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { GRID_GAP } from '../../../constants/grid';
Expand Down Expand Up @@ -32,7 +33,7 @@ const TILE_COMPONENTS: Record<TileType, React.ComponentType<{ tile: Tile }>> = {
*/
const TileContainer = ({ tiles }: TileContainerProps): JSX.Element => {
return (
<View style={styles.container}>
<View style={styles.container} testID="tile-container">
{tiles.map((tile, index) => {
const TileComponent = TILE_COMPONENTS[tile.type!];
const { isHalfSize } = useTileSize(tile);
Expand Down
Loading

0 comments on commit 99b81b5

Please sign in to comment.