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
45 changes: 45 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions docs/TEST-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Install Bun using the official **[Installation](https://bun.com/docs/installatio
| `bun install` | Install dependencies (run once after clone). **Use Bun only** — not npm/yarn/pnpm (see [`TECHNICAL.md`](TECHNICAL.md)). |
| `bun test` | Full suite: scanner unit tests, DB integration, Next API route tests. |
| `bun run test:scan` | `src/scan` + `src/lib` (scanner + `card-id` unit tests). |
| `bun run test:web` | Only `tests/web/**/*.test.ts`. |
| `bun run test:web` | Only `tests/web/**/*.{test.ts,test.tsx}`. |
| `bun run scan` | CLI smoke (requires `TARGET_REPO` and writable `CODEPIECE_DB` / default `data/`). |
| `bun run seed:samples` | Loads **`samples/mini-algorithms`** into **`data/codepiece.db`** with **`--force`** (handy after an empty DB + existing scan memory). |
| `bun run db:stats` | Read-only SQLite summary (**`CODEPIECE_DB`**): counts, swipes by action, per-user swipes, cards by **`repo_label`**, file sizes. |
Expand Down Expand Up @@ -64,9 +64,17 @@ Or: `TARGET_REPO=./samples/mini-algorithms bun run scan -- --force` if you need

These tests **do not** start the HTTP server; they call `GET`/`POST` exported functions with `Request` objects.

## UI
## UI / component tests (`tests/web/*.test.tsx`)

The swipe UI (`app/swipe-client.tsx`) is thin; API tests cover the contract it depends on. Optional follow-up: Playwright or component tests with `happy-dom` — not required for v1 in this repo.
Uses **happy-dom** + **React Testing Library** (devDependencies). Import **`tests/web/happy-dom-globals`** first so `document`, `localStorage`, and related globals exist before rendering.

| File | Covers |
|------|--------|
| `theme-defaults.test.ts` | **`CP_THEME_DEFAULT`** (`classic`) and **`CP_THEME_STORAGE_KEY`** (no DOM). |
| `ui-theme-picker.test.tsx` | **Theme** `<select>` option order (Original first), default value, `data-cp-theme` + **`localStorage`** on change. |
| `ui-dashboard-context.test.tsx` | **`DashboardStatsProvider`**: mocked **`fetch`** to `/api/dashboard/stats`, stats surface in UI; **`refreshDashboard(cardId)`** highlight clears after timeout. |

End-to-end browser tests (e.g. Playwright) are still optional for this repo.

## Manual smoke (after scan + dev)

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
"ts-morph": "^25.0.1"
},
"devDependencies": {
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@types/better-sqlite3": "^7.6.12",
"@types/node": "^22.10.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"drizzle-kit": "^0.30.1",
"happy-dom": "^20.8.9",
"typescript": "^5.7.2"
}
}
40 changes: 40 additions & 0 deletions tests/web/happy-dom-globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Registers happy-dom globals for React Testing Library (import before RTL / components).
* Safe to import from multiple UI test files; registers once.
*/
import { GlobalWindow } from 'happy-dom';

declare global {
var __CODEPIECE_HAPPY_WINDOW__: GlobalWindow | undefined;
}

if (globalThis.__CODEPIECE_HAPPY_WINDOW__ == null) {
const w = new GlobalWindow({ url: 'http://localhost:4000/' });
globalThis.__CODEPIECE_HAPPY_WINDOW__ = w;
globalThis.window = w as unknown as Window & typeof globalThis;
globalThis.document = w.document;
globalThis.navigator = w.navigator;
globalThis.HTMLElement = w.HTMLElement as unknown as typeof HTMLElement;
globalThis.Element = w.Element as unknown as typeof Element;
globalThis.Node = w.Node as unknown as typeof Node;
globalThis.Text = w.Text as unknown as typeof Text;
globalThis.DocumentFragment = w.DocumentFragment as unknown as typeof DocumentFragment;
globalThis.customElements = w.customElements;
globalThis.MutationObserver = w.MutationObserver;
globalThis.getComputedStyle = w.getComputedStyle.bind(w);
globalThis.requestAnimationFrame = w.requestAnimationFrame.bind(w);
globalThis.cancelAnimationFrame = w.cancelAnimationFrame.bind(w);
globalThis.FileReader = w.FileReader as unknown as typeof FileReader;
globalThis.HTMLSelectElement = w.HTMLSelectElement as unknown as typeof HTMLSelectElement;
globalThis.HTMLButtonElement = w.HTMLButtonElement as unknown as typeof HTMLButtonElement;
globalThis.HTMLTextAreaElement = w.HTMLTextAreaElement as unknown as typeof HTMLTextAreaElement;
globalThis.localStorage = w.localStorage;
globalThis.sessionStorage = w.sessionStorage;
}

if (document.body == null) {
const html = document.createElement('html');
const body = document.createElement('body');
html.appendChild(body);
document.appendChild(html);
}
12 changes: 12 additions & 0 deletions tests/web/theme-defaults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { describe, expect, test } from 'bun:test';
import { CP_THEME_DEFAULT, CP_THEME_STORAGE_KEY } from '../../app/theme-context';

describe('theme defaults (no DOM)', () => {
test('CP_THEME_DEFAULT is classic (Original)', () => {
expect(CP_THEME_DEFAULT).toBe('classic');
});

test('storage key is stable', () => {
expect(CP_THEME_STORAGE_KEY).toBe('cp-theme');
});
});
Loading
Loading