-
-
Notifications
You must be signed in to change notification settings - Fork 29
test: add Jest + RTL and Playwright E2E with example tests (fixes #24) #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| module.exports = { | ||
| useAppKit: () => ({ open: () => {} }), | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| const React = require('react'); | ||
|
|
||
| // Return a simple SVG React component for any named export | ||
| module.exports = new Proxy({}, { | ||
| get: (_target, prop) => { | ||
| if (prop === '__esModule') return true; | ||
| return (props) => React.createElement('svg', { ...props, 'data-icon': String(prop) }); | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,6 @@ | ||||||
| import { test, expect } from '@playwright/test'; | ||||||
|
|
||||||
| test('homepage shows brand', async ({ page }) => { | ||||||
| await page.goto('http://localhost:3000'); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use baseURL from Playwright config instead of hardcoded URL. The test uses a hardcoded Add export default defineConfig({
testDir: './e2e',
+ use: {
+ baseURL: 'http://localhost:3000',
- use: {
headless: true,Then update the test to use relative URLs: - await page.goto('http://localhost:3000');
+ await page.goto('/');📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| await expect(page.locator('text=FairFund')).toBeVisible(); | ||||||
| }); | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| const nextJest = require('next/jest'); | ||
|
|
||
| const createJestConfig = nextJest({ dir: './' }); | ||
|
|
||
| const customJestConfig = { | ||
| roots: ['<rootDir>/src'], | ||
| setupFiles: ['<rootDir>/jest.mocks.js'], | ||
| testEnvironment: 'jsdom', | ||
| setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'], | ||
| moduleNameMapper: { | ||
| '\\.(css|less|scss|sass)$': 'identity-obj-proxy', | ||
| '^lucide-react(.*)$': '<rootDir>/__mocks__/lucide-react.js', | ||
| '^@reown/appkit/react$': '<rootDir>/__mocks__/@reown-appkit-react.js', | ||
| }, | ||
| }; | ||
|
|
||
| module.exports = createJestConfig(customJestConfig); | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||
| // Mocks to run before modules are imported | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Mock wagmi hooks used in components | ||||||||||||||||||||
| jest.mock('wagmi', () => ({ | ||||||||||||||||||||
| useAccount: () => ({ isConnected: false }), | ||||||||||||||||||||
| useWalletClient: () => ({ data: null }), | ||||||||||||||||||||
| useChainId: () => undefined, | ||||||||||||||||||||
| })); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Mock @reown/appkit react hook used for wallet modal | ||||||||||||||||||||
| jest.mock('@reown/appkit/react', () => ({ | ||||||||||||||||||||
| useAppKit: () => ({ open: jest.fn() }), | ||||||||||||||||||||
| })); | ||||||||||||||||||||
|
Comment on lines
+11
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate mocking strategy for This module is mocked here with Recommend consolidating to a single approach—either use Apply this diff to remove the duplicate mock (keep only moduleNameMapper): -// Mock @reown/appkit react hook used for wallet modal
-jest.mock('@reown/appkit/react', () => ({
- useAppKit: () => ({ open: jest.fn() }),
-}));
-📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| // Mock lucide-react icons (ESM) to prevent ESM parsing issues in Jest | ||||||||||||||||||||
| jest.mock('lucide-react', () => { | ||||||||||||||||||||
| const React = require('react'); | ||||||||||||||||||||
| return new Proxy({}, { | ||||||||||||||||||||
| get: (_target, prop) => { | ||||||||||||||||||||
| if (prop === '__esModule') return true; | ||||||||||||||||||||
| return (props) => React.createElement('svg', { ...props, 'data-icon': String(prop) }); | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
Comment on lines
+16
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate mocking strategy for This module is mocked here with Consolidate to a single mocking approach to avoid confusion and potential conflicts. Apply this diff to remove the duplicate mock (keep only moduleNameMapper): -// Mock lucide-react icons (ESM) to prevent ESM parsing issues in Jest
-jest.mock('lucide-react', () => {
- const React = require('react');
- return new Proxy({}, {
- get: (_target, prop) => {
- if (prop === '__esModule') return true;
- return (props) => React.createElement('svg', { ...props, 'data-icon': String(prop) });
- },
- });
-});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Setup file executed before tests to mock environment and modules | ||
| require('@testing-library/jest-dom'); | ||
|
|
||
| // Mock wagmi hooks used in components | ||
| jest.mock('wagmi', () => ({ | ||
| useAccount: () => ({ isConnected: false }), | ||
| useWalletClient: () => ({ data: null }), | ||
| useChainId: () => undefined, | ||
| })); | ||
|
|
||
| // Mock @reown/appkit react hook used for wallet modal | ||
| jest.mock('@reown/appkit/react', () => ({ | ||
| useAppKit: () => ({ open: jest.fn() }), | ||
| })); | ||
|
|
||
| // Mock lucide-react icons (ESM) to prevent ESM parsing issues in Jest | ||
| jest.mock('lucide-react', () => { | ||
| const React = require('react'); | ||
| return new Proxy({}, { | ||
| get: (_target, prop) => { | ||
| if (prop === '__esModule') return true; | ||
| return (props) => React.createElement('svg', { ...props, 'data-icon': String(prop) }); | ||
| }, | ||
| }); | ||
| }); | ||
|
Comment on lines
+17
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate mock: consolidate lucide-react mocking. This Proxy-based lucide-react mock is identical to the one in Recommended approach:
Jest automatically uses manual mocks from 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| import '@testing-library/jest-dom'; | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,11 @@ | |||||||||||||||||||||||||||||||||||||
| "start": "next start", | ||||||||||||||||||||||||||||||||||||||
| "lint": "next lint", | ||||||||||||||||||||||||||||||||||||||
| "format": "prettier --write .", | ||||||||||||||||||||||||||||||||||||||
| "test": "jest --passWithNoTests", | ||||||||||||||||||||||||||||||||||||||
| "test:watch": "jest --watch", | ||||||||||||||||||||||||||||||||||||||
| "test:coverage": "jest --coverage", | ||||||||||||||||||||||||||||||||||||||
| "e2e": "playwright test", | ||||||||||||||||||||||||||||||||||||||
| "e2e:headed": "playwright test --headed", | ||||||||||||||||||||||||||||||||||||||
| "addMockSpaces": "ts-node --compiler-options '{\"module\":\"CommonJS\"}' src/mocks/add-spaces.ts", | ||||||||||||||||||||||||||||||||||||||
| "cleanDB": "ts-node --compiler-options '{\"module\":\"CommonJS\"}' src/mocks/remove.ts" | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -52,6 +57,15 @@ | |||||||||||||||||||||||||||||||||||||
| "zod": "^3.23.8" | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||||||||||||||
| "@testing-library/jest-dom": "^6.0.1", | ||||||||||||||||||||||||||||||||||||||
| "@testing-library/react": "^14.0.0", | ||||||||||||||||||||||||||||||||||||||
| "@testing-library/user-event": "^14.4.3", | ||||||||||||||||||||||||||||||||||||||
| "@types/jest": "^29.5.3", | ||||||||||||||||||||||||||||||||||||||
| "jest-environment-jsdom": "^28.0.0", | ||||||||||||||||||||||||||||||||||||||
| "identity-obj-proxy": "^3.0.0", | ||||||||||||||||||||||||||||||||||||||
| "jest": "^29.7.0", | ||||||||||||||||||||||||||||||||||||||
| "ts-jest": "^29.1.0", | ||||||||||||||||||||||||||||||||||||||
| "@playwright/test": "^1.43.0", | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n app/web-app/package.json | sed -n '55,75p'Repository: StabilityNexus/FairFund Length of output: 927 🌐 Web query:
💡 Result: Use the jest-environment-jsdom package that matches Jest 29 (i.e., any 29.x release). Jest 29’s environment package (jest-environment-jsdom@29.x) upgrades jsdom to v20, so install the 29.x variant: npm install --save-dev jest@29 jest-environment-jsdom@29 Reference: Jest v29 upgrade notes (jest-environment-jsdom -> jsdom v20). [1][2] Sources
Version mismatch: upgrade jest-environment-jsdom to match Jest 29. Line 64 specifies Apply this diff: "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.3",
- "jest-environment-jsdom": "^28.0.0",
+ "jest-environment-jsdom": "^29.7.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| "@faker-js/faker": "^8.4.1", | ||||||||||||||||||||||||||||||||||||||
| "@types/node": "^20", | ||||||||||||||||||||||||||||||||||||||
| "@types/react": "^18", | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { defineConfig } from '@playwright/test'; | ||
|
|
||
| export default defineConfig({ | ||
| testDir: './e2e', | ||
| use: { | ||
| headless: true, | ||
| viewport: { width: 1280, height: 720 }, | ||
| actionTimeout: 0, | ||
| navigationTimeout: 30000, | ||
| }, | ||
|
Comment on lines
+5
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Set a finite actionTimeout and add baseURL. Line 8 sets Apply this diff: use: {
headless: true,
viewport: { width: 1280, height: 720 },
- actionTimeout: 0,
+ baseURL: 'http://localhost:3000',
+ actionTimeout: 10000,
navigationTimeout: 30000,
},This sets a 10-second action timeout (adjust as needed) and adds 🤖 Prompt for AI Agents |
||
| webServer: { | ||
| command: 'npm run dev', | ||
| port: 3000, | ||
| reuseExistingServer: true, | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import React from 'react'; | ||
| import { render, screen } from '@testing-library/react'; | ||
|
|
||
| // Mocks for next/navigation and next-auth | ||
| jest.mock('next/navigation', () => ({ usePathname: () => '/' })); | ||
| jest.mock('next-auth/react', () => ({ useSession: () => ({ data: null }) })); | ||
|
|
||
| import Navbar from '../navbar'; | ||
|
|
||
| describe('Navbar', () => { | ||
| it('renders the brand', () => { | ||
| render(<Navbar />); | ||
| expect(screen.getByText(/FairFund/i)).toBeInTheDocument(); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent mock: use jest.fn() for testability.
This mock file returns
open: () => {}(a no-op function), butapp/web-app/jest.setup.js(line 13) mocks the same module withopen: jest.fn(). Usingjest.fn()allows tests to assert whetheropenwas called and with what arguments.Apply this diff:
module.exports = { - useAppKit: () => ({ open: () => {} }), + useAppKit: () => ({ open: jest.fn() }), };Note: Verify whether this
__mocks__file is actually loaded, asjest.setup.jsmay override it with ajest.mock()call.📝 Committable suggestion
🤖 Prompt for AI Agents