@carlonicora/nextjs-jsonapi 1.131.1 → 1.131.2
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/dist/{BlockNoteEditor-BR3IRGJW.js → BlockNoteEditor-5RUXGWLR.js} +9 -9
- package/dist/{BlockNoteEditor-BR3IRGJW.js.map → BlockNoteEditor-5RUXGWLR.js.map} +1 -1
- package/dist/{BlockNoteEditor-PVBZYPMF.mjs → BlockNoteEditor-F4AKH532.mjs} +2 -2
- package/dist/billing/index.js +310 -310
- package/dist/billing/index.mjs +1 -1
- package/dist/{chunk-MDXT47V4.mjs → chunk-BBBQGJEG.mjs} +121 -101
- package/dist/chunk-BBBQGJEG.mjs.map +1 -0
- package/dist/{chunk-5SQFK35K.js → chunk-T7F2EVPR.js} +253 -233
- package/dist/chunk-T7F2EVPR.js.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/features/help/index.js +31 -31
- package/dist/features/help/index.mjs +1 -1
- package/package.json +1 -1
- package/src/features/notification/contexts/__tests__/NotificationContext.spec.tsx +34 -8
- package/src/features/user/contexts/CurrentUserContext.tsx +43 -9
- package/src/features/user/contexts/__tests__/CurrentUserContext.spec.tsx +149 -6
- package/dist/chunk-5SQFK35K.js.map +0 -1
- package/dist/chunk-MDXT47V4.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-PVBZYPMF.mjs.map → BlockNoteEditor-F4AKH532.mjs.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { render } from "@testing-library/react";
|
|
2
|
+
import { render, act } from "@testing-library/react";
|
|
3
3
|
|
|
4
4
|
vi.mock("cookies-next", () => ({
|
|
5
5
|
getCookie: vi.fn(() => undefined),
|
|
@@ -9,14 +9,54 @@ vi.mock("next/navigation", () => ({
|
|
|
9
9
|
usePathname: () => "/",
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
+
const socketMock = vi.hoisted(() => {
|
|
13
|
+
const handlers: Record<string, ((data: any) => void)[]> = {};
|
|
14
|
+
return {
|
|
15
|
+
socket: {
|
|
16
|
+
on: (event: string, handler: (data: any) => void) => {
|
|
17
|
+
(handlers[event] ??= []).push(handler);
|
|
18
|
+
},
|
|
19
|
+
off: (event: string, handler: (data: any) => void) => {
|
|
20
|
+
handlers[event] = (handlers[event] ?? []).filter((h) => h !== handler);
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
emit: (event: string, data: any) => {
|
|
24
|
+
(handlers[event] ?? []).forEach((h) => h(data));
|
|
25
|
+
},
|
|
26
|
+
reset: () => {
|
|
27
|
+
for (const key of Object.keys(handlers)) delete handlers[key];
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
12
32
|
vi.mock("../../../../contexts/SocketContext", () => ({
|
|
13
|
-
useSocketContext: () => ({ socket:
|
|
33
|
+
useSocketContext: () => ({ socket: socketMock.socket, isConnected: true }),
|
|
14
34
|
}));
|
|
15
35
|
|
|
16
|
-
vi.mock("../../../../core", () =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
vi.mock("../../../../core", () => {
|
|
37
|
+
// Mirrors the real Company model: public getters over private backing fields.
|
|
38
|
+
// The token overlay writes the backing fields, so a plain object would not
|
|
39
|
+
// exercise it.
|
|
40
|
+
class FakeCompany {
|
|
41
|
+
id = "";
|
|
42
|
+
monthlyTokens = 0;
|
|
43
|
+
_availableMonthlyTokens = 0;
|
|
44
|
+
_availableExtraTokens = 0;
|
|
45
|
+
get availableMonthlyTokens() {
|
|
46
|
+
return this._availableMonthlyTokens ?? 0;
|
|
47
|
+
}
|
|
48
|
+
get availableExtraTokens() {
|
|
49
|
+
return this._availableExtraTokens ?? 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
Modules: { User: { name: "users" } },
|
|
54
|
+
rehydrate: (_module: unknown, data: any) => ({
|
|
55
|
+
...data,
|
|
56
|
+
company: data.company ? Object.assign(new FakeCompany(), data.company) : null,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
20
60
|
|
|
21
61
|
vi.mock("../../../../permissions", () => ({
|
|
22
62
|
Action: { Read: "read", Write: "write", Create: "create", Update: "update", Delete: "delete" },
|
|
@@ -139,3 +179,106 @@ describe("CurrentUserProvider", () => {
|
|
|
139
179
|
expect(getByTestId("current-user-id").textContent).toBe("new");
|
|
140
180
|
});
|
|
141
181
|
});
|
|
182
|
+
|
|
183
|
+
describe("CurrentUserProvider token updates", () => {
|
|
184
|
+
const STORED_USER = {
|
|
185
|
+
id: "user-1",
|
|
186
|
+
roles: [],
|
|
187
|
+
modules: [],
|
|
188
|
+
company: {
|
|
189
|
+
id: "company-1",
|
|
190
|
+
monthlyTokens: 1000,
|
|
191
|
+
_availableMonthlyTokens: 900,
|
|
192
|
+
_availableExtraTokens: 50,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
function CompanyConsumer() {
|
|
197
|
+
const { company } = useCurrentUserContext();
|
|
198
|
+
return (
|
|
199
|
+
<div data-testid="balances">
|
|
200
|
+
{company ? `${(company as any).availableMonthlyTokens}/${(company as any).availableExtraTokens}` : "null"}
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
beforeEach(() => {
|
|
206
|
+
localStorage.clear();
|
|
207
|
+
socketMock.reset();
|
|
208
|
+
vi.clearAllMocks();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("patches balances from company:tokens_updated without refetching the user", async () => {
|
|
212
|
+
localStorage.setItem("user", JSON.stringify(STORED_USER));
|
|
213
|
+
|
|
214
|
+
const { getByTestId } = render(
|
|
215
|
+
<CurrentUserProvider>
|
|
216
|
+
<CompanyConsumer />
|
|
217
|
+
</CurrentUserProvider>,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
221
|
+
expect(getByTestId("balances").textContent).toBe("900/50");
|
|
222
|
+
|
|
223
|
+
await act(async () => {
|
|
224
|
+
socketMock.emit("company:tokens_updated", {
|
|
225
|
+
type: "company:tokens_updated",
|
|
226
|
+
companyId: "company-1",
|
|
227
|
+
availableMonthlyTokens: 750,
|
|
228
|
+
availableExtraTokens: 40,
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(getByTestId("balances").textContent).toBe("750/40");
|
|
233
|
+
expect(UserService.findFullUser).not.toHaveBeenCalled();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("ignores company:tokens_updated for a different company", async () => {
|
|
237
|
+
localStorage.setItem("user", JSON.stringify(STORED_USER));
|
|
238
|
+
|
|
239
|
+
const { getByTestId } = render(
|
|
240
|
+
<CurrentUserProvider>
|
|
241
|
+
<CompanyConsumer />
|
|
242
|
+
</CurrentUserProvider>,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
246
|
+
|
|
247
|
+
await act(async () => {
|
|
248
|
+
socketMock.emit("company:tokens_updated", {
|
|
249
|
+
type: "company:tokens_updated",
|
|
250
|
+
companyId: "someone-else",
|
|
251
|
+
availableMonthlyTokens: 1,
|
|
252
|
+
availableExtraTokens: 1,
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
expect(getByTestId("balances").textContent).toBe("900/50");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("still refetches the full user on company:subscription_updated", async () => {
|
|
260
|
+
localStorage.setItem("user", JSON.stringify(STORED_USER));
|
|
261
|
+
vi.mocked(UserService.findFullUser).mockResolvedValue({
|
|
262
|
+
...STORED_USER,
|
|
263
|
+
dehydrate: () => STORED_USER,
|
|
264
|
+
} as any);
|
|
265
|
+
|
|
266
|
+
render(
|
|
267
|
+
<CurrentUserProvider>
|
|
268
|
+
<CompanyConsumer />
|
|
269
|
+
</CurrentUserProvider>,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
273
|
+
|
|
274
|
+
await act(async () => {
|
|
275
|
+
socketMock.emit("company:subscription_updated", {
|
|
276
|
+
type: "company:subscription_updated",
|
|
277
|
+
companyId: "company-1",
|
|
278
|
+
});
|
|
279
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(UserService.findFullUser).toHaveBeenCalledTimes(1);
|
|
283
|
+
});
|
|
284
|
+
});
|