OrganizationInvite.utils.test.ts126 lines · main
1import { describe, expect, test } from 'vitest'
2
3import {
4 getOrganizationInviteContent,
5 getOrganizationInviteStatus,
6 type OrganizationInviteStatus,
7} from '@/components/interfaces/OrganizationInvite/OrganizationInvite.utils'
8import type { OrganizationInviteByToken } from '@/data/organization-members/organization-invitation-token-query'
9import type { ResponseError } from '@/types'
10
11const READY_INVITE: OrganizationInviteByToken = {
12 authorized_user: true,
13 email_match: true,
14 expired_token: false,
15 invite_id: 42,
16 organization_name: 'Acme Corp',
17 sso_mismatch: false,
18 token_does_not_exist: false,
19}
20
21const responseError = (message: string, code = 500) => ({ message, code }) as ResponseError
22type StatusOverrides = Partial<Parameters<typeof getOrganizationInviteStatus>[0]>
23
24const getStatus = (overrides: StatusOverrides = {}) =>
25 getOrganizationInviteStatus({
26 data: READY_INVITE,
27 error: null,
28 isErrorInvitation: false,
29 isLoadingInvitation: false,
30 isLoadingProfile: false,
31 isLoggedIn: true,
32 isRouterReady: true,
33 isSuccessInvitation: true,
34 profileExists: true,
35 ...overrides,
36 })
37
38describe('OrganizationInvite utils', () => {
39 test.each<[string, OrganizationInviteStatus, StatusOverrides]>([
40 ['signed out when there is no current user', 'signed-out', { isLoggedIn: false }],
41 ['loading while the profile is loading', 'loading', { isLoadingProfile: true }],
42 ['loading while the invite is loading', 'loading', { isLoadingInvitation: true }],
43 [
44 'no longer valid for accepted or declined invites',
45 'no-longer-valid',
46 {
47 data: undefined,
48 error: responseError('Failed to retrieve organization', 401),
49 isErrorInvitation: true,
50 isSuccessInvitation: false,
51 },
52 ],
53 [
54 'invalid when the API returns a missing token response',
55 'invalid',
56 {
57 data: { ...READY_INVITE, token_does_not_exist: true },
58 },
59 ],
60 [
61 'invalid when the invite lookup 404s',
62 'invalid',
63 {
64 data: undefined,
65 error: responseError('Not Found', 404),
66 isErrorInvitation: true,
67 isSuccessInvitation: false,
68 },
69 ],
70 [
71 'error for other API failures',
72 'error',
73 {
74 data: undefined,
75 error: responseError('Failed to retrieve token', 500),
76 isErrorInvitation: true,
77 isSuccessInvitation: false,
78 },
79 ],
80 [
81 'expired when the token has expired',
82 'expired',
83 {
84 data: { ...READY_INVITE, expired_token: true },
85 },
86 ],
87 [
88 'wrong account when the invite email does not match',
89 'wrong-account',
90 {
91 data: { ...READY_INVITE, email_match: false },
92 },
93 ],
94 ['ready when the invite can be accepted', 'ready', {}],
95 ])('returns %s', (_name, expected, overrides) => {
96 expect(getStatus(overrides)).toBe(expected)
97 })
98
99 test.each<[OrganizationInviteStatus, string, string | undefined]>([
100 ['signed-out', 'View invitation', 'Sign in or create an account to view this invitation'],
101 ['ready', 'Join Acme Corp', 'You have been invited to join this Briven organization'],
102 ['wrong-account', 'Wrong account', undefined],
103 ['expired', 'Invite expired', undefined],
104 ['invalid', 'Invite invalid', undefined],
105 ['no-longer-valid', 'Invite no longer available', undefined],
106 ['error', 'Unable to load invitation', undefined],
107 ])('returns content for %s', (status, title, description) => {
108 expect(
109 getOrganizationInviteContent({
110 data: READY_INVITE,
111 isSignUpEnabled: true,
112 status,
113 })
114 ).toEqual({ title, ...(description ? { description } : {}) })
115 })
116
117 test('omits sign-up copy when sign-up is disabled', () => {
118 expect(
119 getOrganizationInviteContent({
120 data: READY_INVITE,
121 isSignUpEnabled: false,
122 status: 'signed-out',
123 }).description
124 ).toBe('Sign in to view this invitation')
125 })
126})