Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
name: CI

on:
workflow_dispatch:
pull_request:
branches: [main]

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
ci:
name: CI
Expand Down Expand Up @@ -32,3 +37,67 @@ jobs:

- name: Lint
run: pnpm run lint

test-headless:
name: Headless Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "lts/*"
cache: "pnpm"

- name: Setup uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck tests
run: pnpm run typecheck:test

- name: Run headless tests
run: pnpm run test:headless

test-e2e:
name: E2E Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "lts/*"
cache: "pnpm"

- name: Setup uv
uses: astral-sh/setup-uv@v6

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium

- name: Run deterministic E2E tests
run: pnpm run test:e2e

- name: Run LLM E2E tests
if: github.event_name == 'workflow_dispatch'
run: pnpm run test:e2e:llm
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ dist-ssr
/.claude/settings.local.json
/scratch/
/agent/.venv/
/tests/server/.venv/
__pycache__/
/tsconfig.tsbuildinfo
.env
*.tgz
/test-results/
/playwright-report/
/tmp/
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ uv run uvicorn chatbot.server:app # Start backend on port 8000

Note: Stop any logfire platform instances to avoid port 8000 conflicts.

**Testing:**

```bash
npm run test:headless # Vitest headless unit tests
npm run test:e2e # Deterministic Playwright E2E tests
npm run test:e2e:llm # LLM provider E2E tests (requires API keys)
```

Set `E2E_VIDEO=1` to record videos with slowMo for any E2E target. Set `E2E_TEST_DIR` to override which test directory Playwright runs.

## Architecture

### Frontend Structure
Expand Down
4 changes: 3 additions & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
"registries": {
"@ai-elements": "https://ai-sdk.dev/elements/api/registry/{name}.json"
}
}
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.2",
"language": "en",
"words": ["Pydantic", "pydantic"]
"words": ["logfire", "Pydantic", "pydantic", "uvicorn"]
}
15 changes: 14 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@ export default defineConfig(
globals: globals.node,
},
},
{ ignores: ['dist/**', 'server/**', 'node_modules/**', 'scratch/**', 'agent/**', 'commitlint.config.js'] },
{
ignores: [
'dist/**',
'server/**',
'node_modules/**',
'scratch/**',
'agent/**',
'tests/**',
'commitlint.config.js',
'playwright.config.ts',
'vitest.config.ts',
],
},
{
rules: {
'@typescript-eslint/no-unused-vars': [
Expand All @@ -45,6 +57,7 @@ export default defineConfig(
},
],
'@typescript-eslint/no-non-null-assertion': 'off',
'no-void': ['error', { allowAsStatement: true }],
},
},
{
Expand Down
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@
"scripts": {
"prepare": "lefthook install",
"typecheck": "tsc --noEmit",
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
"format": "prettier --write",
"lint": "eslint",
"lint-fix": "eslint --fix --quiet",
"dev": "BACKEND_PORT=38001 vite",
"dev:server": "cd agent && uv run uvicorn chatbot.server:app --port 38001",
"build": "tsc -b && vite build",
"preview": "vite preview"
"preview": "vite preview",
"test:headless": "vitest run --config vitest.config.ts",
"test:e2e": "E2E_TEST_DIR=tests/e2e/deterministic playwright test",
"test:e2e:llm": "E2E_TEST_DIR=tests/e2e/llm playwright test"
},
"files": [
"dist"
],
"dependencies": {
"@ai-sdk/react": "^2.0.125",
"@ai-sdk/react": "^3.0.0",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
Expand All @@ -44,16 +48,18 @@
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-query": "^5.90.19",
"@uidotdev/usehooks": "^2.4.1",
"ai": "^5.0.123",
"ai": "^6.0.116",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"embla-carousel-react": "^8.6.0",
"lucide-react": "^0.542.0",
"nanoid": "^5.1.6",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-syntax-highlighter": "^15.6.6",
"shiki": "^4.0.2",
"sonner": "^2.0.7",
"streamdown": "^1.6.11",
"tailwind-merge": "^3.4.0",
Expand All @@ -65,6 +71,7 @@
"@commitlint/cli": "^20.3.1",
"@commitlint/config-conventional": "^20.3.1",
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.58.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^13.1.3",
Expand All @@ -91,6 +98,7 @@
"vite": "^7.3.1",
"vite-bundle-analyzer": "^1.3.2",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.1.0",
"yaml-lint": "^1.7.0"
}
}
37 changes: 37 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, devices } from '@playwright/test'

const TEST_SERVER_PORT = 8787
const record = !!process.env.E2E_VIDEO

export default defineConfig({
testDir: process.env.E2E_TEST_DIR ?? 'tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
expect: {
timeout: 5_000,
},
use: {
...devices['Desktop Chrome'],
baseURL: `http://localhost:5173`,
trace: 'on-first-retry',
video: record ? 'on' : 'off',
launchOptions: record ? { slowMo: 500 } : {},
},
webServer: [
{
command: `cd tests/server && uv run uvicorn server:app --port ${TEST_SERVER_PORT}`,
port: TEST_SERVER_PORT,
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
{
command: `BACKEND_PORT=${TEST_SERVER_PORT} pnpm run dev`,
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
timeout: 15_000,
},
],
})
Loading