docs(09): research CI pipeline hardening domain
Phase 9: CI Pipeline Hardening - Standard stack: Vitest + browser mode, Playwright, svelte-check - Architecture: Multi-project config for client/server tests - Pitfalls: jsdom limitations, database parallelism, SvelteKit mocking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
503
.planning/phases/09-ci-pipeline/09-RESEARCH.md
Normal file
503
.planning/phases/09-ci-pipeline/09-RESEARCH.md
Normal file
@@ -0,0 +1,503 @@
|
||||
# Phase 9: CI Pipeline Hardening - Research
|
||||
|
||||
**Researched:** 2026-02-03
|
||||
**Domain:** Testing infrastructure (Vitest, Playwright, svelte-check) + CI/CD (Gitea Actions)
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Summary
|
||||
|
||||
This phase establishes a comprehensive testing pipeline that blocks deployment on test or type failures. The research covers three testing layers: unit tests (Vitest), type checking (svelte-check), and E2E tests (Playwright), integrated into the existing Gitea Actions workflow.
|
||||
|
||||
The standard approach for SvelteKit testing in 2026 uses Vitest with browser mode for component tests (real browser via Playwright, not jsdom), traditional Vitest for server/utility tests, and standalone Playwright for full E2E tests. The user decisions lock in 80% coverage threshold, Playwright component tests for DOM interactions, and Slack notifications on failure.
|
||||
|
||||
Key finding: Vitest browser mode with `vitest-browser-svelte` is the modern approach for Svelte 5 component testing, replacing the older jsdom + @testing-library/svelte pattern. This provides real browser testing with runes support (`.svelte.test.ts` files).
|
||||
|
||||
**Primary recommendation:** Use multi-project Vitest configuration separating client (browser mode) and server (node) tests, with standalone Playwright for E2E, all gated before Docker build in CI.
|
||||
|
||||
## Standard Stack
|
||||
|
||||
The established libraries/tools for this domain:
|
||||
|
||||
### Core
|
||||
| Library | Version | Purpose | Why Standard |
|
||||
|---------|---------|---------|--------------|
|
||||
| vitest | ^3.x | Unit/component test runner | Official SvelteKit recommendation, Vite-native |
|
||||
| @vitest/browser | ^3.x | Browser mode for component tests | Real browser testing without jsdom limitations |
|
||||
| vitest-browser-svelte | ^0.x | Svelte component rendering in browser mode | Official Svelte 5 support with runes |
|
||||
| @vitest/browser-playwright | ^3.x | Playwright provider for Vitest browser mode | Real Chrome DevTools Protocol, not simulated events |
|
||||
| @vitest/coverage-v8 | ^3.x | V8-based coverage collection | Fast native coverage, identical accuracy to Istanbul since v3.2 |
|
||||
| @playwright/test | ^1.58 | E2E test framework | Already installed, mature E2E solution |
|
||||
| svelte-check | ^4.x | TypeScript/Svelte type checking | Already installed, CI-compatible output |
|
||||
|
||||
### Supporting
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| @testing-library/svelte | ^5.x | Alternative component testing | Only if not using browser mode (jsdom fallback) |
|
||||
| drizzle-seed | ^0.x | Database seeding for tests | E2E test fixtures with Drizzle ORM |
|
||||
|
||||
### Alternatives Considered
|
||||
| Instead of | Could Use | Tradeoff |
|
||||
|------------|-----------|----------|
|
||||
| Vitest browser mode | jsdom + @testing-library/svelte | jsdom simulates browser, misses real CSS/runes issues |
|
||||
| v8 coverage | istanbul | istanbul 300% slower, v8 now equally accurate |
|
||||
| Playwright for E2E | Cypress | Playwright already in project, better multi-browser support |
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
npm install -D vitest @vitest/browser vitest-browser-svelte @vitest/browser-playwright @vitest/coverage-v8 drizzle-seed
|
||||
npx playwright install chromium
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Recommended Project Structure
|
||||
```
|
||||
src/
|
||||
├── lib/
|
||||
│ ├── components/
|
||||
│ │ ├── Button.svelte
|
||||
│ │ └── Button.svelte.test.ts # Component tests (browser mode)
|
||||
│ ├── utils/
|
||||
│ │ ├── format.ts
|
||||
│ │ └── format.test.ts # Utility tests (node mode)
|
||||
│ └── server/
|
||||
│ ├── db/
|
||||
│ │ └── queries.test.ts # Server tests (node mode)
|
||||
│ └── api.test.ts
|
||||
├── routes/
|
||||
│ └── +page.server.test.ts # Server route tests (node mode)
|
||||
tests/
|
||||
├── e2e/ # Playwright E2E tests
|
||||
│ ├── fixtures/
|
||||
│ │ └── db.ts # Database seeding fixture
|
||||
│ ├── user-journeys.spec.ts
|
||||
│ └── index.ts # Custom test with fixtures
|
||||
├── docker-deployment.spec.ts # Existing deployment tests
|
||||
vitest-setup-client.ts # Browser mode setup
|
||||
vitest.config.ts # Multi-project config (or in vite.config.ts)
|
||||
playwright.config.ts # E2E config (already exists)
|
||||
```
|
||||
|
||||
### Pattern 1: Multi-Project Vitest Configuration
|
||||
**What:** Separate test projects for different environments (browser vs node)
|
||||
**When to use:** SvelteKit apps with both client components and server code
|
||||
**Example:**
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
// Source: https://scottspence.com/posts/testing-with-vitest-browser-svelte-guide
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { playwright } from '@vitest/browser-playwright';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
test: {
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
include: ['src/**/*.{ts,svelte}'],
|
||||
exclude: ['src/**/*.test.ts', 'src/**/*.spec.ts'],
|
||||
thresholds: {
|
||||
global: {
|
||||
statements: 80,
|
||||
branches: 80,
|
||||
functions: 80,
|
||||
lines: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
name: 'client',
|
||||
testTimeout: 5000,
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
},
|
||||
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
|
||||
setupFiles: ['./vitest-setup-client.ts'],
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
name: 'server',
|
||||
environment: 'node',
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 2: Component Test with Runes Support
|
||||
**What:** Test Svelte 5 components with $state and $derived in real browser
|
||||
**When to use:** Any component using Svelte 5 runes
|
||||
**Example:**
|
||||
```typescript
|
||||
// src/lib/components/Counter.svelte.test.ts
|
||||
// Source: https://svelte.dev/docs/svelte/testing
|
||||
import { render } from 'vitest-browser-svelte';
|
||||
import { page } from '@vitest/browser/context';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { flushSync } from 'svelte';
|
||||
import Counter from './Counter.svelte';
|
||||
|
||||
describe('Counter Component', () => {
|
||||
it('increments count on click', async () => {
|
||||
render(Counter, { props: { initial: 0 } });
|
||||
|
||||
const button = page.getByRole('button', { name: /increment/i });
|
||||
await button.click();
|
||||
|
||||
// flushSync needed for external state changes
|
||||
flushSync();
|
||||
|
||||
await expect.element(page.getByText('Count: 1')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 3: E2E Database Fixture with Drizzle
|
||||
**What:** Seed database before tests, clean up after
|
||||
**When to use:** E2E tests requiring known data state
|
||||
**Example:**
|
||||
```typescript
|
||||
// tests/e2e/fixtures/db.ts
|
||||
// Source: https://mainmatter.com/blog/2025/08/21/mock-database-in-svelte-tests/
|
||||
import { test as base } from '@playwright/test';
|
||||
import { db } from '../../../src/lib/server/db/index.js';
|
||||
import * as schema from '../../../src/lib/server/db/schema.js';
|
||||
import { reset, seed } from 'drizzle-seed';
|
||||
|
||||
export const test = base.extend<{
|
||||
seededDb: typeof db;
|
||||
}>({
|
||||
seededDb: async ({}, use) => {
|
||||
// Seed with known test data
|
||||
await seed(db, schema, { count: 10 });
|
||||
|
||||
await use(db);
|
||||
|
||||
// Clean up after test
|
||||
await reset(db, schema);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from '@playwright/test';
|
||||
```
|
||||
|
||||
### Pattern 4: SvelteKit Module Mocking
|
||||
**What:** Mock $app/stores and $app/navigation in unit tests
|
||||
**When to use:** Testing components that use SvelteKit-specific imports
|
||||
**Example:**
|
||||
```typescript
|
||||
// vitest-setup-client.ts
|
||||
// Source: https://www.closingtags.com/blog/mocking-svelte-stores-in-vitest
|
||||
/// <reference types="@vitest/browser/matchers" />
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
// Mock $app/navigation
|
||||
vi.mock('$app/navigation', () => ({
|
||||
goto: vi.fn(() => Promise.resolve()),
|
||||
invalidate: vi.fn(() => Promise.resolve()),
|
||||
invalidateAll: vi.fn(() => Promise.resolve()),
|
||||
beforeNavigate: vi.fn(),
|
||||
afterNavigate: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock $app/stores
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: writable({
|
||||
url: new URL('http://localhost'),
|
||||
params: {},
|
||||
route: { id: null },
|
||||
status: 200,
|
||||
error: null,
|
||||
data: {},
|
||||
form: null,
|
||||
}),
|
||||
navigating: writable(null),
|
||||
updated: { check: vi.fn(), subscribe: writable(false).subscribe },
|
||||
}));
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
- **Testing with jsdom for Svelte 5 components:** jsdom cannot properly handle runes reactivity. Use browser mode instead.
|
||||
- **Parallel E2E tests with shared database:** Will cause race conditions. Set `workers: 1` in playwright.config.ts.
|
||||
- **Using deprecated @playwright/experimental-ct-svelte:** Use vitest-browser-svelte instead for component tests.
|
||||
- **Mocking everything in E2E tests:** E2E tests should test real integrations. Only mock external services if necessary.
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
Problems that look simple but have existing solutions:
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| Coverage collection | Custom instrumentation | @vitest/coverage-v8 | Handles source maps, thresholds, reporters automatically |
|
||||
| Database seeding | Manual INSERT statements | drizzle-seed | Generates consistent, seeded random data with schema awareness |
|
||||
| Component mounting | Manual DOM manipulation | vitest-browser-svelte render() | Handles Svelte 5 lifecycle, context, and cleanup |
|
||||
| Screenshot on failure | Custom error handlers | Playwright built-in `screenshot: 'only-on-failure'` | Integrated with test lifecycle and artifacts |
|
||||
| CI test output parsing | Regex parsing | svelte-check --output machine | Structured, timestamp-prefixed output designed for CI |
|
||||
|
||||
**Key insight:** The testing ecosystem has mature solutions for all common needs. Hand-rolling any of these leads to edge cases around cleanup, async timing, and framework integration that the official tools have already solved.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: jsdom Limitations with Svelte 5 Runes
|
||||
**What goes wrong:** Tests pass locally but fail to detect reactivity issues, or throw cryptic errors about $state
|
||||
**Why it happens:** jsdom simulates browser APIs but doesn't actually run JavaScript in a browser context. Svelte 5 runes compile differently and expect real browser reactivity.
|
||||
**How to avoid:** Use Vitest browser mode with Playwright provider for all `.svelte` component tests
|
||||
**Warning signs:** Tests involving $state, $derived, or $effect behave inconsistently or require excessive `await tick()`
|
||||
|
||||
### Pitfall 2: Missing flushSync for External State
|
||||
**What goes wrong:** Assertions fail because DOM hasn't updated after state change
|
||||
**Why it happens:** Svelte batches updates. When state changes outside component (e.g., store update in test), DOM update is async.
|
||||
**How to avoid:** Call `flushSync()` from 'svelte' after modifying external state before asserting
|
||||
**Warning signs:** Tests that work with longer timeouts but fail with short ones
|
||||
|
||||
### Pitfall 3: Parallel E2E with Shared Database
|
||||
**What goes wrong:** Flaky tests that sometimes pass, sometimes fail with data conflicts
|
||||
**Why it happens:** Multiple test workers modify the same database simultaneously
|
||||
**How to avoid:** Set `workers: 1` in playwright.config.ts for E2E tests. Use separate database per worker if parallelism is needed.
|
||||
**Warning signs:** Tests pass individually but fail in full suite runs
|
||||
|
||||
### Pitfall 4: Coverage Threshold Breaking Existing Code
|
||||
**What goes wrong:** CI fails immediately after enabling 80% threshold because existing code has 0% coverage
|
||||
**Why it happens:** Enabling coverage thresholds on existing codebase without tests
|
||||
**How to avoid:** Start with `thresholds: { autoUpdate: true }` to establish baseline, then incrementally raise thresholds as tests are added
|
||||
**Warning signs:** Immediate CI failure when coverage is first enabled
|
||||
|
||||
### Pitfall 5: SvelteKit Module Import Errors
|
||||
**What goes wrong:** Tests fail with "Cannot find module '$app/stores'" or similar
|
||||
**Why it happens:** $app/* modules are virtual modules provided by SvelteKit at build time, not available in test environment
|
||||
**How to avoid:** Mock all $app/* imports in vitest setup file. Keep mocks simple (don't use importOriginal with SvelteKit modules - causes SSR issues).
|
||||
**Warning signs:** Import errors mentioning $app, $env, or other SvelteKit virtual modules
|
||||
|
||||
### Pitfall 6: Playwright Browsers Not Installed in CI
|
||||
**What goes wrong:** CI fails with "browserType.launch: Executable doesn't exist"
|
||||
**Why it happens:** Playwright browsers need explicit installation, not included in npm install
|
||||
**How to avoid:** Add `npx playwright install --with-deps chromium` step before tests
|
||||
**Warning signs:** Works locally (where browsers are cached), fails in fresh CI environment
|
||||
|
||||
## Code Examples
|
||||
|
||||
Verified patterns from official sources:
|
||||
|
||||
### vitest-setup-client.ts
|
||||
```typescript
|
||||
// Source: https://vitest.dev/guide/browser/
|
||||
/// <reference types="@vitest/browser/matchers" />
|
||||
/// <reference types="@vitest/browser/providers/playwright" />
|
||||
```
|
||||
|
||||
### Package.json Scripts
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"test:unit": "vitest run",
|
||||
"test:unit:watch": "vitest",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:e2e": "playwright test",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CI Workflow (Gitea Actions)
|
||||
```yaml
|
||||
# Source: https://docs.gitea.com/usage/actions/quickstart
|
||||
name: Test and Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, main]
|
||||
pull_request:
|
||||
branches: [master, main]
|
||||
|
||||
env:
|
||||
REGISTRY: git.kube2.tricnet.de
|
||||
IMAGE_NAME: admin/taskplaner
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run type check
|
||||
run: npm run check -- --output machine
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
- name: Run unit tests with coverage
|
||||
run: npm run test:coverage
|
||||
|
||||
- name: Run E2E tests
|
||||
run: npm run test:e2e
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Upload test artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: |
|
||||
coverage/
|
||||
playwright-report/
|
||||
test-results/
|
||||
|
||||
build:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.merged == true
|
||||
steps:
|
||||
# ... existing build steps ...
|
||||
|
||||
notify:
|
||||
needs: [test, build]
|
||||
runs-on: ubuntu-latest
|
||||
if: failure()
|
||||
steps:
|
||||
- name: Notify Slack on failure
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
run: |
|
||||
curl -X POST -H 'Content-type: application/json' \
|
||||
--data '{"text":"Pipeline failed for ${{ gitea.repository }} on ${{ gitea.ref }}"}' \
|
||||
$SLACK_WEBHOOK_URL
|
||||
```
|
||||
|
||||
### Playwright Config for E2E with Screenshots
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
// Source: https://playwright.dev/docs/test-configuration
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './tests/e2e',
|
||||
fullyParallel: false, // Sequential for shared database
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: 1, // Single worker for database tests
|
||||
reporter: [
|
||||
['html', { open: 'never' }],
|
||||
['github'], // GitHub/Gitea compatible annotations
|
||||
],
|
||||
use: {
|
||||
baseURL: process.env.BASE_URL || 'http://localhost:5173',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'off', // Per user decision: screenshots only, no video
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium-desktop',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'chromium-mobile',
|
||||
use: { ...devices['Pixel 5'] },
|
||||
},
|
||||
],
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### svelte-check CI Output Format
|
||||
```bash
|
||||
# Machine-readable output for CI parsing
|
||||
# Source: https://svelte.dev/docs/cli/sv-check
|
||||
npx svelte-check --output machine --tsconfig ./tsconfig.json
|
||||
|
||||
# Output format:
|
||||
# 1590680326283 ERROR "/path/file.svelte" 10:5 "Type error message"
|
||||
# 1590680326807 COMPLETED 50 FILES 2 ERRORS 0 WARNINGS
|
||||
```
|
||||
|
||||
## State of the Art
|
||||
|
||||
| Old Approach | Current Approach | When Changed | Impact |
|
||||
|--------------|------------------|--------------|--------|
|
||||
| jsdom + @testing-library/svelte | Vitest browser mode + vitest-browser-svelte | 2025 | Real browser testing, runes support |
|
||||
| Istanbul coverage | V8 coverage with AST remapping | Vitest 3.2 | 10x faster, same accuracy |
|
||||
| @playwright/experimental-ct-svelte | vitest-browser-svelte | 2025 | Better integration, official support |
|
||||
| Jest with svelte-jester | Vitest | 2024 | Native Vite support, faster |
|
||||
|
||||
**Deprecated/outdated:**
|
||||
- `vitest-svelte-kit` package: Deprecated, no longer needed with modern Vitest
|
||||
- `@playwright/experimental-ct-svelte`: Use vitest-browser-svelte for component tests instead
|
||||
- `jsdom` for Svelte 5 components: Does not properly support runes reactivity
|
||||
|
||||
## Open Questions
|
||||
|
||||
Things that couldn't be fully resolved:
|
||||
|
||||
1. **Exact drizzle-seed API for this schema**
|
||||
- What we know: drizzle-seed works with Drizzle ORM schemas
|
||||
- What's unclear: Specific configuration for the project's schema structure
|
||||
- Recommendation: Review drizzle-seed docs during implementation with actual schema
|
||||
|
||||
2. **Gitea Actions Slack notification action availability**
|
||||
- What we know: GitHub Actions Slack actions exist (rtCamp/action-slack-notify, etc.)
|
||||
- What's unclear: Whether these work identically in Gitea Actions
|
||||
- Recommendation: Use direct curl to Slack webhook (shown in examples) for maximum compatibility
|
||||
|
||||
3. **Vitest browser mode stability**
|
||||
- What we know: Vitest documents browser mode as "experimental" with stable core
|
||||
- What's unclear: Edge cases in production CI environments
|
||||
- Recommendation: Pin Vitest version, monitor for issues
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- [Svelte Official Testing Docs](https://svelte.dev/docs/svelte/testing) - Official Vitest + browser mode recommendations
|
||||
- [Vitest Guide](https://vitest.dev/guide/) - Installation, configuration, browser mode
|
||||
- [Vitest Coverage Config](https://vitest.dev/config/coverage) - Threshold configuration
|
||||
- [Vitest Browser Mode](https://vitest.dev/guide/browser/) - Playwright provider setup
|
||||
- [svelte-check CLI](https://svelte.dev/docs/cli/sv-check) - CI output formats
|
||||
- [Gitea Actions Quickstart](https://docs.gitea.com/usage/actions/quickstart) - Workflow syntax
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- [Scott Spence - Vitest Browser Mode Guide](https://scottspence.com/posts/testing-with-vitest-browser-svelte-guide) - Multi-project configuration
|
||||
- [Mainmatter - Database Fixtures](https://mainmatter.com/blog/2025/08/21/mock-database-in-svelte-tests/) - Drizzle seed pattern
|
||||
- [Roy Bakker - Playwright CI Guide](https://www.roybakker.dev/blog/playwright-in-ci-with-github-actions-and-docker-endtoend-guide) - Artifact upload, caching
|
||||
- [@testing-library/svelte Setup](https://testing-library.com/docs/svelte-testing-library/setup/) - Alternative jsdom approach
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- Slack webhook notification patterns from various blog posts - curl approach is safest
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH - Official Svelte docs explicitly recommend Vitest + browser mode
|
||||
- Architecture: HIGH - Multi-project pattern documented in Vitest and community guides
|
||||
- Pitfalls: HIGH - Common issues well-documented in GitHub issues and guides
|
||||
- E2E fixtures: MEDIUM - Drizzle-seed pattern documented but specific schema integration untested
|
||||
|
||||
**Research date:** 2026-02-03
|
||||
**Valid until:** 2026-03-03 (Vitest browser mode evolving, re-verify before major updates)
|
||||
Reference in New Issue
Block a user