接入账号状态宿主刷新

账号状态变化优先通过宿主刷新当前 WebView

宿主刷新不可用时回退浏览器刷新

补充 AuthGate 宿主刷新测试和宿主壳文档
This commit is contained in:
2026-06-18 07:43:26 +08:00
parent 621719077f
commit 29cd7866fc
5 changed files with 89 additions and 3 deletions
+62 -1
View File
@@ -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();
+21 -1
View File
@@ -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();