接入账号状态宿主刷新
账号状态变化优先通过宿主刷新当前 WebView 宿主刷新不可用时回退浏览器刷新 补充 AuthGate 宿主刷新测试和宿主壳文档
This commit is contained in:
@@ -7,9 +7,15 @@ import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import type { AuthSessionSummary, AuthUser } from '../../services/authService';
|
||||
import { LEGAL_CONSENT_STORAGE_KEY } from '../common/legalDocuments';
|
||||
import { AuthGate, setAuthGateReloadForTest } from './AuthGate';
|
||||
import {
|
||||
AuthGate,
|
||||
setAuthGateBrowserReloadForTest,
|
||||
setAuthGateReloadForTest,
|
||||
} from './AuthGate';
|
||||
import { useAuthUi } from './AuthUiContext';
|
||||
|
||||
const browserReloadMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
const authMocks = vi.hoisted(() => ({
|
||||
authEntry: vi.fn(),
|
||||
changePassword: vi.fn(),
|
||||
@@ -84,11 +90,13 @@ const hostBridgeMocks = vi.hoisted(() => ({
|
||||
miniProgramEnv: null as string | null,
|
||||
})),
|
||||
requestHostLogin: vi.fn(),
|
||||
reloadHostWebView: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/host-bridge/hostBridge', () => ({
|
||||
getHostRuntime: hostBridgeMocks.getHostRuntime,
|
||||
requestHostLogin: hostBridgeMocks.requestHostLogin,
|
||||
reloadHostWebView: hostBridgeMocks.reloadHostWebView,
|
||||
}));
|
||||
|
||||
vi.mock('../../hooks/useGameSettings', () => ({
|
||||
@@ -131,6 +139,7 @@ beforeEach(() => {
|
||||
window.localStorage.clear();
|
||||
window.history.replaceState(null, '', '/');
|
||||
setAuthGateReloadForTest(vi.fn());
|
||||
setAuthGateBrowserReloadForTest(browserReloadMock);
|
||||
authMocks.consumeAuthCallbackResult.mockReturnValue(null);
|
||||
authMocks.ensureStoredAccessToken.mockResolvedValue('jwt-existing-token');
|
||||
authMocks.getStoredAccessToken.mockReturnValue('');
|
||||
@@ -189,10 +198,12 @@ beforeEach(() => {
|
||||
miniProgramEnv: null,
|
||||
});
|
||||
hostBridgeMocks.requestHostLogin.mockResolvedValue(true);
|
||||
hostBridgeMocks.reloadHostWebView.mockResolvedValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setAuthGateReloadForTest(null);
|
||||
setAuthGateBrowserReloadForTest(null);
|
||||
});
|
||||
|
||||
async function acceptLegalConsent(
|
||||
@@ -780,6 +791,56 @@ test('logout withdraws user context before backend request finishes', async () =
|
||||
expect(reload).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('auth state reload uses native host webview reload before browser reload', async () => {
|
||||
const user = userEvent.setup();
|
||||
setAuthGateReloadForTest(null);
|
||||
hostBridgeMocks.reloadHostWebView.mockResolvedValueOnce(true);
|
||||
authMocks.getCurrentAuthUser.mockResolvedValue({
|
||||
user: mockUser,
|
||||
availableLoginMethods: ['phone'],
|
||||
});
|
||||
|
||||
render(
|
||||
<AuthGate>
|
||||
<LogoutStateProbe />
|
||||
</AuthGate>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText('当前用户:测试玩家')).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '退出登录' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(hostBridgeMocks.reloadHostWebView).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
expect(browserReloadMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('auth state reload falls back to browser reload when native host cannot reload', async () => {
|
||||
const user = userEvent.setup();
|
||||
setAuthGateReloadForTest(null);
|
||||
hostBridgeMocks.reloadHostWebView.mockResolvedValueOnce(false);
|
||||
authMocks.getCurrentAuthUser.mockResolvedValue({
|
||||
user: mockUser,
|
||||
availableLoginMethods: ['phone'],
|
||||
});
|
||||
|
||||
render(
|
||||
<AuthGate>
|
||||
<LogoutStateProbe />
|
||||
</AuthGate>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText('当前用户:测试玩家')).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '退出登录' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(hostBridgeMocks.reloadHostWebView).toHaveBeenCalledTimes(1);
|
||||
expect(browserReloadMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
test('auth gate shows sms send feedback in the login modal', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
} from '../../services/authService';
|
||||
import {
|
||||
getHostRuntime,
|
||||
reloadHostWebView,
|
||||
requestHostLogin,
|
||||
} from '../../services/host-bridge/hostBridge';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
@@ -70,12 +71,31 @@ type AuthStatus =
|
||||
|
||||
const REQUIRED_LOGIN_METHODS: AuthLoginMethod[] = ['phone', 'password'];
|
||||
|
||||
let reloadCurrentPageForAuthStateChange = () => {
|
||||
let reloadBrowserPageForAuthStateChange = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
function reloadHostPageForAuthStateChange() {
|
||||
void reloadHostWebView()
|
||||
.then((handled) => {
|
||||
if (!handled) {
|
||||
reloadBrowserPageForAuthStateChange();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reloadBrowserPageForAuthStateChange();
|
||||
});
|
||||
}
|
||||
|
||||
let reloadCurrentPageForAuthStateChange = reloadHostPageForAuthStateChange;
|
||||
|
||||
export function setAuthGateReloadForTest(handler: (() => void) | null) {
|
||||
reloadCurrentPageForAuthStateChange =
|
||||
handler ?? reloadHostPageForAuthStateChange;
|
||||
}
|
||||
|
||||
export function setAuthGateBrowserReloadForTest(handler: (() => void) | null) {
|
||||
reloadBrowserPageForAuthStateChange =
|
||||
handler ??
|
||||
(() => {
|
||||
window.location.reload();
|
||||
|
||||
Reference in New Issue
Block a user