Skip to content

Commit 558cc20

Browse files
coopbriTwonarly1
andauthored
Test suite (#51)
* build(deps): add Playwright * build(deps,scripts): add Happy DOM, Bun types, RTL, unit and E2E test scripts * feature: add bunfig.toml, test setup * chore(knip): add test config * build(deps,scripts): add 'ms', dedicated Next.js dev script * build(deps): add msw, msw graphql codegen plugin * feature(graphql): add MSW plugin * docs(readme): update instructions for test env vars * feature: add Playwright config * ci: add test workflow * ci(test): set artifact name with shard ID * ci(test): set artifact path with shard ID * ci(test): correct shard artifacts (use proper job) * ci(test): resolve invalid character error * ci(test): resolve invalid character error * ci(test): iterate over uploaded blob reports for merge * ci(test): fix merge iteration * ci(test): fix merge iteration * ci(test): disable merge job, add discussion TODO * ci(test): update TODO * test(e2e,auth): add basic auth (sign in) test * ci(test): add test credential env vars * chore(test,auth): remove brackets * ci(test): add debug flag * docs(env): improve comment * ci(test): set Bun setup action version from v1 to v2, remove latest flag * test(e2e): add CI test tests * test(e2e): implement page object model pattern with fixtures * ci(test): fix Playwright shard execution * test(e2e,auth): remove example tests * ci(test): add env vars * ci(test): fix env var access typo * ci(test): inject '.env.test' to E2E job * feature(env): add test env var file * build(deps,scripts): create proxy unit test script, add GraphQL Codegen import types preset, add msw to trusted deps, upgrade deps * feature(scripts): add unit test execution script * chore(test,playwright): add TODO * test: add mocks * chore(test,e2e,auth): add TODO * docs: add JSDoc * test(e2e,msw): inject MSW Node.js server instance * chore(knip): update config * refactor(env): update exports, knip ignores * docs(graphql): fix typo * style(feature-card): center content * chore(graphql): update generated artifacts * fix: track graphql.mock.ts artifact * ci(test): add debug step * refactor(scripts,test): 'zsh' -> 'sh' * test: use zsh * fix: fix glob expansion for non-zsh shells * fix: fix glob expansion for non-zsh shells * fix: fix glob expansion for non-zsh shells * test: replace test shell script with package.json script * feature: add MSW browser service worker and config * test(mocks): add HIDRA and WalletConnect handlers * chore(e2e,auth): add warning about app router support * ci(test): remove Playwright debug flag * ci(test): conditionally upload test result artifacts * ci(test): disable sharding * build(deps,scripts): add RTL extensions, fix Bun test pattern match * test: enhance unit test setup file * test: add custom render implementation * test: add theme toggle tests * ci(test): inject WalletConnect project ID env var to unit test job * test(theme-toggle): add TODO * test(unit): add more providers to custom 'render' * test(msw): initialize MSW fixtures on app start behind feature flag * test: add home page (todo), landing page tests * test(mocks): add and inject Next.js app router mocks * docs: add JSDoc to test files * test: add skipped dashboard page test * docs: add MSW unit test integration reference article * docs(handlers,walletconnect): remove trailing slash * build(deps): upgrade web3 dependencies * chore(graphql): enable shared add plugin in mock artifacts * chore: format * docs(tests,theme-toggle): update TODO * docs(tests,theme-toggle): update TODO * ci: trigger build * chore(knip): update test patterns * docs(readme): remove instructions for test env vars * build(scripts): explicitly inject env var files into test scripts * chore(package): move msw config so it doesn't get lost in the sauce * build(deps): upgrade dependencies * chore: format * ci(test): remove E2E test artifact upload job * chore: update mock user ID env var name * build(deps): add ms DT types * fix(docker): fix build * build(deps): upgrade knip, typescript * chore(ts): set bun module types explicitly for language server support * chore: remove instances of `BlockchainProvider` in test files * build(deps): remove '@types/bun' * chore: format * build(deps): upgrade dependencies * chore: format * fix: fix build issues * ci: trigger build * chore: remove WalletConnect content * chore(playwright): remove unused import Co-authored-by: Beau Hawkinson <[email protected]> * ci(test): pin Bun 1.1.42 --------- Co-authored-by: Beau Hawkinson <[email protected]>
1 parent 022e2c0 commit 558cc20

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1995
-391
lines changed

.env.local.template

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ AUTH_KEYCLOAK_SECRET=""
44
# NB: an auth secret can be generated and injected automatically with `npx auth`, see https://cli.authjs.dev
55
AUTH_SECRET=""
66

7-
# NB: mocked user HIDRA ID from locally seeded database.
8-
# TODO: remove once keycloak / hidra are synced with database and mock data is no longer needed (https://linear.app/omnidev/issue/OMNI-147/replace-mock-user-id-flow-with-hidra-integration)
9-
NEXT_PUBLIC_MOCK_USER_HIDRA_ID=""
7+
# testing
8+
# username and password must be valid credentials for a user within the `test` realm
9+
TEST_USERNAME=""
10+
TEST_PASSWORD=""
11+
# whether to enable MSW for mocked network requests/responses
12+
ENABLE_MSW=""
13+
# mocked user ID from local, seeded database
14+
# TODO: remove once HIDRA is synced with database and mock data is no longer needed (https://linear.app/omnidev/issue/OMNI-147/replace-mock-user-id-flow-with-hidra-integration)
15+
NEXT_PUBLIC_MOCK_USER_ID=""

.env.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#test-environment-variables
2+
AUTH_KEYCLOAK_ISSUER="https://hidra.omni.dev/realms/test"
3+
NEXT_PUBLIC_API_BASE_URL="http://127.0.0.1:4000/graphql"
4+
APP_ENV="test"

.github/workflows/test.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Test 🧪
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
test_unit:
11+
name: Run unit tests 🃏
12+
timeout-minutes: 1
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: oven-sh/setup-bun@v2
17+
with:
18+
bun-version: 1.1.42
19+
# - uses: ArtiomTr/jest-coverage-report-action@v2
20+
# with:
21+
# package-manager: bun
22+
# test-script: bun test:coverage
23+
# annotations: failed-tests
24+
# TODO replace below with commented job above, blocked by Bun not yet supporting a JSON coverage reporter for the action to consume nor generate (similar: https://github.com/oven-sh/bun/issues/4099)
25+
- name: Install dependencies
26+
run: bun install
27+
- name: Run unit tests
28+
run: bun run test
29+
30+
test_e2e:
31+
name: Run E2E tests 🗺️
32+
timeout-minutes: 3
33+
runs-on: ubuntu-latest
34+
# TODO enable, disabled for now while E2E tests are simple. No need to shard early
35+
# strategy:
36+
# fail-fast: false
37+
# shard tests across 4 nodes (https://playwright.dev/docs/test-sharding)
38+
# matrix:
39+
# shard: [1/4, 2/4, 3/4, 4/4]
40+
steps:
41+
- uses: actions/checkout@v4
42+
- uses: oven-sh/setup-bun@v2
43+
- name: Install dependencies
44+
run: bun install
45+
- name: Install Playwright
46+
run: bunx playwright install --with-deps
47+
- name: Load test environment variables
48+
uses: falti/[email protected]
49+
with:
50+
path: .env.test
51+
- name: Run E2E tests
52+
# TODO enable, disabled for now while E2E tests are simple. No need to shard early
53+
# run: bun test:e2e --shard ${{ matrix.shard }}
54+
run: bun test:e2e
55+
env:
56+
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
57+
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
58+
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
59+
AUTH_KEYCLOAK_SECRET: ${{ secrets.AUTH_KEYCLOAK_SECRET }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ coverage
1313
!.env
1414
!.env.development
1515
!.env.production
16+
!.env.test
1617
!.env.local.template
1718

1819
# misc
1920
next-env.d.ts
2021

2122
**/generated/*
22-
!**/generated/.gitignore
23+
!**/generated/.gitignore

Dockerfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ FROM base AS deps
1010
WORKDIR /app
1111

1212
# install dependencies
13-
COPY package.json bun.lockb panda.config.ts ./
13+
COPY package.json bun.lock panda.config.ts ./
1414
# `apt` commands below are a workaround for blocked builds on some systems: https://github.com/oven-sh/bun/issues/9807#issuecomment-2218837172
1515
RUN apt update && apt install python3 python3-pip make g++ -y
1616
RUN bun install --frozen-lockfile
@@ -24,8 +24,6 @@ COPY . .
2424
# disable telemetry during build (https://nextjs.org/telemetry)
2525
ENV NEXT_TELEMETRY_DISABLED=1
2626

27-
ARG WALLETCONNECT_PROJECT_ID
28-
2927
RUN bun prepare
3028
RUN bun run build
3129

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Backfeed is an open-source feedback reporting platform.
44

55
## Local Development
66

7-
First, `cp .env.local.template` to `.env.local` and fill in the values.
7+
First, `cp .env.local.template .env.local` and fill in the values.
88

99
### Building and Running (Native)
1010

bun.lock

100755100644
Lines changed: 505 additions & 326 deletions
Large diffs are not rendered by default.

bunfig.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
telemetry = false
2+
3+
# https://bun.sh/docs/runtime/bunfig#test-runner
4+
[test]
5+
preload = "test/test.setup.ts"

knip.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,20 @@ const knipConfig: KnipConfig = {
1414
"src/app/{manifest,sitemap,robots}.ts",
1515
"src/app/**/{icon,apple-icon}.{ts,tsx}",
1616
"src/app/**/{opengraph,twitter}-image.{ts,tsx}",
17+
"src/test/**/*.{ts,tsx}",
1718
],
1819
// NB: files are reported as unused if they are in the set of project files, but not in the set of files resolved from the entry files. See: https://knip.dev/guides/configuring-project-files
1920
project: ["src/**/*.{ts,tsx}"],
2021
// NB: Modified from the default GraphQL Codegen configuration, see: https://knip.dev/reference/plugins/graphql-codegen
2122
"graphql-codegen": {
2223
config: ["package.json", "src/lib/graphql/codegen.config.ts"],
2324
},
24-
ignore: ["panda.config.ts", "src/generated/**"],
25+
ignore: ["panda.config.ts", "src/__mocks__/**", "src/generated/**"],
2526
ignoreDependencies: [
2627
// @omnidev/sigil peer dependency
2728
"@ark-ui/react",
29+
// included by Next.js metapackage, used in Playwright config
30+
"@next/env",
2831
// used by GraphQL Code Generator scripts
2932
"dotenv",
3033
],

next.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const nextConfig: NextConfig = {
44
// enable standalone mode for running in a container (https://github.com/vercel/next.js/tree/canary/examples/with-docker#in-existing-projects)
55
output: "standalone",
66
env: {
7-
WALLETCONNECT_PROJECT_ID: process.env.WALLETCONNECT_PROJECT_ID,
87
// below are safe to be public, but `NEXT_PUBLIC_` is not used because Auth.js uses these to automatically set its own Keycloak provider configuration
98
AUTH_KEYCLOAK_ID: process.env.AUTH_KEYCLOAK_ID,
109
AUTH_KEYCLOAK_ISSUER: process.env.AUTH_KEYCLOAK_ISSUER,

package.json

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,76 @@
22
"name": "backfeed",
33
"private": true,
44
"scripts": {
5-
"dev": "rm -rf .next && concurrently --kill-others --names \"GRAPHQL-CODEGEN,PANDA,CLIENT\" -c \"bgBlue.bold,bgYellow.bold,bgMagenta.bold\" \"bun graphql:generate:watch\" \"bun panda --watch\" \"next dev --turbo\"",
5+
"dev": "rm -rf .next && concurrently --kill-others --names \"GRAPHQL-CODEGEN,PANDA,CLIENT\" -c \"bgBlue.bold,bgYellow.bold,bgMagenta.bold\" \"bun graphql:generate:watch\" \"bun panda --watch\" \"bun dev:next\"",
6+
"dev:next": "next dev --turbo",
67
"build": "next build",
78
"start": "next start",
89
"check": "biome check src",
910
"format": "biome format --write src",
1011
"lint": "biome lint src",
12+
"test": "bun test --env-file .env.local --env-file .env.test .test",
13+
"test:e2e": "NODE_ENV=test bun run --env-file .env.local --env-file .env.test playwright test",
1114
"graphql:generate": "NODE_PATH=src/ dotenv -e .env.development -- graphql-codegen --config src/lib/graphql/codegen.config.ts",
1215
"graphql:generate:watch": "bun graphql:generate -w",
1316
"knip": "knip-bun --cache --tags=-knipignore",
1417
"prepare": "panda codegen && husky"
1518
},
19+
"msw": {
20+
"workerDirectory": ["public"]
21+
},
1622
"devDependencies": {
1723
"@biomejs/biome": "1.9.4",
1824
"@graphql-codegen/add": "^5.0.3",
1925
"@graphql-codegen/cli": "^5.0.3",
26+
"@graphql-codegen/import-types-preset": "^3.0.0",
2027
"@graphql-codegen/plugin-helpers": "^5.1.0",
2128
"@graphql-codegen/typescript": "^4.1.2",
2229
"@graphql-codegen/typescript-graphql-request": "^6.2.0",
30+
"@graphql-codegen/typescript-msw": "^3.0.0",
2331
"@graphql-codegen/typescript-operations": "^4.4.0",
2432
"@graphql-codegen/typescript-react-query": "^6.1.0",
2533
"@graphql-typed-document-node/core": "^3.2.0",
34+
"@happy-dom/global-registrator": "^16.6.0",
2635
"@pandacss/dev": "^0.51.1",
27-
"@types/node": "^22.10.5",
28-
"@types/react": "^19.0.2",
29-
"@types/react-dom": "^19.0.2",
36+
"@playwright/test": "^1.49.1",
37+
"@testing-library/jest-dom": "^6.6.3",
38+
"@testing-library/react": "^16.2.0",
39+
"@testing-library/user-event": "^14.6.0",
40+
"@types/bun": "^1.1.16",
41+
"@types/ms": "^0.7.34",
42+
"@types/node": "^22.10.7",
43+
"@types/react": "^19.0.7",
44+
"@types/react-dom": "^19.0.3",
3045
"concurrently": "^9.1.2",
31-
"dotenv": "^16.4.4",
46+
"dotenv": "^16.4.7",
3247
"dotenv-cli": "^8.0.0",
33-
"husky": "^9.1.6",
34-
"knip": "^5.37.1",
35-
"typescript": "^5.6.3"
48+
"husky": "^9.1.7",
49+
"knip": "^5.42.1",
50+
"msw": "^2.7.0",
51+
"next-router-mock": "^0.9.13",
52+
"typescript": "^5.7.3"
3653
},
3754
"dependencies": {
38-
"@ark-ui/react": "^4.4.3",
39-
"@omnidev/sigil": "^0.12.2",
40-
"@tanstack/react-form": "^0.41.0",
41-
"@tanstack/react-query": "^5.20.5",
42-
"@tanstack/react-query-devtools": "^5.20.5",
55+
"@ark-ui/react": "^4.8.1",
56+
"@omnidev/sigil": "^0.12.3",
57+
"@tanstack/react-form": "^0.41.2",
58+
"@tanstack/react-query": "^5.64.1",
59+
"@tanstack/react-query-devtools": "^5.64.1",
4360
"@theme-toggles/react": "^4.1.0",
44-
"dayjs": "^1.11.10",
61+
"dayjs": "^1.11.13",
4562
"graphql": "^16.10.0",
4663
"graphql-request": "^7.1.2",
47-
"next": "^15.0.3",
64+
"ms": "^2.1.3",
65+
"next": "^15.1.4",
4866
"next-auth": "^5.0.0-beta.25",
49-
"next-themes": "^0.4.3",
50-
"nuqs": "^2.3.0",
67+
"next-themes": "^0.4.4",
68+
"nuqs": "^2.3.1",
5169
"react": "^19.0.0",
5270
"react-dom": "^19.0.0",
53-
"react-icons": "^5.0.1",
71+
"react-icons": "^5.4.0",
5472
"react-infinite-scroll-hook": "^5.0.1",
55-
"recharts": "^2.13.3",
56-
"ts-pattern": "^5.5.0",
73+
"recharts": "^2.15.0",
74+
"ts-pattern": "^5.6.0",
5775
"usehooks-ts": "^3.1.0",
5876
"zod": "^3.24.1",
5977
"zustand": "^5.0.3"
@@ -64,6 +82,7 @@
6482
"bufferutil",
6583
"esbuild",
6684
"keccak",
85+
"msw",
6786
"sharp",
6887
"utf-8-validate"
6988
]

playwright.config.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { loadEnvConfig } from "@next/env";
2+
import { defineConfig } from "@playwright/test";
3+
import ms from "ms";
4+
5+
// import type {
6+
// PlaywrightTestOptions,
7+
// PlaywrightWorkerOptions,
8+
// Project,
9+
// } from "@playwright/test";
10+
11+
// TODO use Bun runtime instead of Node.js (pending https://github.com/oven-sh/bun/issues/8222). After this is resolved, consider using Playwright for running all tests including unit tests (see https://pkerschbaum.com/blog/using-playwright-to-run-unit-tests)
12+
13+
// type PlaywrightDevices = Project<
14+
// PlaywrightTestOptions,
15+
// PlaywrightWorkerOptions
16+
// >;
17+
18+
// load environment variables (see https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#loading-environment-variables-with-nextenv)
19+
loadEnvConfig(process.cwd());
20+
21+
/**
22+
* Desktop devices/viewports.
23+
*/
24+
// TODO enable desktop viewports (https://linear.app/omnidev/issue/OMNI-156/enable-more-playwright-devices)
25+
// const desktopDevices: PlaywrightDevices[] = [
26+
// {
27+
// name: "🖥️ Chromium",
28+
// use: {
29+
// ...devices["Desktop Chrome"],
30+
// },
31+
// },
32+
// {
33+
// name: "🖥️ Firefox",
34+
// use: {
35+
// ...devices["Desktop Firefox"],
36+
// },
37+
// },
38+
39+
// {
40+
// name: "🖥️ Safari (Webkit)",
41+
// use: {
42+
// ...devices["Desktop Safari"],
43+
// },
44+
// },
45+
// {
46+
// name: "🖥️ Microsoft Edge",
47+
// use: {
48+
// channel: "msedge",
49+
// },
50+
// },
51+
// ];
52+
53+
/**
54+
* Mobile devices/viewports.
55+
*/
56+
// TODO enable mobile viewports (https://linear.app/omnidev/issue/OMNI-156/enable-more-playwright-devices)
57+
// const mobileDevices: PlaywrightDevices[] = [
58+
// {
59+
// name: "📱 Chrome",
60+
// use: {
61+
// ...devices["Pixel 5"],
62+
// },
63+
// },
64+
// {
65+
// name: "📱 Safari",
66+
// use: devices["iPhone 12"],
67+
// },
68+
// ];
69+
70+
/**
71+
* Playwright configuration.
72+
* @see https://playwright.dev/docs/test-configuration
73+
*/
74+
const playwrightConfig = defineConfig({
75+
testDir: "src/__tests__",
76+
// maximum single-test timeout
77+
timeout: ms("3m"),
78+
expect: {
79+
timeout: ms("5s"),
80+
},
81+
// run tests within the same file in parallel
82+
fullyParallel: true,
83+
// TODO enable more devices than default (https://linear.app/omnidev/issue/OMNI-156/enable-more-playwright-devices)
84+
// projects: [...desktopDevices, ...mobileDevices],
85+
// test output reporter
86+
reporter: process.env.CI
87+
? // blob is used in CI to merge sharded test results (https://playwright.dev/docs/test-reporters#blob-reporter)
88+
"blob"
89+
: [["html", { outputFolder: "src/test/generated/report" }]],
90+
// fail build in CI if `test.only` is left in source code
91+
forbidOnly: !!process.env.CI,
92+
// number of retry attempts on test failure
93+
retries: process.env.CI ? 2 : undefined,
94+
// use single worker to mitigate flakiness from parallel tests. Note that this effectively disables parallelization across test files. See https://playwright.dev/docs/test-parallel#worker-processes
95+
workers: 1,
96+
// artifact output location (screenshots, videos, traces)
97+
outputDir: "src/generated/test-artifacts",
98+
// run dev server before starting the tests
99+
webServer: {
100+
// NB: no need to run GraphQL code generation, so just the Next.js server is started here
101+
command: "bun dev:next",
102+
port: (process.env.PORT as unknown as number) || 3000,
103+
timeout: ms("2m"),
104+
// do not use an existing server on CI
105+
reuseExistingServer: !process.env.CI,
106+
},
107+
use: {
108+
headless: true,
109+
baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || "http://localhost:3000",
110+
// retry a test with tracing if it is failing (allows analysis of DOM, console logs, network traffic, etc.)
111+
trace: "retry-with-trace",
112+
},
113+
});
114+
115+
export default playwrightConfig;

0 commit comments

Comments
 (0)