Compare commits

...
8 Commits
Author SHA1 Message Date
github-actions[bot] 1809020501 chore: bump version to 0.17.0 2026-03-10 08:53:40 +00:00
Frederic CabassutandClaude Opus 4.6 bd4016c998 feat: add coverage summary to GitHub Actions job summary
Write a markdown coverage table to $GITHUB_STEP_SUMMARY for both unit
and E2E test jobs. Uses a small bash script that parses json-summary
output from the coverage reporters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:30:08 +01:00
Frederic CabassutandClaude Opus 4.6 4ef5dbf20b feat: add code coverage with artifact upload for unit and E2E tests
Add V8 coverage for unit tests via @vitest/coverage-v8 and Istanbul
instrumentation for E2E tests via vite-plugin-istanbul. Coverage data
is collected from browser pages after each Playwright test and merged
with nyc. CI uploads both coverage reports as GitHub Actions artifacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:30:08 +01:00
Frederic CabassutandClaude Opus 4.6 00c748a17b chore: set e2e retries to 0 — fix flaky tests, don't mask them
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:50:33 +01:00
Frederic CabassutandClaude Opus 4.6 f63cb6db1d perf: speed up e2e tests in CI
- Pre-build before Playwright instead of building inside webServer
- Reduce retries from 2 to 1 (fix flaky tests instead of masking them)
- Set CI workers to 4 for more parallelism
- Reduce artifact retention from 30 to 7 days

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:46:22 +01:00
github-actions[bot] 329061fe7e chore: pin docker-compose to v0.16.1 [skip ci] 2026-03-10 06:40:49 +00:00
github-actions[bot] c638dce753 chore: bump version to 0.16.1 2026-03-10 06:36:44 +00:00
Frederic CabassutandClaude Opus 4.6 7918c1ecc2 fix: update e2e test for renamed API settings tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:36:03 +01:00
15 changed files with 1080 additions and 13 deletions
+36 -2
View File
@@ -46,7 +46,18 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm test:unit
- run: pnpm test:unit:coverage
- name: Coverage summary
if: always()
run: bash scripts/coverage-summary.sh "Unit Test Coverage" coverage/unit/coverage-summary.json
- uses: actions/upload-artifact@v4
if: always()
with:
name: unit-coverage
path: coverage/unit/
retention-days: 14
e2e-tests:
name: E2E Tests
@@ -80,9 +91,25 @@ jobs:
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
- name: Build (with coverage instrumentation)
run: pnpm build
env:
DATABASE_URL: ./data/test-crumbs.db
NODE_ENV: test
VITE_COVERAGE: 'true'
- run: pnpm test:e2e
env:
CI: true
VITE_COVERAGE: 'true'
- name: Merge E2E coverage
if: always()
run: npx nyc report
- name: Coverage summary
if: always()
run: bash scripts/coverage-summary.sh "E2E Test Coverage" coverage/e2e/coverage-summary.json
- uses: actions/upload-artifact@v4
if: always()
@@ -91,7 +118,14 @@ jobs:
path: |
playwright-report/
test-results/
retention-days: 30
retention-days: 7
- uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-coverage
path: coverage/e2e/
retention-days: 14
docker-build:
name: Docker Build
+4
View File
@@ -32,6 +32,10 @@ vite.config.ts.timestamp-*
/test-results
/playwright-report
# Coverage
/coverage
.nyc_output
# Claude Code
/.claude/
+7
View File
@@ -0,0 +1,7 @@
{
"report-dir": "coverage/e2e",
"temp-dir": "coverage/e2e/raw",
"reporter": ["text", "html", "lcov", "json-summary"],
"include": ["src/**"],
"exclude": ["node_modules", "tests"]
}
+8 -1
View File
@@ -1,4 +1,5 @@
.PHONY: help dev build preview check test test-unit test-e2e lint \
.PHONY: help dev build preview check test test-unit test-e2e \
test-unit-coverage test-e2e-coverage lint \
db-push db-generate db-migrate db-studio docs-api \
install docker-build docker-up docker-down docker-logs clean \
release release-patch release-minor release-major
@@ -27,6 +28,12 @@ test-unit: ## Run unit tests only
test-e2e: ## Run e2e tests only
pnpm test:e2e
test-unit-coverage: ## Run unit tests with coverage
pnpm test:unit:coverage
test-e2e-coverage: ## Run e2e tests with coverage
VITE_COVERAGE=true pnpm build && pnpm test:e2e && npx nyc report
lint: ## Run linter
pnpm lint
+1 -1
View File
@@ -1,6 +1,6 @@
services:
crumbs:
image: ghcr.io/bretzel-app/crumbs:0.15.0
image: ghcr.io/bretzel-app/crumbs:0.16.1
container_name: crumbs
ports:
- "3000:3000"
+7 -1
View File
@@ -1,7 +1,7 @@
{
"name": "crumbs",
"private": true,
"version": "0.16.0",
"version": "0.17.0",
"type": "module",
"license": "MIT",
"author": "Bretzel (https://bretzel.app)",
@@ -19,7 +19,10 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest run",
"test:unit:watch": "vitest",
"test:unit:coverage": "vitest run --coverage",
"test:e2e": "playwright test",
"test:e2e:coverage": "VITE_COVERAGE=true pnpm build && pnpm test:e2e && nyc report",
"coverage:merge-e2e": "nyc merge coverage/e2e/raw coverage/e2e/merged.json && nyc report",
"test": "pnpm test:unit && pnpm test:e2e",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate",
@@ -68,13 +71,16 @@
"@types/uuid": "^10.0.0",
"@types/zxcvbn": "^4.4.5",
"@vite-pwa/sveltekit": "^1.1.0",
"@vitest/coverage-v8": "^3.2.4",
"drizzle-kit": "^0.31.9",
"nyc": "^18.0.0",
"svelte": "^5.53.7",
"svelte-check": "^4.4.4",
"tailwindcss": "^4.2.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-istanbul": "^7.2.1",
"vitest": "^3.2.4",
"yaml": "^2.8.2"
},
+3 -2
View File
@@ -4,7 +4,8 @@ export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
retries: 0,
workers: process.env.CI ? 4 : undefined,
reporter: 'html',
globalSetup: './tests/e2e/global-setup.ts',
use: {
@@ -27,7 +28,7 @@ export default defineConfig({
}
],
webServer: {
command: 'pnpm build && pnpm preview --port 4173',
command: 'pnpm preview --port 4173',
port: 4173,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
+925
View File
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Writes a coverage summary table to $GITHUB_STEP_SUMMARY (or stdout if not in CI).
# Usage: scripts/coverage-summary.sh <title> <json-summary-path>
set -euo pipefail
TITLE="${1:?Usage: coverage-summary.sh <title> <json-summary-path>}"
JSON="${2:?Usage: coverage-summary.sh <title> <json-summary-path>}"
if [ ! -f "$JSON" ]; then
echo "Coverage summary not found: $JSON" >&2
exit 0
fi
# Resolve to absolute path for require()
ABS_JSON="$(cd "$(dirname "$JSON")" && pwd)/$(basename "$JSON")"
# Extract totals with node (available in CI, no jq dependency)
read -r stmts branches funcs lines < <(node -e "
const s = require('$ABS_JSON').total;
console.log(s.statements.pct, s.branches.pct, s.functions.pct, s.lines.pct);
")
TABLE="## $TITLE
| Metric | Coverage |
|--------|----------|
| Statements | ${stmts}% |
| Branches | ${branches}% |
| Functions | ${funcs}% |
| Lines | ${lines}% |
"
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
echo "$TABLE" >> "$GITHUB_STEP_SUMMARY"
else
echo "$TABLE"
fi
+24
View File
@@ -0,0 +1,24 @@
import { writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import type { Page } from '@playwright/test';
const COVERAGE_DIR = join(process.cwd(), 'coverage', 'e2e', 'raw');
let counter = 0;
/**
* Collect Istanbul coverage data from the browser page.
* The vite-plugin-istanbul injects `window.__coverage__` when COVERAGE=true.
*/
export async function collectCoverage(page: Page, testTitle: string) {
if (process.env.VITE_COVERAGE !== 'true') return;
const coverage = await page.evaluate(() => (window as any).__coverage__);
if (!coverage) return;
mkdirSync(COVERAGE_DIR, { recursive: true });
const safeName = testTitle.replace(/[^a-zA-Z0-9-_]/g, '_');
const filename = `coverage-${safeName}-${counter++}-${Date.now()}.json`;
writeFileSync(join(COVERAGE_DIR, filename), JSON.stringify(coverage));
}
+4 -1
View File
@@ -1,4 +1,5 @@
import { test as base, expect, type Page, type Locator } from '@playwright/test';
import { collectCoverage } from './coverage';
const TEST_EMAIL = 'admin@test.com';
const TEST_PASSWORD = 'testpassword123';
@@ -13,10 +14,12 @@ export function noteCard(page: Page, title: string): Locator {
* Extended test fixture that handles setup/auth.
*/
export const test = base.extend<{ authenticatedPage: Page }>({
authenticatedPage: async ({ page }, use) => {
authenticatedPage: async ({ page }, use, testInfo) => {
// Setup: create user and login
await setupAndLogin(page);
await use(page);
// Teardown: collect coverage data from the browser
await collectCoverage(page, testInfo.title);
}
});
+3 -3
View File
@@ -57,10 +57,10 @@ test.describe('Settings — API Key Management', () => {
// Given the user is on the settings page
await page.goto('/settings');
// When the user clicks the MCP Server nav link
await page.getByRole('link', { name: 'MCP Server' }).click();
// When the user clicks the API nav link
await page.getByRole('link', { name: 'API' }).click();
// Then the MCP settings page is shown
// Then the API settings page is shown
await expect(page).toHaveURL('/settings/mcp');
await expect(page.getByRole('heading', { name: 'API Keys' })).toBeVisible();
+12
View File
@@ -1,6 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { SvelteKitPWA } from '@vite-pwa/sveltekit';
import tailwindcss from '@tailwindcss/vite';
import istanbul from 'vite-plugin-istanbul';
import { defineConfig } from 'vite';
export default defineConfig({
@@ -10,6 +11,17 @@ export default defineConfig({
plugins: [
tailwindcss(),
sveltekit(),
...(process.env.VITE_COVERAGE === 'true'
? [
istanbul({
include: 'src/**/*',
exclude: ['node_modules', 'tests/', '**/*.test.ts'],
extension: ['.ts', '.svelte'],
requireEnv: true,
forceBuildInstrument: true
})
]
: []),
SvelteKitPWA({
registerType: 'autoUpdate',
manifest: {
+8 -1
View File
@@ -9,6 +9,13 @@ export default defineConfig({
},
test: {
include: ['src/**/*.test.ts'],
environment: 'node'
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov', 'json-summary'],
reportsDirectory: './coverage/unit',
include: ['src/lib/**/*.ts'],
exclude: ['src/lib/**/*.test.ts', 'src/lib/types/**']
}
}
});
+1 -1
View File
@@ -253,7 +253,7 @@
<li><a href="https://github.com/bretzel-app/crumbs/blob/main/docs/DEPLOYMENT.md" target="_blank" rel="noopener">Docs</a></li>
<li><a href="https://github.com/bretzel-app/crumbs/blob/main/LICENSE" target="_blank" rel="noopener">License</a></li>
</ul>
<span class="footer-version">v0.16.0</span>
<span class="footer-version">v0.17.0</span>
</div>
</footer>