Implement registration invite code flow and admin invite codes

This commit is contained in:
2026-04-30 20:49:38 +08:00
parent 2aef81e51d
commit 42aab671ed
32 changed files with 1241 additions and 179 deletions

View File

@@ -88,13 +88,19 @@ const mockUser: AuthUser = {
beforeEach(() => {
vi.clearAllMocks();
window.history.replaceState(null, '', '/');
authMocks.consumeAuthCallbackResult.mockReturnValue(null);
authMocks.ensureStoredAccessToken.mockResolvedValue('jwt-existing-token');
authMocks.getCurrentAuthUser.mockResolvedValue({
user: null,
availableLoginMethods: ['phone'],
});
authMocks.loginWithPhoneCode.mockResolvedValue(mockUser);
authMocks.loginWithPhoneCode.mockResolvedValue({
token: 'jwt-phone',
user: mockUser,
created: false,
referral: null,
});
authMocks.authEntry.mockResolvedValue(mockUser);
authMocks.changePassword.mockResolvedValue(mockUser);
authMocks.logoutAllAuthSessions.mockResolvedValue(undefined);
@@ -287,6 +293,7 @@ test('auth gate opens a login modal for protected actions and resumes after logi
expect(authMocks.loginWithPhoneCode).toHaveBeenCalledWith(
'13800000000',
'123456',
undefined,
);
expect(authMocks.getCurrentAuthUser).toHaveBeenCalledTimes(1);
expect(onAuthenticated).toHaveBeenCalledTimes(1);
@@ -295,6 +302,44 @@ test('auth gate opens a login modal for protected actions and resumes after logi
expect(screen.queryByRole('dialog', { name: '账号入口' })).toBeNull();
});
test('auth gate opens register tab and preloads invite code from url', async () => {
const user = userEvent.setup();
window.history.replaceState(null, '', '/?inviteCode=spring-2026');
authMocks.getAuthLoginOptions.mockResolvedValue({
availableLoginMethods: ['phone'],
});
render(
<AuthGate>
<div></div>
</AuthGate>,
);
const dialog = await screen.findByRole('dialog', { name: '账号入口' });
await waitFor(() => {
expect(
within(dialog)
.getByRole('tab', { name: '注册' })
.getAttribute('aria-selected'),
).toBe('true');
});
expect(
(within(dialog).getByLabelText('邀请码') as HTMLInputElement).value,
).toBe('SPRING2026');
await user.type(within(dialog).getByLabelText('手机号'), '13800000000');
await user.type(within(dialog).getByLabelText('验证码'), '123456');
await user.click(within(dialog).getByRole('button', { name: '注册' }));
await waitFor(() => {
expect(authMocks.loginWithPhoneCode).toHaveBeenCalledWith(
'13800000000',
'123456',
'SPRING2026',
);
});
});
test('auth state refresh keeps mounted platform content and local tab state', async () => {
const user = userEvent.setup();
authMocks.getCurrentAuthUser.mockResolvedValue({