import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import vm from 'node:vm'; import { beforeEach, describe, expect, test, vi } from 'vitest'; const repoRoot = process.cwd(); const pageScriptPath = join( repoRoot, 'miniprogram', 'pages', 'web-view', 'index.js', ); type MiniProgramPage = { data: Record; setData: (patch: Record) => void; onLoad: (query?: Record) => Promise; onShow: () => void; consumePayResult: () => void; }; function createWxMock() { return { getStorageSync: vi.fn(() => ''), getSystemInfoSync: vi.fn(() => ({ platform: 'ios' })), login: vi.fn(), navigateBack: vi.fn(), removeStorageSync: vi.fn(), request: vi.fn(), setStorageSync: vi.fn(), }; } function loadWebViewPage( wxMock: ReturnType, configOverrides: Record = {}, ) { let pageConfig: Record | null = null; const source = readFileSync(pageScriptPath, 'utf8'); const sandbox = { console, getCurrentPages: () => [], module: { exports: {} }, Page(config: Record) { pageConfig = config; }, setTimeout(callback: () => void) { callback(); return 1; }, require(requestPath: string) { if (requestPath === '../../config') { return { API_BASE_URL: 'https://www.genarrative.world/', MINI_PROGRAM_APP_ID: 'wx-test-app', MINI_PROGRAM_ENV: 'release', WEB_VIEW_ENTRY_URL: 'https://www.genarrative.world/', WEB_VIEW_SOURCE_QUERY: { clientType: 'mini_program', clientRuntime: 'wechat_mini_program', }, ...configOverrides, }; } throw new Error(`Unexpected require: ${requestPath}`); }, wx: wxMock, }; vm.runInNewContext(source, sandbox, { filename: pageScriptPath }); if (!pageConfig) { throw new Error('web-view page did not call Page()'); } const page = { ...pageConfig, data: { ...(pageConfig.data as Record) }, setData(patch: Record) { Object.assign(this.data, patch); }, } as MiniProgramPage; return page; } describe('mini-program web-view auth page', () => { beforeEach(() => { vi.clearAllMocks(); }); test('默认进入时刷新微信小程序登录态后打开 web-view', async () => { const wxMock = createWxMock(); wxMock.login.mockImplementation(({ success }) => { success({ code: 'wx-login-code' }); }); wxMock.request.mockImplementation(({ success }) => { success({ statusCode: 200, data: { token: 'jwt-active-wechat', bindingStatus: 'active', }, }); }); const page = loadWebViewPage(wxMock); await page.onLoad({}); expect(wxMock.login).toHaveBeenCalledTimes(1); expect(wxMock.request).toHaveBeenCalledWith( expect.objectContaining({ url: 'https://www.genarrative.world/api/auth/wechat/miniprogram-login', method: 'POST', data: { code: 'wx-login-code' }, }), ); expect(page.data.loading).toBe(false); expect(page.data.phoneBindingRequired).toBe(false); expect(page.data.webViewUrl).toBe( 'https://www.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program#auth_provider=wechat&auth_token=jwt-active-wechat&auth_binding_status=active', ); }); test('默认匿名进入 web-view 仍不依赖 API_BASE_URL 配置', async () => { const wxMock = createWxMock(); const page = loadWebViewPage(wxMock, { API_BASE_URL: '', }); await page.onLoad({}); expect(wxMock.login).not.toHaveBeenCalled(); expect(wxMock.request).not.toHaveBeenCalled(); expect(page.data.errorMessage).toBe(''); expect(page.data.webViewUrl).toBe( 'https://www.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program', ); }); test('onShow 二次检查支付结果并写回 web-view hash', () => { const wxMock = createWxMock(); wxMock.getStorageSync.mockImplementation((key) => key === 'genarrative:wechat-pay-result' ? 'request-1:success:order-1' : '', ); const page = loadWebViewPage(wxMock); page.data.webViewUrl = 'https://www.genarrative.world/?clientType=mini_program#tab=profile'; page.onShow(); expect(wxMock.removeStorageSync).toHaveBeenCalledWith( 'genarrative:wechat-pay-result', ); expect(page.data.webViewUrl).toBe( 'https://www.genarrative.world/?clientType=mini_program#tab=profile&wx_pay_result=request-1%3Asuccess%3Aorder-1', ); }); test('H5 请求登录时才启动微信小程序登录并进入手机号授权态', async () => { const wxMock = createWxMock(); wxMock.login.mockImplementation(({ success }) => { success({ code: 'wx-login-code' }); }); wxMock.request.mockImplementation(({ success }) => { success({ statusCode: 200, data: { token: 'jwt-pending-wechat', bindingStatus: 'pending_bind_phone', }, }); }); const page = loadWebViewPage(wxMock); await page.onLoad({ authAction: 'login', returnTo: 'previous' }); expect(wxMock.login).toHaveBeenCalledTimes(1); expect(wxMock.request).toHaveBeenCalledWith( expect.objectContaining({ url: 'https://www.genarrative.world/api/auth/wechat/miniprogram-login', method: 'POST', data: { code: 'wx-login-code' }, }), ); expect(page.data.webViewUrl).toBe(''); expect(page.data.loading).toBe(false); expect(page.data.phoneBindingRequired).toBe(true); }); });