Skip to content

Commit 0536418

Browse files
chore: improved test structure
1 parent 5b96e9a commit 0536418

19 files changed

+453
-20
lines changed

TESTING.md

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# Testing Setup
2+
3+
This document describes the testing setup for the WalletConnect AppKit React Native project.
4+
5+
## Shared Jest Setup
6+
7+
To avoid duplication and ensure consistency across packages, we use a shared Jest setup approach:
8+
9+
### Structure
10+
11+
- `jest-shared-setup.ts`: Contains common mocks used across all packages
12+
- Package-specific `jest-setup.ts` files: Import the shared setup and add package-specific mocks
13+
14+
### How it works
15+
16+
1. The root `jest.config.ts` defines a moduleNameMapper that maps `@shared-jest-setup` to the shared setup file:
17+
18+
```js
19+
moduleNameMapper: {
20+
'^@shared-jest-setup$': '<rootDir>/jest-shared-setup.ts'
21+
}
22+
```
23+
24+
2. Each package's `jest.config.ts` overrides this mapping to use a relative path:
25+
26+
```js
27+
moduleNameMapper: {
28+
'^@shared-jest-setup$': '../../jest-shared-setup.ts'
29+
}
30+
```
31+
32+
3. Each package has its own `jest-setup.ts` file that imports the shared setup and only adds package-specific mocks:
33+
34+
```js
35+
// Import shared setup
36+
import '@shared-jest-setup';
37+
38+
// Import helper functions from shared setup (if needed)
39+
import { mockThemeContext, mockUseTheme } from '@shared-jest-setup';
40+
41+
// Apply package-specific mocks
42+
mockThemeContext('../src/context/ThemeContext');
43+
mockUseTheme('../src/hooks/useTheme');
44+
45+
// Add any other package-specific mocks here if needed
46+
```
47+
48+
### Shared Mocks
49+
50+
The shared setup includes mocks for:
51+
52+
- `@react-native-async-storage/async-storage`
53+
- React Native components and APIs (StyleSheet, Dimensions, Platform, etc.)
54+
- `react-native-svg` components
55+
- Helper functions for mocking package-specific modules
56+
57+
All common mocks are centralized in the shared setup file, eliminating duplication across packages. This makes the testing setup more maintainable and consistent.
58+
59+
### Adding New Mocks
60+
61+
To add a new mock that should be shared across packages:
62+
63+
1. Add it to `jest-shared-setup.ts`
64+
2. If it's a function that needs to be imported by packages, export it from `jest-shared-setup.ts`
65+
66+
For package-specific mocks, add them to the package's `jest-setup.ts` file.
67+
68+
### Type Declarations
69+
70+
Each package includes a type declaration file for the shared setup module:
71+
72+
```ts
73+
// types/shared-jest-setup.d.ts
74+
declare module '@shared-jest-setup' {
75+
export function mockThemeContext(modulePath: string): void;
76+
export function mockUseTheme(modulePath: string): void;
77+
}
78+
```
79+
80+
## Running Tests
81+
82+
To run tests for all packages:
83+
84+
```bash
85+
yarn test
86+
```
87+
88+
To run tests for a specific package:
89+
90+
```bash
91+
yarn workspace @reown/appkit-[package-name]-react-native test
92+
```
93+
94+
## Playwright Testing
95+
96+
For end-to-end testing of web interfaces (such as the web demo or web views within the React Native app), we use Playwright.
97+
98+
### Setup
99+
100+
1. Install Playwright:
101+
102+
```bash
103+
# Install Playwright and browsers
104+
npx playwright install
105+
```
106+
107+
2. Playwright tests are located in the `e2e` directory at the root of the project.
108+
109+
### Writing Tests
110+
111+
Playwright tests are written using the Playwright Test framework. Here's a basic example:
112+
113+
```typescript
114+
// e2e/example.spec.ts
115+
import { test, expect } from '@playwright/test';
116+
117+
test('basic test', async ({ page }) => {
118+
// Navigate to the page
119+
await page.goto('https://your-app-url.com');
120+
121+
// Interact with the page
122+
await page.click('text=Sign In');
123+
await page.fill('input[name="email"]', '[email protected]');
124+
await page.fill('input[name="password"]', 'password');
125+
await page.click('button[type="submit"]');
126+
127+
// Assert the result
128+
await expect(page.locator('.welcome-message')).toContainText('Welcome');
129+
});
130+
```
131+
132+
### Running Playwright Tests
133+
134+
To run all Playwright tests:
135+
136+
```bash
137+
npx playwright test
138+
```
139+
140+
To run a specific test file:
141+
142+
```bash
143+
npx playwright test e2e/example.spec.ts
144+
```
145+
146+
To run tests in headed mode (with browser visible):
147+
148+
```bash
149+
npx playwright test --headed
150+
```
151+
152+
To run tests in a specific browser:
153+
154+
```bash
155+
npx playwright test --browser=chromium
156+
# or
157+
npx playwright test --browser=firefox
158+
# or
159+
npx playwright test --browser=webkit
160+
```
161+
162+
### Debugging Playwright Tests
163+
164+
To debug tests:
165+
166+
1. Run with the `--debug` flag:
167+
168+
```bash
169+
npx playwright test --debug
170+
```
171+
172+
2. Use the Playwright Inspector to step through the test.
173+
174+
3. Add `await page.pause()` in your test to pause at a specific point.
175+
176+
### Generating Test Reports
177+
178+
To generate an HTML report:
179+
180+
```bash
181+
npx playwright test --reporter=html
182+
```
183+
184+
Then open the report:
185+
186+
```bash
187+
npx playwright show-report
188+
```
189+
190+
### Testing Mobile Web Views
191+
192+
For testing mobile web views:
193+
194+
```typescript
195+
test('mobile view test', async ({ browser }) => {
196+
// Create a new context with mobile device emulation
197+
const context = await browser.newContext({
198+
viewport: { width: 375, height: 667 },
199+
userAgent:
200+
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1'
201+
});
202+
203+
const page = await context.newPage();
204+
// Continue with your test...
205+
});
206+
```
207+
208+
### CI Integration
209+
210+
Playwright tests can be integrated into your CI pipeline. Here's an example GitHub Actions workflow:
211+
212+
```yaml
213+
name: Playwright Tests
214+
on:
215+
push:
216+
branches: [main, master]
217+
pull_request:
218+
branches: [main, master]
219+
jobs:
220+
test:
221+
timeout-minutes: 60
222+
runs-on: ubuntu-latest
223+
steps:
224+
- uses: actions/checkout@v3
225+
- uses: actions/setup-node@v3
226+
with:
227+
node-version: 16
228+
- name: Install dependencies
229+
run: yarn install
230+
- name: Install Playwright Browsers
231+
run: npx playwright install --with-deps
232+
- name: Run Playwright tests
233+
run: npx playwright test
234+
- uses: actions/upload-artifact@v3
235+
if: always()
236+
with:
237+
name: playwright-report
238+
path: playwright-report/
239+
retention-days: 30
240+
```
241+
242+
For more information, refer to the [Playwright documentation](https://playwright.dev/docs/intro).

babel.config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
module.exports = {
2-
presets: ['module:metro-react-native-babel-preset']
2+
presets: ['module:metro-react-native-babel-preset'],
3+
plugins: [
4+
'@babel/plugin-transform-flow-strip-types',
5+
['@babel/plugin-proposal-class-properties', { loose: true }],
6+
['@babel/plugin-proposal-private-methods', { loose: true }]
7+
]
38
};

jest-setup.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

jest-shared-setup.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Mock AsyncStorage
2+
jest.mock('@react-native-async-storage/async-storage', () =>
3+
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
4+
);
5+
6+
// Mock react-native modules that cause issues with Flow types
7+
jest.mock('react-native', () => {
8+
return {
9+
StyleSheet: {
10+
create: (styles: Record<string, any>) => styles,
11+
hairlineWidth: 1,
12+
absoluteFill: {},
13+
flatten: jest.fn()
14+
},
15+
Dimensions: {
16+
get: jest.fn().mockReturnValue({ width: 375, height: 812 }),
17+
addEventListener: jest.fn(),
18+
removeEventListener: jest.fn()
19+
},
20+
Platform: {
21+
OS: 'ios',
22+
select: jest.fn().mockImplementation((obj: { ios?: any; android?: any }) => obj.ios)
23+
},
24+
NativeModules: {
25+
StatusBarManager: { height: 44 }
26+
},
27+
Text: 'Text',
28+
View: 'View',
29+
TouchableOpacity: 'TouchableOpacity',
30+
TouchableWithoutFeedback: 'TouchableWithoutFeedback',
31+
TextInput: 'TextInput',
32+
ScrollView: 'ScrollView',
33+
Image: 'Image',
34+
Animated: {
35+
View: 'Animated.View',
36+
createAnimatedComponent: (component: string) => `Animated.${component}`,
37+
timing: jest.fn().mockReturnValue({ start: jest.fn() }),
38+
Value: jest.fn(() => ({
39+
interpolate: jest.fn(),
40+
setValue: jest.fn()
41+
}))
42+
},
43+
I18nManager: {
44+
isRTL: false
45+
},
46+
useColorScheme: jest.fn().mockReturnValue('light')
47+
};
48+
});
49+
50+
// Mock react-native-svg
51+
jest.mock('react-native-svg', () => {
52+
const mockSvg = () => 'Svg';
53+
mockSvg.Circle = () => 'Circle';
54+
mockSvg.Rect = () => 'Rect';
55+
mockSvg.Path = () => 'Path';
56+
mockSvg.G = () => 'G';
57+
mockSvg.Defs = () => 'Defs';
58+
mockSvg.LinearGradient = () => 'LinearGradient';
59+
mockSvg.Stop = () => 'Stop';
60+
61+
return mockSvg;
62+
});
63+
64+
// Export a function to mock package-specific modules
65+
export const mockThemeContext = (modulePath: string) => {
66+
jest.mock(
67+
modulePath,
68+
() => ({
69+
ThemeContext: {
70+
Provider: ({ children }: { children: React.ReactNode }) => children,
71+
Consumer: ({ children }: { children: Function }) => children({ themeMode: 'light' })
72+
},
73+
useTheme: jest.fn().mockReturnValue({
74+
themeMode: 'light',
75+
colors: {
76+
text: '#000000',
77+
background: '#FFFFFF',
78+
primary: '#3396FF'
79+
}
80+
})
81+
}),
82+
{ virtual: true }
83+
);
84+
};
85+
86+
export const mockUseTheme = (modulePath: string) => {
87+
jest.mock(
88+
modulePath,
89+
() => ({
90+
useTheme: jest.fn().mockReturnValue({
91+
themeMode: 'light',
92+
colors: {
93+
text: '#000000',
94+
background: '#FFFFFF',
95+
primary: '#3396FF'
96+
}
97+
})
98+
}),
99+
{ virtual: true }
100+
);
101+
};

jest.config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
module.exports = {
22
preset: 'react-native',
3-
modulePathIgnorePatterns: ['<rootDir>/node_modules', '<rootDir>/lib/']
3+
modulePathIgnorePatterns: ['<rootDir>/node_modules', '<rootDir>/lib/'],
4+
transformIgnorePatterns: [
5+
'node_modules/(?!(react-native|@react-native|react-native-.*|@react-native-.*)/)'
6+
],
7+
transform: {
8+
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { configFile: './babel.config.js' }]
9+
},
10+
moduleNameMapper: {
11+
'^@shared-jest-setup$': '<rootDir>/jest-shared-setup.ts'
12+
}
413
};

0 commit comments

Comments
 (0)