Skip to content

Commit 4011c5e

Browse files
authored
chore(shared): Add React Query variant of usePageOrInfinite (#7143)
1 parent 7d9ade4 commit 4011c5e

33 files changed

+1278
-206
lines changed

.changeset/fuzzy-keys-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/shared': patch
3+
---
4+
5+
Build internal variants of all paginated hooks that use React Query instead of SWR.

.github/workflows/ci.yml

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ jobs:
191191
unit-tests:
192192
needs: [check-permissions, build-packages]
193193
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
194-
name: Unit Tests
194+
name: Unit Tests (${{ matrix.node-version }}, ${{ matrix.filter-label }}${{ matrix.clerk-use-rq == 'true' && ', RQ' || '' }})
195195
permissions:
196196
contents: read
197197
actions: write # needed for actions/upload-artifact
@@ -205,11 +205,17 @@ jobs:
205205
TURBO_SUMMARIZE: false
206206

207207
strategy:
208-
fail-fast: true
208+
fail-fast: false
209209
matrix:
210210
include:
211211
- node-version: 22
212212
test-filter: "**"
213+
clerk-use-rq: "false"
214+
filter-label: "**"
215+
- node-version: 22
216+
test-filter: "--filter=@clerk/shared --filter=@clerk/clerk-js"
217+
clerk-use-rq: "true"
218+
filter-label: "shared, clerk-js"
213219

214220
steps:
215221
- name: Checkout Repo
@@ -229,22 +235,35 @@ jobs:
229235
turbo-team: ${{ vars.TURBO_TEAM }}
230236
turbo-token: ${{ secrets.TURBO_TOKEN }}
231237

238+
- name: Rebuild @clerk/shared with CLERK_USE_RQ=true
239+
if: ${{ matrix.clerk-use-rq == 'true' }}
240+
run: pnpm turbo build $TURBO_ARGS --filter=@clerk/shared --force
241+
env:
242+
CLERK_USE_RQ: true
243+
244+
- name: Rebuild dependent packages with CLERK_USE_RQ=true
245+
if: ${{ matrix.clerk-use-rq == 'true' }}
246+
run: pnpm turbo build $TURBO_ARGS --filter=@clerk/shared^... --force
247+
env:
248+
CLERK_USE_RQ: true
249+
232250
- name: Run tests in packages
233251
run: |
234252
if [ "${{ matrix.test-filter }}" = "**" ]; then
235-
echo "Running full test suite on Node ${{ matrix.node-version }}."
253+
echo "Running full test suite on Node ${{ matrix.node-version }}"
236254
pnpm turbo test $TURBO_ARGS
237255
else
238-
echo "Running LTS subset on Node ${{ matrix.node-version }}."
256+
echo "Running tests: ${{ matrix.filter-label }}"
239257
pnpm turbo test $TURBO_ARGS ${{ matrix.test-filter }}
240258
fi
241259
env:
242260
NODE_VERSION: ${{ matrix.node-version }}
261+
CLERK_USE_RQ: ${{ matrix.clerk-use-rq }}
243262

244263
- name: Run Typedoc tests
245264
run: |
246-
# Only run Typedoc tests for one matrix version
247-
if [ "${{ matrix.node-version }}" == "22" ]; then
265+
# Only run Typedoc tests for one matrix version and main test run
266+
if [ "${{ matrix.node-version }}" == "22" ] && [ "${{ matrix.test-filter }}" = "**" ]; then
248267
pnpm test:typedoc
249268
fi
250269
env:
@@ -255,14 +274,14 @@ jobs:
255274
if: ${{ env.TURBO_SUMMARIZE == 'true' }}
256275
continue-on-error: true
257276
with:
258-
name: turbo-summary-report-unit-${{ github.run_id }}-${{ github.run_attempt }}-node-${{ matrix.node-version }}
277+
name: turbo-summary-report-unit-${{ github.run_id }}-${{ github.run_attempt }}-node-${{ matrix.node-version }}${{ matrix.clerk-use-rq == 'true' && '-rq' || '' }}
259278
path: .turbo/runs
260279
retention-days: 5
261280

262281
integration-tests:
263282
needs: [check-permissions, build-packages]
264283
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
265-
name: Integration Tests
284+
name: Integration Tests (${{ matrix.test-name }}, ${{ matrix.test-project }}${{ matrix.next-version && format(', {0}', matrix.next-version) || '' }}${{ matrix.clerk-use-rq == 'true' && ', RQ' || '' }})
266285
permissions:
267286
contents: read
268287
actions: write # needed for actions/upload-artifact
@@ -291,18 +310,28 @@ jobs:
291310
'vue',
292311
'nuxt',
293312
'react-router',
294-
'billing',
295313
'machine',
296314
'custom',
297315
]
298316
test-project: ["chrome"]
299317
include:
318+
- test-name: 'billing'
319+
test-project: 'chrome'
320+
clerk-use-rq: 'false'
321+
- test-name: 'billing'
322+
test-project: 'chrome'
323+
clerk-use-rq: 'true'
300324
- test-name: 'nextjs'
301325
test-project: 'chrome'
302326
next-version: '14'
303327
- test-name: 'nextjs'
304328
test-project: 'chrome'
305329
next-version: '15'
330+
clerk-use-rq: 'false'
331+
- test-name: 'nextjs'
332+
test-project: 'chrome'
333+
next-version: '15'
334+
clerk-use-rq: 'true'
306335
- test-name: 'nextjs'
307336
test-project: 'chrome'
308337
next-version: '16'
@@ -360,12 +389,24 @@ jobs:
360389
echo "affected=${AFFECTED}"
361390
echo "affected=${AFFECTED}" >> $GITHUB_OUTPUT
362391
392+
- name: Rebuild @clerk/shared with CLERK_USE_RQ=true
393+
if: ${{ steps.task-status.outputs.affected == '1' && matrix.clerk-use-rq == 'true' }}
394+
run: pnpm turbo build $TURBO_ARGS --filter=@clerk/shared --force
395+
env:
396+
CLERK_USE_RQ: true
397+
398+
- name: Rebuild dependent packages with CLERK_USE_RQ=true
399+
if: ${{ steps.task-status.outputs.affected == '1' && matrix.clerk-use-rq == 'true' }}
400+
run: pnpm turbo build $TURBO_ARGS --filter=@clerk/shared^... --force
401+
env:
402+
CLERK_USE_RQ: true
403+
363404
- name: Verdaccio
364405
if: ${{ steps.task-status.outputs.affected == '1' }}
365406
uses: ./.github/actions/verdaccio
366407
with:
367408
publish-cmd: |
368-
if [ "$(pnpm config get registry)" = "https://registry.npmjs.org/" ]; then echo 'Error: Using default registry' && exit 1; else pnpm turbo build $TURBO_ARGS --only && pnpm changeset publish --no-git-tag; fi
409+
if [ "$(pnpm config get registry)" = "https://registry.npmjs.org/" ]; then echo 'Error: Using default registry' && exit 1; else CLERK_USE_RQ=${{ matrix.clerk-use-rq }} pnpm turbo build $TURBO_ARGS --only && pnpm changeset publish --no-git-tag; fi
369410
370411
- name: Edit .npmrc [link-workspace-packages=false]
371412
run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc
@@ -425,6 +466,7 @@ jobs:
425466
E2E_NEXTJS_VERSION: ${{ matrix.next-version }}
426467
E2E_PROJECT: ${{ matrix.test-project }}
427468
E2E_CLERK_ENCRYPTION_KEY: ${{ matrix.clerk-encryption-key }}
469+
CLERK_USE_RQ: ${{ matrix.clerk-use-rq }}
428470
INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }}
429471
MAILSAC_API_KEY: ${{ secrets.MAILSAC_API_KEY }}
430472
NODE_EXTRA_CA_CERTS: ${{ github.workspace }}/integration/certs/rootCA.pem
@@ -433,7 +475,7 @@ jobs:
433475
if: ${{ cancelled() || failure() }}
434476
uses: actions/upload-artifact@v4
435477
with:
436-
name: playwright-traces-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.test-name }}
478+
name: playwright-traces-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.test-name }}${{ matrix.next-version && format('-next{0}', matrix.next-version) || '' }}${{ matrix.clerk-use-rq == 'true' && '-rq' || '' }}
437479
path: integration/test-results
438480
retention-days: 1
439481

