AuthLayout.utils.test.ts156 lines · main
1import { describe, expect, it } from 'vitest'
2
3import { generateAuthMenu, GenerateAuthMenuOptions } from './AuthLayout.utils'
4
5const allFeaturesEnabled: GenerateAuthMenuOptions = {
6 ref: 'test-ref',
7 isPlatform: true,
8 showOverview: true,
9 features: {
10 signInProviders: true,
11 rateLimits: true,
12 emails: true,
13 multiFactor: true,
14 attackProtection: true,
15 performance: true,
16 passkeys: true,
17 },
18}
19
20const allFeaturesDisabled: GenerateAuthMenuOptions = {
21 ref: 'test-ref',
22 isPlatform: true,
23 showOverview: false,
24 features: {
25 signInProviders: false,
26 rateLimits: false,
27 emails: false,
28 multiFactor: false,
29 attackProtection: false,
30 performance: false,
31 passkeys: true,
32 },
33}
34
35function flatItemNames(menu: ReturnType<typeof generateAuthMenu>): string[] {
36 return menu.flatMap((group) => group.items.map((item) => item.name))
37}
38
39function findItem(menu: ReturnType<typeof generateAuthMenu>, name: string) {
40 for (const group of menu) {
41 const item = group.items.find((i) => i.name === name)
42 if (item) return item
43 }
44 return undefined
45}
46
47describe('generateAuthMenu', () => {
48 it('platform with all features enabled includes all menu items', () => {
49 const menu = generateAuthMenu(allFeaturesEnabled)
50 const names = flatItemNames(menu)
51
52 expect(names).toContain('Overview')
53 expect(names).toContain('Users')
54 expect(names).toContain('OAuth Apps')
55 expect(names).toContain('Emails')
56 expect(names).toContain('Sign In / Providers')
57 expect(names).toContain('OAuth Server')
58 expect(names).toContain('Passkeys')
59 expect(names).toContain('Sessions')
60 expect(names).toContain('Rate Limits')
61 expect(names).toContain('Multi-Factor')
62 expect(names).toContain('URL Configuration')
63 expect(names).toContain('Attack Protection')
64 expect(names).toContain('Auth Hooks')
65 expect(names).toContain('Audit Logs')
66 expect(names).toContain('Performance')
67 })
68
69 it('platform with all features disabled shows only core items', () => {
70 const menu = generateAuthMenu(allFeaturesDisabled)
71 const names = flatItemNames(menu)
72
73 expect(names).toContain('Users')
74 expect(names).toContain('OAuth Apps')
75 expect(names).toContain('Policies')
76 expect(names).toContain('OAuth Server')
77 expect(names).toContain('Passkeys')
78 expect(names).toContain('Sessions')
79 expect(names).toContain('URL Configuration')
80 expect(names).toContain('Auth Hooks')
81 expect(names).toContain('Audit Logs')
82
83 expect(names).not.toContain('Overview')
84 expect(names).not.toContain('Emails')
85 expect(names).not.toContain('Sign In / Providers')
86 expect(names).not.toContain('Rate Limits')
87 expect(names).not.toContain('Multi-Factor')
88 expect(names).not.toContain('Attack Protection')
89 expect(names).not.toContain('Performance')
90 })
91
92 it('self-hosted hides OAuth Apps, Notifications, and platform-only Configuration items', () => {
93 const menu = generateAuthMenu({
94 ...allFeaturesEnabled,
95 isPlatform: false,
96 })
97 const names = flatItemNames(menu)
98 const groupTitles = menu.map((g) => g.title)
99
100 expect(names).not.toContain('OAuth Apps')
101 expect(groupTitles).not.toContain('Notifications')
102
103 // Configuration should only have Policies
104 const configGroup = menu.find((g) => g.title === 'Configuration')!
105 expect(configGroup.items).toHaveLength(1)
106 expect(configGroup.items[0].name).toBe('Policies')
107 })
108
109 it('shows Overview when showOverview is true', () => {
110 const menu = generateAuthMenu({ ...allFeaturesDisabled, showOverview: true })
111 expect(flatItemNames(menu)).toContain('Overview')
112 })
113
114 it('hides Overview when showOverview is false', () => {
115 const menu = generateAuthMenu({ ...allFeaturesEnabled, showOverview: false })
116 expect(flatItemNames(menu)).not.toContain('Overview')
117 })
118
119 it.each([
120 ['signInProviders', 'Sign In / Providers'],
121 ['rateLimits', 'Rate Limits'],
122 ['emails', 'Emails'],
123 ['multiFactor', 'Multi-Factor'],
124 ['attackProtection', 'Attack Protection'],
125 ['performance', 'Performance'],
126 ] as const)('feature flag %s toggles %s', (flag, itemName) => {
127 const withEnabled = generateAuthMenu({
128 ...allFeaturesDisabled,
129 features: { ...allFeaturesDisabled.features, [flag]: true },
130 })
131 const withDisabled = generateAuthMenu({
132 ...allFeaturesEnabled,
133 features: { ...allFeaturesEnabled.features, [flag]: false },
134 })
135
136 expect(flatItemNames(withEnabled)).toContain(itemName)
137 expect(flatItemNames(withDisabled)).not.toContain(itemName)
138 })
139
140 it('generates correct URLs using the ref parameter', () => {
141 const menu = generateAuthMenu({ ...allFeaturesEnabled, ref: 'my-project' })
142 const users = findItem(menu, 'Users')
143 const oauthApps = findItem(menu, 'OAuth Apps')
144
145 expect(users?.url).toBe('/project/my-project/auth/users')
146 expect(oauthApps?.url).toBe('/project/my-project/auth/oauth-apps')
147 })
148
149 it('hides Passkeys when passkeys feature is false', () => {
150 const menu = generateAuthMenu({
151 ...allFeaturesEnabled,
152 features: { ...allFeaturesEnabled.features, passkeys: false },
153 })
154 expect(flatItemNames(menu)).not.toContain('Passkeys')
155 })
156})