diff --git a/.changeset/slimy-maps-read.md b/.changeset/slimy-maps-read.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/slimy-maps-read.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts b/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts index a3af6124a73..2ad17f92cbf 100644 --- a/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts +++ b/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts @@ -1,42 +1,144 @@ -// TODO: jest fails because of a circular dependency on Client -> Base -> Client -// This circular dep is a known issue we plan to address soon. Enable the tests then -import { describe, it } from 'vitest'; +import type { Resources, UserJSON } from '@clerk/shared/types'; +import { describe, expect, it, vi } from 'vitest'; -describe.skip('memoizeStateListenerCallback', () => { - it.skip('runs', () => { - // TODO +import { User } from '../../core/resources/User'; +import { memoizeListenerCallback } from '../memoizeStateListenerCallback'; + +function createTestUser(overrides: Partial = {}): User { + const defaultUserJSON: UserJSON = { + email_addresses: [], + external_accounts: [], + first_name: 'clerk', + id: 'u1', + phone_numbers: [], + updated_at: 1, + web3_wallets: [], + } as unknown as UserJSON; + + return new User({ ...defaultUserJSON, ...overrides } as unknown as UserJSON); +} + +describe('memoizeStateListenerCallback', () => { + it('returns same user ref if user obj state has not changed', () => { + const user1 = createTestUser(); + const user2 = createTestUser(); + + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: user1 }); + listener({ client: null, organization: null, session: null, user: user2 }); + + expect(calledWith?.user).toBe(user1); }); -}); -// import { Resources, UserJSON } from '@clerk/shared/types'; -// -// const frontEndApi = ''; -// const path = ''; -// -// describe('memoizeStateListenerCallback', () => { -// it('returns same user ref if user obj state has not changed', () => { -// const user1 = new User(frontEndApi, path, { -// id: 'u1', -// updated_at: 1, -// first_name: 'clerk', -// } as UserJSON); -// -// const user2 = new User(frontEndApi, path, { -// id: 'u1', -// updated_at: 1, -// first_name: 'clerk', -// } as UserJSON); -// -// let calledWith: any; -// const listener = memoizeListenerCallback( -// jest.fn((e: Resources) => { -// console.log(e); -// calledWith = e.user; -// }), -// ); -// -// listener(({ user: user1 } as any) as Resources); -// listener(({ user: user2 } as any) as Resources); -// expect(calledWith).toBe(user1); -// }); -// }); + it('returns new user ref if user obj state has changed', () => { + const user1 = createTestUser(); + const user2 = createTestUser({ updated_at: 2 }); + + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: user1 }); + listener({ client: null, organization: null, session: null, user: user2 }); + + expect(calledWith?.user).toBe(user2); + }); + + it('returns new user ref if user id has changed', () => { + const user1 = createTestUser(); + const user2 = createTestUser({ id: 'u2' }); + + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: user1 }); + listener({ client: null, organization: null, session: null, user: user2 }); + + expect(calledWith?.user).toBe(user2); + }); + + it('handles user becoming null', () => { + const user1 = createTestUser(); + + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: user1 }); + listener({ client: null, organization: null, session: null, user: null }); + + expect(calledWith?.user).toBe(null); + }); + + it('handles user transitioning from null to defined', () => { + const user1 = createTestUser(); + + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: null }); + listener({ client: null, organization: null, session: null, user: user1 }); + + expect(calledWith?.user).toBe(user1); + }); + + it('calls the callback function each time', () => { + const user1 = createTestUser(); + + const mockCallback = vi.fn(); + const listener = memoizeListenerCallback(mockCallback); + + listener({ client: null, organization: null, session: null, user: user1 }); + listener({ client: null, organization: null, session: null, user: user1 }); + + expect(mockCallback).toHaveBeenCalledTimes(2); + }); + + it('treats null and undefined as different values (null to undefined)', () => { + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: null }); + listener({ client: null, organization: null, session: null, user: undefined }); + + expect(calledWith?.user).toBe(undefined); + }); + + it('treats null and undefined as different values (undefined to null)', () => { + let calledWith: Resources | undefined; + const listener = memoizeListenerCallback( + vi.fn((e: Resources) => { + calledWith = e; + }), + ); + + listener({ client: null, organization: null, session: null, user: undefined }); + listener({ client: null, organization: null, session: null, user: null }); + + expect(calledWith?.user).toBe(null); + }); +});