packages/clerk-js/src/test/mock-helpers.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ActiveSessionResource, LoadedClerk } from '@clerk/shared/types';
22
import { type Mocked, vi } from 'vitest';
33

4+
import { QueryClient } from '../core/query-core';
45
import type { RouteContextValue } from '../ui/router';
56

67
type FunctionLike = (...args: any) => any;
@@ -45,6 +46,20 @@ export const mockClerkMethods = (clerk: LoadedClerk): DeepVitestMocked<LoadedCle
4546
// Cast clerk to any to allow mocking properties
4647
const clerkAny = clerk as any;
4748

49+
const defaultQueryClient = {
50+
__tag: 'clerk-rq-client' as const,
51+
client: new QueryClient({
52+
defaultOptions: {
53+
queries: {
54+
retry: false,
55+
// Setting staleTime to Infinity will not cause issues between tests as long as each test
56+
// case has its own wrapper that initializes a Clerk instance with a new QueryClient.
57+
staleTime: Infinity,
58+
},
59+
},
60+
}),
61+
};
62+
4863
mockMethodsOf(clerkAny);
4964
if (clerkAny.client) {
5065
mockMethodsOf(clerkAny.client.signIn);
@@ -76,6 +91,13 @@ export const mockClerkMethods = (clerk: LoadedClerk): DeepVitestMocked<LoadedCle
7691
if (clerkAny.billing) {
7792
mockMethodsOf(clerkAny.billing);
7893
}
94+
95+
// Mock the __internal_queryClient getter property
96+
Object.defineProperty(clerkAny, '__internal_queryClient', {
97+
get: vi.fn(() => defaultQueryClient),
98+
configurable: true,
99+
});
100+
79101
mockProp(clerkAny, 'navigate');
80102
mockProp(clerkAny, 'setActive');
81103
mockProp(clerkAny, 'redirectWithAuth');

packages/clerk-js/src/ui/components/Checkout/__tests__/Checkout.test.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ describe('Checkout', () => {
316316
});
317317

318318
const freeTrialEndsAt = new Date('2025-08-19');
319+
320+
fixtures.clerk.user?.getPaymentMethods.mockResolvedValue({
321+
data: [],
322+
total_count: 0,
323+
});
324+
319325
fixtures.clerk.billing.startCheckout.mockResolvedValue({
320326
id: 'chk_trial_1',
321327
status: 'needs_confirmation',
@@ -1034,13 +1040,18 @@ describe('Checkout', () => {
10341040
{ wrapper },
10351041
);
10361042

1037-
await waitFor(async () => {
1043+
await waitFor(() => {
10381044
expect(getByRole('heading', { name: 'Checkout' })).toBeVisible();
1039-
const addPaymentMethodButton = getByText('Add payment method');
1040-
expect(addPaymentMethodButton).toBeVisible();
1041-
await userEvent.click(addPaymentMethodButton);
10421045
});
10431046

1047+
const addPaymentMethodButton = await waitFor(() => {
1048+
const button = getByText('Add payment method');
1049+
expect(button).toBeVisible();
1050+
return button;
1051+
});
1052+
1053+
await userEvent.click(addPaymentMethodButton);
1054+
10441055
await waitFor(() => {
10451056
expect(getByRole('button', { name: 'Start free trial' })).toBeInTheDocument();
10461057
});

packages/clerk-js/src/ui/components/OrganizationList/__tests__/OrganizationList.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('OrganizationList', () => {
4545
});
4646
});
4747

48-
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
48+
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValue(
4949
Promise.resolve({
5050
data: [
5151
createFakeUserOrganizationMembership({
@@ -117,7 +117,7 @@ describe('OrganizationList', () => {
117117
});
118118
});
119119

120-
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
120+
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValue(
121121
Promise.resolve({
122122
data: [
123123
createFakeUserOrganizationMembership({
@@ -156,7 +156,7 @@ describe('OrganizationList', () => {
156156
}),
157157
);
158158

159-
fixtures.clerk.user?.getOrganizationInvitations.mockReturnValueOnce(
159+
fixtures.clerk.user?.getOrganizationInvitations.mockReturnValue(
160160
Promise.resolve({
161161
data: [invitation],
162162
total_count: 1,
@@ -342,7 +342,7 @@ describe('OrganizationList', () => {
342342
});
343343

344344
await waitFor(async () => {
345-
fixtures.clerk.setActive.mockReturnValueOnce(Promise.resolve());
345+
fixtures.clerk.setActive.mockReturnValue(Promise.resolve());
346346
await userEvent.click(getByText(/Personal account/i));
347347

348348
expect(fixtures.router.navigate).toHaveBeenCalledWith(`/user/test_user_id`);
@@ -376,7 +376,7 @@ describe('OrganizationList', () => {
376376
},
377377
});
378378

379-
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
379+
fixtures.clerk.user?.getOrganizationMemberships.mockReturnValue(
380380
Promise.resolve({
381381
data: [membership],
382382
total_count: 1,
@@ -392,7 +392,7 @@ describe('OrganizationList', () => {
392392
});
393393

394394
await waitFor(async () => {
395-
fixtures.clerk.setActive.mockReturnValueOnce(Promise.resolve());
395+
fixtures.clerk.setActive.mockReturnValue(Promise.resolve());
396396
await userEvent.click(getByRole('button', { name: /Org1/i }));
397397
expect(fixtures.clerk.setActive).toHaveBeenCalledWith(
398398
expect.objectContaining({
@@ -423,7 +423,7 @@ describe('OrganizationList', () => {
423423
wrapper,
424424
});
425425

426-
fixtures.clerk.setActive.mockReturnValueOnce(Promise.resolve());
426+
fixtures.clerk.setActive.mockReturnValue(Promise.resolve());
427427
await waitFor(async () =>
428428
expect(await findByRole('menuitem', { name: 'Create organization' })).toBeInTheDocument(),
429429
);

0 commit comments

Comments
 (0)