Skip to content

Commit 04e934a

Browse files
ci: add frontend tests to CI pipeline
- Add test-frontend job to ci-tests.yml - Add @vitest/coverage-v8 for coverage reporting - Add vitest.config.ts with coverage configuration - Upload frontend coverage to Codecov with 'frontend' flag - Only runs when app/ files change - Add app/coverage/ to .gitignore Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0fadf71 commit 04e934a

5 files changed

Lines changed: 184 additions & 1 deletion

File tree

.github/workflows/ci-tests.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,74 @@ jobs:
139139
- name: Skip notice
140140
if: steps.check.outputs.should_test == 'false'
141141
run: echo "::notice::Tests skipped - no testable Python changes (only plots/ or non-Python files)"
142+
143+
test-frontend:
144+
name: Run Frontend Tests
145+
runs-on: ubuntu-latest
146+
permissions:
147+
contents: read
148+
149+
steps:
150+
- uses: actions/checkout@v6
151+
with:
152+
fetch-depth: 0
153+
154+
- name: Check for frontend changes
155+
id: check
156+
run: |
157+
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
158+
BASE_SHA="${{ github.event.pull_request.base.sha }}"
159+
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
160+
CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA)
161+
else
162+
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
163+
fi
164+
165+
echo "Changed files:"
166+
echo "$CHANGED_FILES"
167+
168+
# Check for frontend changes
169+
FRONTEND_CHANGES=$(echo "$CHANGED_FILES" | grep '^app/' || true)
170+
171+
# Force run on workflow_dispatch with force_run=true
172+
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.force_run }}" == "true" ]]; then
173+
echo "Manual trigger with force_run=true, will run frontend tests"
174+
echo "should_test=true" >> $GITHUB_OUTPUT
175+
elif [[ -n "$FRONTEND_CHANGES" ]]; then
176+
echo "Found frontend changes, will run tests"
177+
echo "should_test=true" >> $GITHUB_OUTPUT
178+
else
179+
echo "No frontend changes, skipping frontend tests"
180+
echo "should_test=false" >> $GITHUB_OUTPUT
181+
fi
182+
183+
- name: Set up Node.js
184+
if: steps.check.outputs.should_test == 'true'
185+
uses: actions/setup-node@v4
186+
with:
187+
node-version: '20'
188+
cache: 'yarn'
189+
cache-dependency-path: app/yarn.lock
190+
191+
- name: Install dependencies
192+
if: steps.check.outputs.should_test == 'true'
193+
working-directory: app
194+
run: yarn install --frozen-lockfile
195+
196+
- name: Run frontend tests with coverage
197+
if: steps.check.outputs.should_test == 'true'
198+
working-directory: app
199+
run: yarn test --coverage
200+
201+
- name: Upload frontend coverage to Codecov
202+
if: steps.check.outputs.should_test == 'true'
203+
uses: codecov/codecov-action@v5
204+
with:
205+
token: ${{ secrets.CODECOV_TOKEN }}
206+
files: ./app/coverage/coverage-final.json
207+
flags: frontend
208+
fail_ci_if_error: false
209+
210+
- name: Skip notice
211+
if: steps.check.outputs.should_test == 'false'
212+
run: echo "::notice::Frontend tests skipped - no app/ changes"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ dist/
216216
*.tsbuildinfo
217217
.yarn/
218218
.pnp.*
219+
app/coverage/
219220

220221
# OS-specific files
221222
.DS_Store

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@types/react-dom": "^19.1.7",
3535
"@types/react-syntax-highlighter": "^15.5.13",
3636
"@vitejs/plugin-react-swc": "^4.0.0",
37+
"@vitest/coverage-v8": "^4.0.17",
3738
"typescript": "^5.9.2",
3839
"vite": "^7.3.1",
3940
"vitest": "^4.0.17"

app/vitest.config.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
globals: true,
6+
environment: 'node',
7+
include: ['src/**/*.test.ts'],
8+
coverage: {
9+
provider: 'v8',
10+
reporter: ['text', 'json', 'html'],
11+
include: ['src/utils/**/*.ts'],
12+
exclude: ['src/**/*.test.ts'],
13+
},
14+
},
15+
});

app/yarn.lock

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@
8787
"@babel/helper-string-parser" "^7.27.1"
8888
"@babel/helper-validator-identifier" "^7.28.5"
8989

90+
"@bcoe/v8-coverage@^1.0.2":
91+
version "1.0.2"
92+
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa"
93+
integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==
94+
9095
"@emotion/babel-plugin@^11.13.5":
9196
version "11.13.5"
9297
resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz"
@@ -342,7 +347,7 @@
342347
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz"
343348
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
344349

345-
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
350+
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.31":
346351
version "0.3.31"
347352
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz"
348353
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
@@ -722,6 +727,22 @@
722727
"@rolldown/pluginutils" "1.0.0-beta.47"
723728
"@swc/core" "^1.13.5"
724729

