{"_id":"@amadeni/convex-e2e","_rev":"4-01428f49aa02e8c4715fc02e8127ae43","name":"@amadeni/convex-e2e","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@amadeni/convex-e2e","version":"0.1.0","keywords":["convex","e2e","testing","test-runner"],"author":{"name":"Amadeni","email":"nicolai.hallberg@amadeni.ai"},"license":"MIT","_id":"@amadeni/convex-e2e@0.1.0","maintainers":[{"name":"amadeni_ai","email":"admin@amadeni.ai"}],"homepage":"https://github.com/amadeni/convex-e2e#readme","bugs":{"url":"https://github.com/amadeni/convex-e2e/issues"},"bin":{"convex-e2e":"dist/cli.js"},"dist":{"shasum":"c190cd45a365c89e01c9f252a4efca59c4d05d71","tarball":"https://registry.npmjs.org/@amadeni/convex-e2e/-/convex-e2e-0.1.0.tgz","fileCount":44,"integrity":"sha512-3oX9rwdV5Y81ttDTwojvP5rrB0xvQ+RkdEv5Kt5pL/YvKQH7+AbWewppZ0etXBMjFN7eat9mFMTeLvIEPShwLg==","signatures":[{"sig":"MEYCIQCvlvj5SbUwEwwoZJU9YtYD3H9f74/1/QH/tH4bf1F4uwIhAOJmDbVxzNeaP7PvESSlIXM0O/H5T+JblZitKMyAq7Xv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":111288},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"b40f7cda8572499b83682df25bc9649a49199c18","scripts":{"ci":"pnpm run prettier:check && pnpm run lint && pnpm run ts && pnpm run check-spelling && pnpm run test","ts":"tsc --noEmit --incremental false","lint":"eslint src/ convex-helpers/","test":"vitest run","build":"tsc","prepare":"tsc","test:e2e":"bash fixtures/minimal/run-e2e.sh","check-spelling":"cspell '**/*.*' --no-progress","prepublishOnly":"pnpm run build","prettier:check":"prettier --check .","prettier:write":"prettier --write ."},"_npmUser":{"name":"amadeni_ai","email":"admin@amadeni.ai"},"repository":{"url":"git+https://github.com/amadeni/convex-e2e.git","type":"git"},"_npmVersion":"10.9.2","description":"Reusable, config-driven end-to-end test runner for Convex + Next.js projects","directories":{},"_nodeVersion":"22.14.0","dependencies":{"dotenv":"^16.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6","devDependencies":{"convex":"^1.32.0","cspell":"^9.7.0","eslint":"^10.0.2","vitest":"^4.0.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.0.0","@types/node":"^25.3.3","typescript-eslint":"^8.56.1"},"peerDependencies":{"convex":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/convex-e2e_0.1.0_1772472441505_0.3181065685793172","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@amadeni/convex-e2e","version":"0.1.1","keywords":["convex","e2e","testing","test-runner"],"author":{"name":"Amadeni","email":"nicolai.hallberg@amadeni.ai"},"license":"MIT","_id":"@amadeni/convex-e2e@0.1.1","maintainers":[{"name":"amadeni_ai","email":"admin@amadeni.ai"}],"homepage":"https://github.com/amadeni/convex-e2e#readme","bugs":{"url":"https://github.com/amadeni/convex-e2e/issues"},"bin":{"convex-e2e":"dist/cli.js"},"dist":{"shasum":"e1ba2cbf8ad0879efae6b100bf2a7c3784731b76","tarball":"https://registry.npmjs.org/@amadeni/convex-e2e/-/convex-e2e-0.1.1.tgz","fileCount":44,"integrity":"sha512-3R17c9NySWLXH5mLMLDXnGVKWYMxB9dR//1oAD3hk0Jasrr6mvtgW175ml+N5raWPeoKaY0bBpLdi9KT9gFn+A==","signatures":[{"sig":"MEUCIEVBJNtccE1T4CDJBpndCPDFxbNtoB5tMOkwPfEwse3KAiEAnkqqFVs0PFkKNAlVnOVzQdw6M8Mv+26vgFeoJcK5j+I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amadeni%2fconvex-e2e@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":111288},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"79e7fd819652237d023410944803437067b33eed","scripts":{"ci":"pnpm run prettier:check && pnpm run lint && pnpm run ts && pnpm run check-spelling && pnpm run test","ts":"tsc --noEmit --incremental false","lint":"eslint src/ convex-helpers/","test":"vitest run","build":"tsc","prepare":"tsc","test:e2e":"bash fixtures/minimal/run-e2e.sh","check-spelling":"cspell '**/*.*' --no-progress","prepublishOnly":"pnpm run build","prettier:check":"prettier --check .","prettier:write":"prettier --write ."},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05459145-7d72-4994-9647-1e60c2ffaa9e"}},"repository":{"url":"git+https://github.com/amadeni/convex-e2e.git","type":"git"},"_npmVersion":"11.11.0","description":"Reusable, config-driven end-to-end test runner for Convex + Next.js projects","directories":{},"_nodeVersion":"22.22.0","dependencies":{"dotenv":"^16.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6","devDependencies":{"convex":"^1.32.0","cspell":"^9.7.0","eslint":"^10.0.2","vitest":"^4.0.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.0.0","@types/node":"^25.3.3","typescript-eslint":"^8.56.1"},"peerDependencies":{"convex":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/convex-e2e_0.1.1_1772473637577_0.4000626356069914","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@amadeni/convex-e2e","version":"0.1.3","keywords":["convex","e2e","testing","test-runner"],"author":{"name":"Amadeni","email":"nicolai.hallberg@amadeni.ai"},"license":"MIT","_id":"@amadeni/convex-e2e@0.1.3","maintainers":[{"name":"amadeni_ai","email":"admin@amadeni.ai"}],"homepage":"https://github.com/amadeni/convex-e2e#readme","bugs":{"url":"https://github.com/amadeni/convex-e2e/issues"},"bin":{"convex-e2e":"dist/cli.js"},"dist":{"shasum":"904d43d87b00bdbef8ebb16010fe4244eaf523c2","tarball":"https://registry.npmjs.org/@amadeni/convex-e2e/-/convex-e2e-0.1.3.tgz","fileCount":44,"integrity":"sha512-jLC9T46ZtAQP+z9Ipmw+w8FdS3C/Sd0fQ0x52YO1I4HN7E+oMZD5tV4gieXxosKtPmECDFjl4uzMb9aHm2GAXw==","signatures":[{"sig":"MEQCIDiS8mWhv+TOy2Si1or62Faw/xaL9uoCnlzSUDF1GsPTAiBTQ9feMQ6UCI4GsJvVTfpFdXYe3II4B9IG4AcxzvuKOw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amadeni%2fconvex-e2e@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":109946},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"0aad09f3671f4d549f8a586b2e7be71973eac418","scripts":{"ci":"pnpm run prettier:check && pnpm run lint && pnpm run ts && pnpm run check-spelling && pnpm run test","ts":"tsc --noEmit --incremental false","lint":"eslint src/ convex-helpers/","test":"vitest run","build":"tsc","prepare":"tsc","release":"pnpm version patch && git push && git push --tags","test:e2e":"bash fixtures/minimal/run-e2e.sh","re-release":"git tag -d v$(node -p \"require('./package.json').version\") && git push origin :refs/tags/v$(node -p \"require('./package.json').version\") && git tag v$(node -p \"require('./package.json').version\") && git push --tags","release:major":"pnpm version major && git push && git push --tags","release:minor":"pnpm version minor && git push && git push --tags","check-spelling":"cspell '**/*.*' --no-progress","prepublishOnly":"pnpm run build","prettier:check":"prettier --check .","prettier:write":"prettier --write ."},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05459145-7d72-4994-9647-1e60c2ffaa9e"}},"repository":{"url":"git+https://github.com/amadeni/convex-e2e.git","type":"git"},"_npmVersion":"11.11.0","description":"Reusable, config-driven end-to-end test runner for Convex + Next.js projects","directories":{},"_nodeVersion":"22.22.0","dependencies":{"dotenv":"^16.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6","devDependencies":{"convex":"^1.32.0","cspell":"^9.7.0","eslint":"^10.0.2","vitest":"^4.0.18","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.0.0","@types/node":"^25.3.3","typescript-eslint":"^8.56.1"},"peerDependencies":{"convex":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/convex-e2e_0.1.3_1772527327283_0.33438071260823077","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@amadeni/convex-e2e","version":"0.1.4","description":"Reusable, config-driven end-to-end test runner for Convex + Next.js projects","main":"dist/index.js","types":"dist/index.d.ts","bin":{"convex-e2e":"dist/cli.js"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"tsc","prepare":"tsc","prepublishOnly":"pnpm run build","lint":"eslint src/ convex-helpers/","ts":"tsc --noEmit --incremental false","prettier:check":"prettier --check .","prettier:write":"prettier --write .","check-spelling":"cspell '**/*.*' --no-progress","test":"vitest run","test:e2e":"bash fixtures/minimal/run-e2e.sh","ci":"pnpm run prettier:check && pnpm run lint && pnpm run ts && pnpm run check-spelling && pnpm run test","release":"pnpm version patch && git push && git push --tags","release:minor":"pnpm version minor && git push && git push --tags","release:major":"pnpm version major && git push && git push --tags","re-release":"git tag -d v$(node -p \"require('./package.json').version\") && git push origin :refs/tags/v$(node -p \"require('./package.json').version\") && git tag v$(node -p \"require('./package.json').version\") && git push --tags"},"keywords":["convex","e2e","testing","test-runner"],"author":{"name":"Amadeni","email":"nicolai.hallberg@amadeni.ai"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/amadeni/convex-e2e.git"},"publishConfig":{"access":"public"},"peerDependencies":{"convex":">=1.0.0"},"dependencies":{"dotenv":"^16.0.0"},"devDependencies":{"@eslint/js":"^10.0.1","@types/node":"^25.3.3","convex":"^1.32.0","cspell":"^9.7.0","eslint":"^10.0.2","prettier":"^3.8.1","typescript":"^5.0.0","typescript-eslint":"^8.56.1","vitest":"^4.0.18"},"packageManager":"pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6","gitHead":"f319b827bd41c551e3c448e2f318448348a58754","_id":"@amadeni/convex-e2e@0.1.4","bugs":{"url":"https://github.com/amadeni/convex-e2e/issues"},"homepage":"https://github.com/amadeni/convex-e2e#readme","_nodeVersion":"22.22.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-NFryFhO8s0Il5wuiy37IIEVbXwR5hAvHztsUcGFMhMEzE83kd+lImbcc/0pYpLeSibYtOZmLHsKeh5wYRdNsKg==","shasum":"18b09ad87f19b0d163494b7353c6f39a4ab6dbac","tarball":"https://registry.npmjs.org/@amadeni/convex-e2e/-/convex-e2e-0.1.4.tgz","fileCount":44,"unpackedSize":110734,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amadeni%2fconvex-e2e@0.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCUeUTLSFNveHAQQyIXswalkpuCgPjNrhrprDP7jKx3vAIgD8qWXWEX1oVffBNultfACWijK4AR3BTYsloP+taaE1g="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05459145-7d72-4994-9647-1e60c2ffaa9e"}},"directories":{},"maintainers":[{"name":"amadeni_ai","email":"admin@amadeni.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/convex-e2e_0.1.4_1772795941827_0.561233285003252"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-02T17:27:21.412Z","modified":"2026-03-06T11:19:02.295Z","0.1.0":"2026-03-02T17:27:21.645Z","0.1.1":"2026-03-02T17:47:17.824Z","0.1.3":"2026-03-03T08:42:07.439Z","0.1.4":"2026-03-06T11:19:01.983Z"},"bugs":{"url":"https://github.com/amadeni/convex-e2e/issues"},"author":{"name":"Amadeni","email":"nicolai.hallberg@amadeni.ai"},"license":"MIT","homepage":"https://github.com/amadeni/convex-e2e#readme","keywords":["convex","e2e","testing","test-runner"],"repository":{"type":"git","url":"git+https://github.com/amadeni/convex-e2e.git"},"description":"Reusable, config-driven end-to-end test runner for Convex + Next.js projects","maintainers":[{"name":"amadeni_ai","email":"admin@amadeni.ai"}],"readme":"# @amadeni/convex-e2e\n\nA config-driven end-to-end test runner for [Convex](https://convex.dev) projects.\n\nThe package provides the **engine** -- test runner, auth manager, assertions, reporting, and local backend orchestration. Your project provides the **content** -- roles, seed data, Convex support functions, and test suites -- via a typed config object.\n\n## Install\n\n```bash\nnpm install @amadeni/convex-e2e\n# or\npnpm add @amadeni/convex-e2e\n```\n\n`convex` is a peer dependency and must be installed in your project.\n\n## Overview\n\n```\n┌─────────────────────────────────────────────────────┐\n│  Your Project                                       │\n│                                                     │\n│  e2e.config.ts         ← defineConfig({ ... })      │\n│  e2e.test.ts           ← entry point: run(config)   │\n│  e2e/suites/*.ts       ← your test suites           │\n│                                                     │\n│  convex/                                            │\n│    testAuth.ts         ← createTestSession (JWT)    │\n│    testSupport.ts      ← seed, clear, inspect fns   │\n│    http.ts             ← OIDC discovery for auth     │\n│    auth.config.ts      ← auth provider config        │\n│                                                     │\n│  run-e2e.sh            ← local backend wrapper       │\n└─────────────────────────────────────────────────────┘\n```\n\n## Quick Start\n\n### 1. Create a config\n\n```typescript\n// e2e.config.ts\nimport { defineConfig } from '@amadeni/convex-e2e';\nimport { suites } from './e2e/suites';\n\ntype Role = 'admin' | 'editor';\n\nexport const config = defineConfig<Role>({\n  projectName: 'My App',\n  roles: {\n    admin: 'admin@test.local',\n    editor: 'editor@test.local',\n  },\n  defaultRole: 'admin',\n  convexFunctions: {\n    createSession: 'testAuth:createTestSession',\n    seedBase: 'testSupport:seedBase',\n    seedAll: 'testSupport:seedAll',\n    clearAll: 'testSupport:clearAll',\n    getSeedIdMap: 'testSupport:getSeedIdMap',\n    deleteTracked: 'testSupport:deleteTracked',\n    listRecords: 'testSupport:listRecords',\n  },\n  loadSuites: () => suites,\n});\n```\n\n### 2. Create an entry point\n\n```typescript\n// e2e.test.ts\nimport { run } from '@amadeni/convex-e2e';\nimport { config } from './e2e.config';\n\nrun(config);\n```\n\n### 3. Write a test suite\n\n```typescript\n// e2e/suites/index.ts\nimport {\n  assertDefined,\n  assertMinLength,\n  type TestSuite,\n} from '@amadeni/convex-e2e';\nimport { api } from '../convex/_generated/api';\n\ntype Role = 'admin' | 'editor';\n\nconst itemsSuite: TestSuite<Role> = {\n  name: 'items',\n  tests: [\n    {\n      name: 'seed data exists',\n      run: async ctx => {\n        const items = await ctx.client.query(api.items.list);\n        assertDefined(items);\n        assertMinLength(items, 1);\n      },\n    },\n    {\n      name: 'can create as editor',\n      role: 'editor',\n      run: async ctx => {\n        const id = await ctx.client.mutation(api.items.create, {\n          title: 'Test',\n        });\n        assertDefined(id);\n        ctx.track('items', id);\n      },\n    },\n  ],\n};\n\nexport const suites: TestSuite<Role>[] = [itemsSuite];\n```\n\n### 4. Create a local backend wrapper\n\n```bash\n#!/usr/bin/env bash\n# run-e2e.sh\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"$0\")\" && pwd)\"\n\nexport E2E_PROJECT_ROOT=\"$SCRIPT_DIR\"\nexport E2E_TEST_COMMAND=\"npx tsx e2e.test.ts run --format=human\"\nexport E2E_KEYGEN_SCRIPT=\"node_modules/@amadeni/convex-e2e/scripts/generate-keys.ts\"\n\nexec bash node_modules/@amadeni/convex-e2e/scripts/local-backend.sh\n```\n\n### 5. Add scripts to package.json\n\n```json\n{\n  \"scripts\": {\n    \"test:e2e\": \"bash run-e2e.sh\",\n    \"test:e2e:dev\": \"npx tsx e2e.test.ts run --format=human\"\n  }\n}\n```\n\n### 6. Add to .gitignore\n\n```gitignore\n.local-backend/\nconvex_local_storage/\nconvex_local_backend.sqlite3\n.env.e2e\n.env.local.backup\ntest-private.pem\ntest-jwks.json\n```\n\n---\n\n## What You Need to Implement in Convex\n\nThe test runner calls 7 Convex functions by path. You implement them in your `convex/` directory.\n\n### Schema requirement\n\nYour schema must include an `appSettings` table for storing the seed ID map:\n\n```typescript\n// convex/schema.ts (add to your existing schema)\nappSettings: defineTable({\n  key: v.string(),\n  value: v.string(),\n}).index('by_key', ['key']),\n```\n\n### Function reference\n\n| Function            | Type               | Signature                              | Purpose                               |\n| ------------------- | ------------------ | -------------------------------------- | ------------------------------------- |\n| `createTestSession` | `internalAction`   | `(email: string) => { userId, token }` | Signs a JWT for a test user           |\n| `seedBase`          | `internalAction`   | `() => Record<string, string>`         | Seeds minimal data, returns ID map    |\n| `seedAll`           | `internalAction`   | `() => Record<string, string>`         | Seeds all fixture data                |\n| `clearAll`          | `internalAction`   | `() => void`                           | Deletes all test data                 |\n| `getSeedIdMap`      | `internalQuery`    | `() => Record<string, string>`         | Reads the stored ID map               |\n| `deleteTracked`     | `internalMutation` | `(ids: {table, id}[]) => void`         | Deletes tracked test entities         |\n| `listRecords`       | `internalQuery`    | `(table, limit?) => Document[]`        | Lists records (for `inspect` command) |\n\n### Auth setup (for local backend)\n\nThe local backend needs to validate JWTs. This requires:\n\n1. An HTTP route serving OIDC discovery + JWKS (so the backend can validate tokens)\n2. An `auth.config.ts` pointing to the site URL\n3. A `createTestSession` action that signs JWTs with the test private key\n\nSee the [fixtures/minimal/](fixtures/minimal/) directory for a complete working example.\n\n---\n\n## AI Setup Prompt\n\nIf you use an AI coding assistant, run this prompt in your project to have it scaffold the required Convex functions:\n\n```\nI am using the @amadeni/convex-e2e package for end-to-end testing.\n\nLook at my convex/schema.ts to understand my tables. Then look at the reference\nimplementation in node_modules/@amadeni/convex-e2e/fixtures/minimal/convex/ to\nunderstand the required function signatures and patterns.\n\nCreate the following files in my convex/ directory:\n\n1. **convex/testAuth.ts** (`\"use node\"`) - `createTestSession` internalAction\n   (signs an RS256 JWT using Node crypto and `JWT_PRIVATE_KEY` env var) and\n   `getJwks` internalAction (returns `process.env.JWKS`).\n\n2. **convex/testSupport.ts** - `seedBase`, `seedAll`, `clearAll` internalActions;\n   `getSeedIdMap`, `listRecords` internalQueries; `deleteTracked` internalMutation.\n   Seed data should cover my actual tables with realistic test records.\n   Use `_symId` fields (e.g. `\"@@user1\"`) for cross-table ID references.\n\n3. **convex/http.ts** - OIDC discovery routes (`/.well-known/openid-configuration`\n   and `/jwks.json`) so the local backend can validate JWTs.\n\n4. **convex/auth.config.ts** - Provider config pointing to `http://127.0.0.1:3211`.\n\n5. Add an `appSettings` table to my schema if not already present (needed for the\n   seed ID map).\n\nAlso create e2e.config.ts, e2e.test.ts, a test suite file, and run-e2e.sh at the\nproject root. Follow the Quick Start section of the package README for these.\n```\n\n---\n\n## Working Example\n\nThe [fixtures/minimal/](fixtures/minimal/) directory is a complete, runnable example project. It demonstrates:\n\n- Schema with 2 tables (`items` + `appSettings`)\n- All 7 required Convex functions\n- JWT auth with OIDC discovery\n- Config, test suite, and entry point\n- Local backend wrapper script\n\n---\n\n## Config Reference\n\n```typescript\ninterface ConvexE2EConfig<R extends string> {\n  projectName: string;\n  projectRoot?: string; // defaults to process.cwd()\n  roles: Record<R, string>; // role name -> test user email\n  defaultRole: R;\n  convexFunctions: {\n    createSession: string; // \"module:functionName\" format\n    seedBase: string;\n    seedAll: string;\n    clearAll: string;\n    getSeedIdMap: string;\n    deleteTracked: string;\n    listRecords: string;\n  };\n  loadSuites: () => TestSuite<R>[];\n  envFiles?: string[]; // defaults to ['.env.local', '.env', '.env.test']\n}\n```\n\n## Test Context\n\nEach test receives a `TestContext` with:\n\n```typescript\ninterface TestContext<R extends string> {\n  client: ConvexHttpClient; // authenticated for the test's role\n  auth: TestAuthManager<R>; // token management\n  userId: string; // current role's user ID\n  role: R; // current role\n  seedData: Record<string, SeedId>; // symbolic ID map (e.g. seedData['@@user1'])\n  track(table: string, id: string): void; // register for cleanup\n  asAdmin<T>(fn: (client) => Promise<T>): Promise<T>;\n  actAs<T>(role: R, fn: (client) => Promise<T>): Promise<T>;\n  log(msg: string): void; // verbose-only output\n}\n```\n\n## Assertions\n\n```typescript\nimport {\n  assert, // re-export of node:assert/strict\n  assertDefined, // value is not null/undefined\n  assertMinLength, // array has at least N items\n  assertThrows, // async function throws (optional pattern match)\n  assertForbidden, // function throws a permission error\n  assertNotPermissionFailure, // function succeeds or fails for non-permission reasons\n} from '@amadeni/convex-e2e';\n```\n\n## CLI Commands\n\n```\nnpx tsx e2e.test.ts <command> [options]\n\nCommands:\n  run [filter]       Run tests (filter by suite name prefix)\n  seed               Load all seed data\n  clear              Clear all test data\n  reset              Clear + seed\n  list               List suites and tests as JSON\n  inspect <table>    Show records in a table\n\nOptions:\n  --format=json|human  Output format (default: human)\n  --json               Shorthand for --format=json\n  --verbose            Show detailed output\n  --bail               Stop on first failure\n  --timeout=<ms>       Per-test timeout (default: 30000)\n  --deployment=<url>   Override Convex URL\n```\n\n## Local Backend Script\n\nThe included `scripts/local-backend.sh` orchestrates a fully isolated test environment:\n\n1. Downloads `convex-local-backend` (cached in `.local-backend/`)\n2. Starts the backend on a local port\n3. Generates a JWT key pair for test auth\n4. Deploys your Convex functions\n5. Sets `JWT_PRIVATE_KEY` and `JWKS` environment variables\n6. Runs your test command\n7. Cleans up on exit (backend process, temp files, restores `.env.local`)\n\nControlled via environment variables set in your wrapper script:\n\n| Variable            | Required | Description                                                                                        |\n| ------------------- | -------- | -------------------------------------------------------------------------------------------------- |\n| `E2E_PROJECT_ROOT`  | yes      | Absolute path to project root                                                                      |\n| `E2E_TEST_COMMAND`  | yes      | Command to run tests                                                                               |\n| `E2E_KEYGEN_SCRIPT` | yes      | Path to key generation script (relative to project root)                                           |\n| `E2E_JWT_ENV_VARS`  | no       | Space-separated `KEY=file` pairs (default: `JWT_PRIVATE_KEY=test-private.pem JWKS=test-jwks.json`) |\n| `CONVEX_LOCAL_PORT` | no       | Backend port (default: `3210`)                                                                     |\n\n## Convex Helpers\n\nThe `convex-helpers/` directory contains reference implementations for the Convex-side functions. Since Convex functions must live inside your project's `convex/` directory, these are meant to be copied and adapted.\n\n- `convex-helpers/seed.ts` -- `resolveRefs()` for symbolic ID resolution + templates for seed/clear functions\n- `convex-helpers/inspect.ts` -- templates for `getSeedIdMap`, `listRecords`, etc.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}