feather-testing-convex 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +353 -0
- package/dist/ConvexTestAuthProvider.d.ts +19 -0
- package/dist/ConvexTestAuthProvider.d.ts.map +1 -0
- package/dist/ConvexTestAuthProvider.js +31 -0
- package/dist/ConvexTestProvider.d.ts +23 -0
- package/dist/ConvexTestProvider.d.ts.map +1 -0
- package/dist/ConvexTestProvider.js +71 -0
- package/dist/helpers.d.ts +21 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +49 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/vitest-plugin.d.ts +32 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +47 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# feather-testing-convex
|
|
2
|
+
|
|
3
|
+
React provider that adapts [convex-test](https://www.npmjs.com/package/convex-test)'s one-shot query/mutation client for use with Convex's `ConvexProvider`, so `useQuery` and `useMutation` work in tests against an in-memory backend.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i convex convex-test react
|
|
9
|
+
npm i -D feather-testing-convex
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Three files to get from zero to a working test:
|
|
15
|
+
|
|
16
|
+
**1. `vitest.config.ts`**
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { defineConfig } from "vitest/config";
|
|
20
|
+
import react from "@vitejs/plugin-react";
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
plugins: [react()],
|
|
24
|
+
test: {
|
|
25
|
+
environment: "jsdom",
|
|
26
|
+
environmentMatchGlobs: [["convex/**", "edge-runtime"]],
|
|
27
|
+
server: { deps: { inline: ["convex-test"] } },
|
|
28
|
+
globals: true,
|
|
29
|
+
setupFiles: ["./src/test-setup.ts"],
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**2. `convex/test.setup.ts`**
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
/// <reference types="vite/client" />
|
|
38
|
+
import { createConvexTest, renderWithConvex } from "feather-testing-convex";
|
|
39
|
+
import schema from "./schema";
|
|
40
|
+
|
|
41
|
+
export const modules = import.meta.glob("./**/!(*.*.*)*.*s");
|
|
42
|
+
export const test = createConvexTest(schema, modules);
|
|
43
|
+
export { renderWithConvex };
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**3. `src/test-setup.ts`**
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import "@testing-library/jest-dom/vitest";
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**First test:**
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { describe, expect } from "vitest";
|
|
56
|
+
import { screen } from "@testing-library/react";
|
|
57
|
+
import { test, renderWithConvex } from "../../convex/test.setup";
|
|
58
|
+
import { TodoList } from "./TodoList";
|
|
59
|
+
|
|
60
|
+
describe("TodoList", () => {
|
|
61
|
+
test("shows seeded data", async ({ client, seed }) => {
|
|
62
|
+
await seed("todos", { text: "Buy milk", completed: false });
|
|
63
|
+
renderWithConvex(<TodoList />, client);
|
|
64
|
+
expect(await screen.findByText("Buy milk")).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For auth testing, see [Auth Testing](#auth-testing) below.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
> For the recommended setup, see [Quick Start](#quick-start) above. This section shows the low-level API.
|
|
74
|
+
|
|
75
|
+
1. Create a convex-test client: `convexTest(schema, modules)`.
|
|
76
|
+
2. Wrap your component with `ConvexTestProvider` and pass the client:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { convexTest } from "convex-test";
|
|
80
|
+
import { ConvexTestProvider } from "feather-testing-convex";
|
|
81
|
+
import schema from "./convex/schema";
|
|
82
|
+
import { modules } from "./convex/test.setup";
|
|
83
|
+
|
|
84
|
+
const testClient = convexTest(schema, modules);
|
|
85
|
+
|
|
86
|
+
render(
|
|
87
|
+
<ConvexTestProvider client={testClient}>
|
|
88
|
+
<YourComponent />
|
|
89
|
+
</ConvexTestProvider>
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Query reactivity
|
|
94
|
+
|
|
95
|
+
This adapter runs each query **once** (when the component mounts). The UI does not re-render after a mutation in the same test. Assert backend state via `client.query(api.your.list, {})`, or re-mount to run the query again.
|
|
96
|
+
|
|
97
|
+
## Helper Functions
|
|
98
|
+
|
|
99
|
+
Reduce test boilerplate from ~15 lines to ~2 lines with `createConvexTest`.
|
|
100
|
+
|
|
101
|
+
### Setup
|
|
102
|
+
|
|
103
|
+
Create a test setup file in your Convex directory:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// convex/test.setup.ts
|
|
107
|
+
import { createConvexTest, renderWithConvex } from "feather-testing-convex";
|
|
108
|
+
import schema from "./schema";
|
|
109
|
+
|
|
110
|
+
export const modules = import.meta.glob("./**/!(*.*.*)*.*s");
|
|
111
|
+
export const test = createConvexTest(schema, modules);
|
|
112
|
+
export { renderWithConvex };
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Usage
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// src/components/TodoList.test.tsx
|
|
119
|
+
import { test, renderWithConvex } from "../../convex/test.setup";
|
|
120
|
+
import { expect } from "vitest";
|
|
121
|
+
import { api } from "../../convex/_generated/api";
|
|
122
|
+
|
|
123
|
+
test("creates a todo", async ({ client, seed }) => {
|
|
124
|
+
await seed("todos", { text: "Buy milk", completed: false });
|
|
125
|
+
|
|
126
|
+
const todos = await client.query(api.todos.list, {});
|
|
127
|
+
expect(todos).toHaveLength(1);
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Fixtures
|
|
132
|
+
|
|
133
|
+
| Fixture | Description |
|
|
134
|
+
|---------|-------------|
|
|
135
|
+
| `testClient` | Raw convex-test client (unauthenticated) |
|
|
136
|
+
| `userId` | ID of an auto-created user (string) |
|
|
137
|
+
| `client` | Authenticated client for the auto-created user |
|
|
138
|
+
| `seed(table, data)` | Insert a document. Auto-fills `userId` unless `data` includes an explicit `userId` (explicit wins). Returns the document ID. |
|
|
139
|
+
| `createUser()` | Create another user, return authenticated client with `.userId` property. |
|
|
140
|
+
|
|
141
|
+
### Multi-user Test Example
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
test("users only see their own todos", async ({ client, seed, createUser }) => {
|
|
145
|
+
// Alice creates a todo
|
|
146
|
+
await client.mutation(api.todos.create, { text: "Alice's todo" });
|
|
147
|
+
|
|
148
|
+
// Bob creates a todo (seed with explicit userId)
|
|
149
|
+
const bob = await createUser();
|
|
150
|
+
await seed("todos", { text: "Bob's todo", completed: false, userId: bob.userId });
|
|
151
|
+
|
|
152
|
+
// Each user only sees their own
|
|
153
|
+
const aliceTodos = await client.query(api.todos.list, {});
|
|
154
|
+
expect(aliceTodos).toHaveLength(1);
|
|
155
|
+
|
|
156
|
+
const bobTodos = await bob.query(api.todos.list, {});
|
|
157
|
+
expect(bobTodos).toHaveLength(1);
|
|
158
|
+
expect(bobTodos[0].text).toBe("Bob's todo");
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Configuration
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Custom users table name
|
|
166
|
+
export const test = createConvexTest(schema, modules, {
|
|
167
|
+
usersTable: "profiles",
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Additional Helpers
|
|
172
|
+
|
|
173
|
+
- `wrapWithConvex(children, client)` — JSX wrapper for custom rendering
|
|
174
|
+
- `renderWithConvex(ui, client)` — Testing Library render with Convex provider
|
|
175
|
+
|
|
176
|
+
## Auth Testing
|
|
177
|
+
|
|
178
|
+
Test components that use `<Authenticated>`, `<Unauthenticated>`, `useConvexAuth()`, and `useAuthActions()` — without mocking.
|
|
179
|
+
|
|
180
|
+
### Prerequisites
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm i -D @convex-dev/auth
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Add the vitest plugin to resolve an internal `@convex-dev/auth` import ([upstream fix requested](https://github.com/get-convex/convex-auth/issues/281)):
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// vitest.config.ts
|
|
190
|
+
import { convexTestProviderPlugin } from "feather-testing-convex/vitest-plugin";
|
|
191
|
+
|
|
192
|
+
export default defineConfig({
|
|
193
|
+
plugins: [
|
|
194
|
+
react(),
|
|
195
|
+
convexTestProviderPlugin(), // resolves @convex-dev/auth internal import
|
|
196
|
+
],
|
|
197
|
+
// ... rest of config unchanged
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Usage
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { renderWithConvexAuth } from "feather-testing-convex";
|
|
205
|
+
|
|
206
|
+
// Authenticated (default) — <Authenticated> children render
|
|
207
|
+
renderWithConvexAuth(<App />, client);
|
|
208
|
+
|
|
209
|
+
// Unauthenticated — <Unauthenticated> children render
|
|
210
|
+
renderWithConvexAuth(<App />, client, { authenticated: false });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`renderWithConvexAuth` wraps your component with both auth state (so `<Authenticated>`, `<Unauthenticated>`, and `useConvexAuth()` work) and auth actions context (so `useAuthActions()` works). Calling `signIn()` sets auth to true; calling `signOut()` sets auth to false — the view re-renders accordingly.
|
|
214
|
+
|
|
215
|
+
### Complete auth test example
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { test, renderWithConvexAuth } from "../../convex/test.setup";
|
|
219
|
+
import { screen } from "@testing-library/react";
|
|
220
|
+
import userEvent from "@testing-library/user-event";
|
|
221
|
+
import { expect } from "vitest";
|
|
222
|
+
|
|
223
|
+
test("sign out toggles the view", async ({ client }) => {
|
|
224
|
+
const user = userEvent.setup();
|
|
225
|
+
renderWithConvexAuth(<App />, client);
|
|
226
|
+
|
|
227
|
+
await user.click(screen.getByRole("button", { name: /sign out/i }));
|
|
228
|
+
expect(await screen.findByText("Please sign in")).toBeInTheDocument();
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Sign-in error simulation
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
renderWithConvexAuth(<App />, client, {
|
|
236
|
+
authenticated: false,
|
|
237
|
+
signInError: new Error("Invalid credentials"),
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Direct `ConvexTestAuthProvider` (custom wrapping)
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { ConvexTestAuthProvider } from "feather-testing-convex";
|
|
245
|
+
|
|
246
|
+
<ConvexTestAuthProvider client={client} authenticated={true}>
|
|
247
|
+
<YourComponent />
|
|
248
|
+
</ConvexTestAuthProvider>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Vitest Configuration Reference
|
|
252
|
+
|
|
253
|
+
### Minimal config (no auth)
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { defineConfig } from "vitest/config";
|
|
257
|
+
import react from "@vitejs/plugin-react";
|
|
258
|
+
|
|
259
|
+
export default defineConfig({
|
|
260
|
+
plugins: [react()],
|
|
261
|
+
test: {
|
|
262
|
+
environment: "jsdom",
|
|
263
|
+
environmentMatchGlobs: [["convex/**", "edge-runtime"]],
|
|
264
|
+
server: { deps: { inline: ["convex-test"] } },
|
|
265
|
+
globals: true,
|
|
266
|
+
setupFiles: ["./src/test-setup.ts"],
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### With auth testing
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { defineConfig } from "vitest/config";
|
|
275
|
+
import react from "@vitejs/plugin-react";
|
|
276
|
+
import { convexTestProviderPlugin } from "feather-testing-convex/vitest-plugin";
|
|
277
|
+
|
|
278
|
+
export default defineConfig({
|
|
279
|
+
plugins: [react(), convexTestProviderPlugin()],
|
|
280
|
+
test: {
|
|
281
|
+
environment: "jsdom",
|
|
282
|
+
environmentMatchGlobs: [["convex/**", "edge-runtime"]],
|
|
283
|
+
server: { deps: { inline: ["convex-test"] } },
|
|
284
|
+
globals: true,
|
|
285
|
+
setupFiles: ["./src/test-setup.ts"],
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Config options explained
|
|
291
|
+
|
|
292
|
+
| Option | Why |
|
|
293
|
+
|--------|-----|
|
|
294
|
+
| `react()` | JSX transform for test files |
|
|
295
|
+
| `environment: "jsdom"` | DOM APIs for React component tests |
|
|
296
|
+
| `environmentMatchGlobs` | Convex functions run in edge runtime, not jsdom |
|
|
297
|
+
| `server.deps.inline: ["convex-test"]` | convex-test must be inlined for Vitest to resolve it |
|
|
298
|
+
| `setupFiles` | Load jest-dom matchers (`toBeInTheDocument()`, etc.) |
|
|
299
|
+
| `convexTestProviderPlugin()` | Resolves `@convex-dev/auth` internal import (auth testing only) |
|
|
300
|
+
|
|
301
|
+
## Agent Skills
|
|
302
|
+
|
|
303
|
+
Install skills for AI coding agents via [skills.sh](https://skills.sh):
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx skills add siraj-samsudeen/feather-testing-convex
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
This installs three skills: `setup-convex-testing`, `add-convex-auth-testing`, and `convex-test-patterns`.
|
|
310
|
+
|
|
311
|
+
## Limitations
|
|
312
|
+
|
|
313
|
+
### One-shot query execution (non-reactive)
|
|
314
|
+
|
|
315
|
+
Queries resolve **once** at component mount. After a mutation, the UI does not automatically re-render with updated data — this adapter does not simulate Convex's reactive subscription model.
|
|
316
|
+
|
|
317
|
+
To verify backend state after a mutation, query directly:
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
await user.click(screen.getByRole("button", { name: "Add" }));
|
|
321
|
+
const items = await client.query(api.items.list, {});
|
|
322
|
+
expect(items).toHaveLength(1);
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
To see updated data in the UI, unmount and remount the component (or call `rerender`).
|
|
326
|
+
|
|
327
|
+
### Nested `runQuery`/`runMutation` lose auth context
|
|
328
|
+
|
|
329
|
+
When a Convex function calls `ctx.runQuery()` or `ctx.runMutation()`, the nested call does not inherit the caller's auth identity. This is an [upstream limitation in convex-test](https://github.com/get-convex/convex-test) ([issue #50](https://github.com/get-convex/convex-test/issues/50)), not in this package.
|
|
330
|
+
|
|
331
|
+
**Root cause:** In `convex-test`, the `queryFromPath` and `mutationFromPath` handlers spread `{ ...ctx, auth }` but do not override `ctx.runQuery`/`ctx.runMutation` with auth-aware versions. Actions (`actionFromPath`) already do this correctly.
|
|
332
|
+
|
|
333
|
+
**Workarounds:**
|
|
334
|
+
|
|
335
|
+
1. **Pass userId as an explicit argument** (recommended) — create `internalQuery`/`internalMutation` variants that accept `userId` instead of reading `ctx.auth.getUserIdentity()` inside nested calls.
|
|
336
|
+
2. **Use `patch-package`** — apply a 2-line fix to `node_modules/convex-test/dist/index.js`:
|
|
337
|
+
- In `queryFromPath`: change `{ ...ctx, auth }` to `{ ...ctx, auth, runQuery: byType.query }`
|
|
338
|
+
- In `runTransaction`: change `{ ...ctx, auth, ...extraCtx }` to `{ ...ctx, auth, runQuery: byType.query, runMutation: byType.mutation, ...extraCtx }`
|
|
339
|
+
3. **Use actions for orchestration** — actions already propagate auth correctly to nested calls (note: different transactional semantics).
|
|
340
|
+
|
|
341
|
+
## Types
|
|
342
|
+
|
|
343
|
+
The `client` prop accepts any object with `query(ref, args)` and `mutation(ref, args)` returning promises. The result of `convexTest(schema, modules)` (and `.withIdentity(...)`) satisfies this.
|
|
344
|
+
|
|
345
|
+
## Contributing
|
|
346
|
+
|
|
347
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow.
|
|
348
|
+
|
|
349
|
+
AI agents: See [CLAUDE.md](CLAUDE.md) for quick reference.
|
|
350
|
+
|
|
351
|
+
## Versioning
|
|
352
|
+
|
|
353
|
+
Releases follow [semantic versioning](https://semver.org/). See [CHANGELOG.md](./CHANGELOG.md) for release history.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type ConvexTestClient } from "./ConvexTestProvider.js";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps children with both auth state (ConvexProviderWithAuth) and auth actions
|
|
5
|
+
* (ConvexAuthActionsContext), so components using <Authenticated>, <Unauthenticated>,
|
|
6
|
+
* useConvexAuth(), and useAuthActions() all work in tests.
|
|
7
|
+
*
|
|
8
|
+
* signIn/signOut are pure React state toggles — no backend calls.
|
|
9
|
+
* For auth-dependent queries, use `client.withIdentity()` to inject identity
|
|
10
|
+
* so `getAuthUserId(ctx)` returns a valid user ID.
|
|
11
|
+
*/
|
|
12
|
+
export declare function ConvexTestAuthProvider({ client, children, authenticated, signInError, }: {
|
|
13
|
+
client: ConvexTestClient;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
authenticated?: boolean;
|
|
16
|
+
/** When set, signIn() rejects with this error instead of toggling state. */
|
|
17
|
+
signInError?: Error;
|
|
18
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=ConvexTestAuthProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConvexTestAuthProvider.d.ts","sourceRoot":"","sources":["../src/ConvexTestAuthProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKjD,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEpF;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,aAAoB,EACpB,WAAW,GACZ,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,2CAqBA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// TODO: Replace with public export when @convex-dev/auth provides one.
|
|
3
|
+
// Track: https://github.com/get-convex/convex-auth — request exported TestAuthProvider or context.
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
// @ts-expect-error — internal path not in package exports; works at runtime via bundler
|
|
6
|
+
import { ConvexAuthActionsContext } from "@convex-dev/auth/dist/react/client.js";
|
|
7
|
+
import { ConvexTestProvider } from "./ConvexTestProvider.js";
|
|
8
|
+
/**
|
|
9
|
+
* Wraps children with both auth state (ConvexProviderWithAuth) and auth actions
|
|
10
|
+
* (ConvexAuthActionsContext), so components using <Authenticated>, <Unauthenticated>,
|
|
11
|
+
* useConvexAuth(), and useAuthActions() all work in tests.
|
|
12
|
+
*
|
|
13
|
+
* signIn/signOut are pure React state toggles — no backend calls.
|
|
14
|
+
* For auth-dependent queries, use `client.withIdentity()` to inject identity
|
|
15
|
+
* so `getAuthUserId(ctx)` returns a valid user ID.
|
|
16
|
+
*/
|
|
17
|
+
export function ConvexTestAuthProvider({ client, children, authenticated = true, signInError, }) {
|
|
18
|
+
const [isAuth, setIsAuth] = useState(authenticated);
|
|
19
|
+
const actions = {
|
|
20
|
+
signIn: async () => {
|
|
21
|
+
if (signInError)
|
|
22
|
+
throw signInError;
|
|
23
|
+
setIsAuth(true);
|
|
24
|
+
return { signingIn: false };
|
|
25
|
+
},
|
|
26
|
+
signOut: async () => {
|
|
27
|
+
setIsAuth(false);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
return (_jsx(ConvexTestProvider, { client: client, authenticated: isAuth, children: _jsx(ConvexAuthActionsContext.Provider, { value: actions, children: children }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** Minimal client shape: one-shot query, mutation, action, plus identity injection. Matches convex-test client. */
|
|
3
|
+
export interface ConvexTestClient {
|
|
4
|
+
query: (query: unknown, args: unknown) => Promise<unknown>;
|
|
5
|
+
mutation: (mutation: unknown, args: unknown) => Promise<unknown>;
|
|
6
|
+
action: (action: unknown, args: unknown) => Promise<unknown>;
|
|
7
|
+
run: (fn: (ctx: any) => Promise<any>) => Promise<any>;
|
|
8
|
+
withIdentity: (identity: Record<string, unknown>) => ConvexTestClient;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Wraps children in ConvexProvider with a fake client that adapts convex-test's
|
|
12
|
+
* one-shot query/mutation API to the reactive watchQuery API the real ConvexProvider expects.
|
|
13
|
+
* Lets useQuery/useMutation run in tests against an in-memory backend.
|
|
14
|
+
*
|
|
15
|
+
* When `authenticated` is provided, uses ConvexProviderWithAuth instead of ConvexProvider,
|
|
16
|
+
* enabling <Authenticated>, <Unauthenticated>, and useConvexAuth() in tested components.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ConvexTestProvider({ client, children, authenticated, }: {
|
|
19
|
+
client: ConvexTestClient;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
authenticated?: boolean;
|
|
22
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=ConvexTestProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConvexTestProvider.d.ts","sourceRoot":"","sources":["../src/ConvexTestProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,mHAAmH;AACnH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjE,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,gBAAgB,CAAC;CACvE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,EACR,aAAa,GACd,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,2CA2EA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
3
|
+
import { ConvexProvider, ConvexProviderWithAuth } from "convex/react";
|
|
4
|
+
/**
|
|
5
|
+
* Wraps children in ConvexProvider with a fake client that adapts convex-test's
|
|
6
|
+
* one-shot query/mutation API to the reactive watchQuery API the real ConvexProvider expects.
|
|
7
|
+
* Lets useQuery/useMutation run in tests against an in-memory backend.
|
|
8
|
+
*
|
|
9
|
+
* When `authenticated` is provided, uses ConvexProviderWithAuth instead of ConvexProvider,
|
|
10
|
+
* enabling <Authenticated>, <Unauthenticated>, and useConvexAuth() in tested components.
|
|
11
|
+
*/
|
|
12
|
+
export function ConvexTestProvider({ client, children, authenticated, }) {
|
|
13
|
+
// Two-level cache: query reference identity → serialized args → result.
|
|
14
|
+
// Avoids collisions between different query functions whose proxies all
|
|
15
|
+
// stringify to "{}" (see issue #2). Approach from PR #3.
|
|
16
|
+
// useRef so cache survives re-renders (e.g. auth state toggle).
|
|
17
|
+
const cache = useRef(new Map());
|
|
18
|
+
// Ref for authenticated so fakeClient.setAuth reads the latest value
|
|
19
|
+
// without needing authenticated in the useMemo dependency array.
|
|
20
|
+
const authenticatedRef = useRef(authenticated);
|
|
21
|
+
authenticatedRef.current = authenticated;
|
|
22
|
+
// Stable reference — only changes when the client prop itself changes.
|
|
23
|
+
// ConvexProviderWithAuth puts client in useEffect deps; an unstable
|
|
24
|
+
// reference would trigger setAuth/clearAuth cycles every render.
|
|
25
|
+
const fakeClient = useMemo(() => ({
|
|
26
|
+
watchQuery: (query, args) => {
|
|
27
|
+
let queryCache = cache.current.get(query);
|
|
28
|
+
if (!queryCache) {
|
|
29
|
+
queryCache = new Map();
|
|
30
|
+
cache.current.set(query, queryCache);
|
|
31
|
+
}
|
|
32
|
+
const argsKey = JSON.stringify(args ?? {});
|
|
33
|
+
let subscriber = null;
|
|
34
|
+
client.query(query, args ?? {}).then((result) => {
|
|
35
|
+
queryCache.set(argsKey, result);
|
|
36
|
+
subscriber?.();
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
localQueryResult: () => queryCache.get(argsKey),
|
|
40
|
+
onUpdate: (cb) => {
|
|
41
|
+
subscriber = cb;
|
|
42
|
+
return () => { subscriber = null; };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
mutation: (mutation, args) => {
|
|
47
|
+
return client.mutation(mutation, args ?? {});
|
|
48
|
+
},
|
|
49
|
+
// Synchronous — real client calls onChange async, but ConvexProviderWithAuth
|
|
50
|
+
// calls setAuth inside useEffect (committed state), so sync is safe here.
|
|
51
|
+
setAuth: (_fetchToken, onChange) => {
|
|
52
|
+
onChange(authenticatedRef.current ?? false);
|
|
53
|
+
},
|
|
54
|
+
clearAuth: () => { },
|
|
55
|
+
}), [client]);
|
|
56
|
+
// Stable fetchAccessToken — ConvexProviderWithAuth puts this in useEffect
|
|
57
|
+
// deps. Unstable reference would trigger setAuth/clearAuth every render,
|
|
58
|
+
// pausing/resuming websockets. Matches Convex's own Auth0 provider pattern.
|
|
59
|
+
const fetchAccessToken = useCallback(async () => null, []);
|
|
60
|
+
// Only changes when authenticated changes — which is when we actually
|
|
61
|
+
// want ConvexProviderWithAuth to re-run its auth effects.
|
|
62
|
+
const useAuth = useCallback(() => ({
|
|
63
|
+
isLoading: false,
|
|
64
|
+
isAuthenticated: authenticated ?? false,
|
|
65
|
+
fetchAccessToken,
|
|
66
|
+
}), [authenticated, fetchAccessToken]);
|
|
67
|
+
if (authenticated === undefined) {
|
|
68
|
+
return (_jsx(ConvexProvider, { client: fakeClient, children: children }));
|
|
69
|
+
}
|
|
70
|
+
return (_jsx(ConvexProviderWithAuth, { client: fakeClient, useAuth: useAuth, children: children }));
|
|
71
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode, ReactElement } from "react";
|
|
2
|
+
interface CreateConvexTestOptions {
|
|
3
|
+
usersTable?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function createConvexTest(schema: any, modules: any, options?: CreateConvexTestOptions): import("vitest").TestAPI<{
|
|
6
|
+
client: any;
|
|
7
|
+
testClient: any;
|
|
8
|
+
userId: string;
|
|
9
|
+
seed: (table: string, data: Record<string, unknown>) => Promise<string>;
|
|
10
|
+
createUser: () => Promise<any & {
|
|
11
|
+
userId: string;
|
|
12
|
+
}>;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function wrapWithConvex(children: ReactNode, client: unknown): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function renderWithConvex(ui: ReactElement, client: unknown): import("@testing-library/react").RenderResult<typeof import("@testing-library/dom/types/queries.js"), HTMLElement, HTMLElement>;
|
|
16
|
+
export declare function renderWithConvexAuth(ui: ReactElement, client: unknown, options?: {
|
|
17
|
+
authenticated?: boolean;
|
|
18
|
+
signInError?: Error;
|
|
19
|
+
}): import("@testing-library/react").RenderResult<typeof import("@testing-library/dom/types/queries.js"), HTMLElement, HTMLElement>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAErD,UAAU,uBAAuB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,GAAG,EACZ,OAAO,GAAE,uBAA4B;;;;kBAVvB,MAAM,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC;sBACrD,OAAO,CAAC,GAAG,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;GA4CpD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,2CAElE;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,mIAIjE;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,OAAO,EACf,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,mIAa3D"}
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { test as baseTest } from "vitest";
|
|
3
|
+
import { convexTest } from "convex-test";
|
|
4
|
+
import { ConvexTestProvider } from "./ConvexTestProvider.js";
|
|
5
|
+
import { ConvexTestAuthProvider } from "./ConvexTestAuthProvider.js";
|
|
6
|
+
import { render } from "@testing-library/react";
|
|
7
|
+
const dbInsert = (client, table, data) => client.run((ctx) => ctx.db.insert(table, data));
|
|
8
|
+
export function createConvexTest(schema, modules, options = {}) {
|
|
9
|
+
const usersTable = options.usersTable ?? "users";
|
|
10
|
+
return baseTest.extend({
|
|
11
|
+
testClient: async ({}, use) => {
|
|
12
|
+
const client = convexTest(schema, modules);
|
|
13
|
+
await use(client);
|
|
14
|
+
},
|
|
15
|
+
userId: async ({ testClient }, use) => {
|
|
16
|
+
const id = await dbInsert(testClient, usersTable, {});
|
|
17
|
+
await use(id);
|
|
18
|
+
},
|
|
19
|
+
client: async ({ testClient, userId }, use) => {
|
|
20
|
+
const authenticated = testClient.withIdentity({ subject: userId });
|
|
21
|
+
await use(authenticated);
|
|
22
|
+
},
|
|
23
|
+
seed: async ({ testClient, userId }, use) => {
|
|
24
|
+
const seedFn = (table, data) => dbInsert(testClient, table, { userId, ...data });
|
|
25
|
+
await use(seedFn);
|
|
26
|
+
},
|
|
27
|
+
createUser: async ({ testClient }, use) => {
|
|
28
|
+
const createUserFn = async () => {
|
|
29
|
+
const newUserId = await dbInsert(testClient, usersTable, {});
|
|
30
|
+
const userClient = testClient.withIdentity({ subject: newUserId });
|
|
31
|
+
return Object.assign(userClient, { userId: newUserId });
|
|
32
|
+
};
|
|
33
|
+
await use(createUserFn);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function wrapWithConvex(children, client) {
|
|
38
|
+
return _jsx(ConvexTestProvider, { client: client, children: children });
|
|
39
|
+
}
|
|
40
|
+
export function renderWithConvex(ui, client) {
|
|
41
|
+
return render(ui, {
|
|
42
|
+
wrapper: ({ children }) => wrapWithConvex(children, client),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function renderWithConvexAuth(ui, client, options) {
|
|
46
|
+
return render(ui, {
|
|
47
|
+
wrapper: ({ children }) => (_jsx(ConvexTestAuthProvider, { client: client, authenticated: options?.authenticated ?? true, signInError: options?.signInError, children: children })),
|
|
48
|
+
});
|
|
49
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ConvexTestProvider, type ConvexTestClient } from "./ConvexTestProvider.js";
|
|
2
|
+
export { ConvexTestAuthProvider } from "./ConvexTestAuthProvider.js";
|
|
3
|
+
export { createConvexTest, wrapWithConvex, renderWithConvex, renderWithConvexAuth, } from "./helpers.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin that resolves the internal @convex-dev/auth import used by
|
|
3
|
+
* ConvexTestAuthProvider. Add this to your vitest.config.ts plugins array
|
|
4
|
+
* so you don't need a manual resolve.alias entry.
|
|
5
|
+
*
|
|
6
|
+
* Background: ConvexTestAuthProvider imports ConvexAuthActionsContext from
|
|
7
|
+
* @convex-dev/auth/dist/react/client.js — an internal path not in the
|
|
8
|
+
* package's exports field. Vite enforces exports strictly and blocks it.
|
|
9
|
+
* This plugin adds a resolve.alias so Vite can find the file.
|
|
10
|
+
*
|
|
11
|
+
* Upstream fix requested: https://github.com/get-convex/convex-auth/issues/281
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { convexTestProviderPlugin } from "feather-testing-convex/vitest-plugin";
|
|
16
|
+
*
|
|
17
|
+
* export default defineConfig({
|
|
18
|
+
* plugins: [convexTestProviderPlugin()],
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function convexTestProviderPlugin(): {
|
|
23
|
+
name: string;
|
|
24
|
+
config(): {
|
|
25
|
+
resolve: {
|
|
26
|
+
alias: {
|
|
27
|
+
"@convex-dev/auth/dist/react/client.js": string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=vitest-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest-plugin.d.ts","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,wBAAwB;;;;;;;;;EA2BvC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Vite plugin that resolves the internal @convex-dev/auth import used by
|
|
5
|
+
* ConvexTestAuthProvider. Add this to your vitest.config.ts plugins array
|
|
6
|
+
* so you don't need a manual resolve.alias entry.
|
|
7
|
+
*
|
|
8
|
+
* Background: ConvexTestAuthProvider imports ConvexAuthActionsContext from
|
|
9
|
+
* @convex-dev/auth/dist/react/client.js — an internal path not in the
|
|
10
|
+
* package's exports field. Vite enforces exports strictly and blocks it.
|
|
11
|
+
* This plugin adds a resolve.alias so Vite can find the file.
|
|
12
|
+
*
|
|
13
|
+
* Upstream fix requested: https://github.com/get-convex/convex-auth/issues/281
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { convexTestProviderPlugin } from "feather-testing-convex/vitest-plugin";
|
|
18
|
+
*
|
|
19
|
+
* export default defineConfig({
|
|
20
|
+
* plugins: [convexTestProviderPlugin()],
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function convexTestProviderPlugin() {
|
|
25
|
+
// require.resolve("@convex-dev/auth/dist/react/client.js") throws
|
|
26
|
+
// ERR_PACKAGE_PATH_NOT_EXPORTED because the path isn't in exports.
|
|
27
|
+
// Resolve from the consumer's project root (not from this plugin's
|
|
28
|
+
// location) so the alias points to the same copy that useAuthActions()
|
|
29
|
+
// uses — avoiding React Context duplication.
|
|
30
|
+
const require = createRequire(resolve(process.cwd(), "package.json"));
|
|
31
|
+
const mainEntry = require.resolve("@convex-dev/auth/react");
|
|
32
|
+
const pkgName = "@convex-dev/auth";
|
|
33
|
+
const authRoot = mainEntry.slice(0, mainEntry.indexOf(pkgName) + pkgName.length);
|
|
34
|
+
const resolved = resolve(authRoot, "dist/react/client.js");
|
|
35
|
+
return {
|
|
36
|
+
name: "feather-testing-convex",
|
|
37
|
+
config() {
|
|
38
|
+
return {
|
|
39
|
+
resolve: {
|
|
40
|
+
alias: {
|
|
41
|
+
"@convex-dev/auth/dist/react/client.js": resolved,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "feather-testing-convex",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "React provider that adapts convex-test's one-shot client for use with ConvexProvider, so useQuery/useMutation work in tests.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"convex",
|
|
8
|
+
"testing",
|
|
9
|
+
"react",
|
|
10
|
+
"provider",
|
|
11
|
+
"convex-test"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/siraj-samsudeen/feather-testing-convex.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/siraj-samsudeen/feather-testing-convex/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/siraj-samsudeen/feather-testing-convex#readme",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"module": "dist/index.js",
|
|
24
|
+
"types": "dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./vitest-plugin": {
|
|
32
|
+
"types": "./dist/vitest-plugin.d.ts",
|
|
33
|
+
"import": "./dist/vitest-plugin.js",
|
|
34
|
+
"default": "./dist/vitest-plugin.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@convex-dev/auth": ">=0.0.80",
|
|
42
|
+
"@testing-library/react": ">=14.0.0",
|
|
43
|
+
"convex": ">=1.0.0",
|
|
44
|
+
"convex-test": ">=0.0.1",
|
|
45
|
+
"react": ">=18.0.0",
|
|
46
|
+
"vitest": ">=1.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"@testing-library/react": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"@convex-dev/auth": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@convex-dev/auth": "^0.0.90",
|
|
63
|
+
"@testing-library/dom": "^10.4.1",
|
|
64
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
65
|
+
"@testing-library/react": "^16.3.2",
|
|
66
|
+
"@testing-library/user-event": "^14.6.1",
|
|
67
|
+
"@types/node": "^25.2.3",
|
|
68
|
+
"@types/react": "^19.2.10",
|
|
69
|
+
"@vitejs/plugin-react": "^5.1.3",
|
|
70
|
+
"convex": "^1.31.7",
|
|
71
|
+
"convex-test": "^0.0.41",
|
|
72
|
+
"jsdom": "^28.0.0",
|
|
73
|
+
"react": "^19.2.4",
|
|
74
|
+
"typescript": "~5.9.3",
|
|
75
|
+
"vitest": "^4.0.18"
|
|
76
|
+
}
|
|
77
|
+
}
|