730+
"@vitest/coverage-v8@^4.0.17":
731+
version "4.0.17"
732+
resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.0.17.tgz#3bb100e9a6766de282049fba28e21a010a73509a"
733+
integrity sha512-/6zU2FLGg0jsd+ePZcwHRy3+WpNTBBhDY56P4JTRqUN/Dp6CvOEa9HrikcQ4KfV2b2kAHUFB4dl1SuocWXSFEw==
734+
dependencies:
735+
"@bcoe/v8-coverage" "^1.0.2"
736+
"@vitest/utils" "4.0.17"
737+
ast-v8-to-istanbul "^0.3.10"
738+
istanbul-lib-coverage "^3.2.2"
739+
istanbul-lib-report "^3.0.1"
740+
istanbul-reports "^3.2.0"
741+
magicast "^0.5.1"
742+
obug "^2.1.1"
743+
std-env "^3.10.0"
744+
tinyrainbow "^3.0.3"
745+
725746
"@vitest/expect@4.0.17":
726747
version "4.0.17"
727748
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.17.tgz#67bb0d4a7d37054590a19dcf831f7936d14a8a02"
@@ -785,6 +806,15 @@ assertion-error@^2.0.1:
785806
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
786807
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
787808

809+
ast-v8-to-istanbul@^0.3.10:
810+
version "0.3.10"
811+
resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz#ceff0094c8c64b9e04393c2377fd61857429ec04"
812+
integrity sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==
813+
dependencies:
814+
"@jridgewell/trace-mapping" "^0.3.31"
815+
estree-walker "^3.0.3"
816+
js-tokens "^9.0.1"
817+
788818
babel-plugin-macros@^3.1.0:
789819
version "3.1.0"
790820
resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz"
@@ -975,6 +1005,11 @@ fuse.js@^7.1.0:
9751005
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.1.0.tgz#306228b4befeee11e05b027087c2744158527d09"
9761006
integrity sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==
9771007

1008+
has-flag@^4.0.0:
1009+
version "4.0.0"
1010+
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
1011+
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
1012+
9781013
hasown@^2.0.2:
9791014
version "2.0.2"
9801015
resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz"
@@ -1017,6 +1052,11 @@ hoist-non-react-statics@^3.3.1:
10171052
dependencies:
10181053
react-is "^16.7.0"
10191054

1055+
html-escaper@^2.0.0:
1056+
version "2.0.2"
1057+
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
1058+
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
1059+
10201060
import-fresh@^3.2.1:
10211061
version "3.3.1"
10221062
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz"
@@ -1067,11 +1107,38 @@ is-hexadecimal@^2.0.0:
10671107
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz"
10681108
integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
10691109

1110+
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2:
1111+
version "3.2.2"
1112+
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
1113+
integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
1114+
1115+
istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
1116+
version "3.0.1"
1117+
resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
1118+
integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
1119+
dependencies:
1120+
istanbul-lib-coverage "^3.0.0"
1121+
make-dir "^4.0.0"
1122+
supports-color "^7.1.0"
1123+
1124+
istanbul-reports@^3.2.0:
1125+
version "3.2.0"
1126+
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93"
1127+
integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==
1128+
dependencies:
1129+
html-escaper "^2.0.0"
1130+
istanbul-lib-report "^3.0.0"
1131+
10701132
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
10711133
version "4.0.0"
10721134
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
10731135
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
10741136

1137+
js-tokens@^9.0.1:
1138+
version "9.0.1"
1139+
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4"
1140+
integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==
1141+
10751142
jsesc@^3.0.2:
10761143
version "3.1.0"
10771144
resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz"
@@ -1109,6 +1176,22 @@ magic-string@^0.30.21:
11091176
dependencies:
11101177
"@jridgewell/sourcemap-codec" "^1.5.5"
11111178

1179+
magicast@^0.5.1:
1180+
version "0.5.1"
1181+
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.5.1.tgz#518959aea78851cd35d4bb0da92f780db3f606d3"
1182+
integrity sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==
1183+
dependencies:
1184+
"@babel/parser" "^7.28.5"
1185+
"@babel/types" "^7.28.5"
1186+
source-map-js "^1.2.1"
1187+
1188+
make-dir@^4.0.0:
1189+
version "4.0.0"
1190+
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
1191+
integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
1192+
dependencies:
1193+
semver "^7.5.3"
1194+
11121195
ms@^2.1.3:
11131196
version "2.1.3"
11141197
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
@@ -1345,6 +1428,11 @@ scheduler@^0.27.0:
13451428
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz"
13461429
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
13471430

1431+
semver@^7.5.3:
1432+
version "7.7.3"
1433+
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
1434+
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
1435+
13481436
set-cookie-parser@^2.6.0:
13491437
version "2.7.2"
13501438
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68"
@@ -1390,6 +1478,13 @@ stylis@4.2.0:
13901478
resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz"
13911479
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
13921480

1481+
supports-color@^7.1.0:
1482+
version "7.2.0"
1483+
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
1484+
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
1485+
dependencies:
1486+
has-flag "^4.0.0"
1487+
13931488
supports-preserve-symlinks-flag@^1.0.0:
13941489
version "1.0.0"
13951490
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"

0 commit comments

Comments
 (0)