import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron'; type WalletFixture = { requests: string[]; paid: boolean; created: boolean }; type FixtureGlobal = typeof globalThis & { __pointWalletE2E: WalletFixture }; test.describe('Permanent point wallet', () => { test('reopens an unpaid order, keeps its frozen amount, and refreshes only after confirmed credit', async ({ launchElectronApp }, testInfo) => { const app = await launchElectronApp({ skipSetup: true }); try { const page = await getStableWindow(app); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); const applicationUrl = page.url(); await page.goto('about:blank'); await app.evaluate(({ ipcMain }) => { const state: WalletFixture = { requests: [], paid: false, created: false }; (globalThis as FixtureGlobal).__pointWalletE2E = state; const result = (json: unknown) => ({ ok: true, data: { status: 200, ok: true, json } }); const points = () => ({ total: state.paid ? '500.00' : '0.00', used: '0.00', reserved: '0.00', total_remaining: state.paid ? '500.00' : '0.00', entitlement_source: 'self', family_shared: false, can_recharge: true, shared_available: null, expires_at: null, }); const order = () => ({ id: 'order-e2e', product_id: 'product-e2e', product_name: '500 词元点数', status: state.paid ? 'succeeded' : 'pending', amount_cents: 1000, point_amount: '500.00', currency: 'CNY', created_at: '2026-09-22T00:00:00Z', paid_at: state.paid ? '2026-09-22T01:00:00Z' : null, manual_review_required: false, qr_payload: state.paid ? null : 'weixin://wxpay/e2e-fixture-not-a-real-payment', }); ipcMain.removeHandler('hostapi:fetch'); ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string }) => { const path = request.path ?? ''; const method = (request.method ?? 'GET').toUpperCase(); state.requests.push(method + ' ' + path); if (path === '/api/auth/session/sync') return result({ success: true, session: { accessToken: 'point-wallet-fixture-token', tokenType: 'Bearer', expiresAt: Date.now() + 3_600_000, lastActiveAt: Date.now(), canRefresh: false, }, }); if (path === '/api/auth/me') return result({ success: true, user: { username: 'point-wallet', userId: 'point-wallet-user', tenantId: null, deptId: null, authorities: [] }, moduleAccess: { programming: true, design: true, robot: true }, }); if (path === '/api/works/user/agent-profile') return result({ success: true, profile: { display_name: '词元点数用户', age: null, gender: null, avatar_url: null, share_age_with_agents: false, share_gender_with_agents: false, analysis_enabled: true, completed: true, version: 1, updated_at: '2026-09-22T00:00:00Z', }, }); if (path === '/api/works/billing/me') return result({ success: true, data: { own_points: points(), points: points(), funding_error: null, can_recharge: true, payment_configured: true, signup_bonus: '100.00', points_per_yuan: 50, recharge_products: [{ id: 'product-e2e', name: '词元点数', amount_cents: state.created ? 2000 : 1000, point_amount: state.created ? '1000.00' : '500.00', currency: 'CNY', }], } }); if (path.startsWith('/api/works/billing/recharge/orders?')) return result({ success: true, data: { items: state.created ? [order()] : [] }, }); if (path === '/api/works/billing/recharge/orders' && method === 'POST') { state.created = true; return result({ success: true, data: order() }); } if (path === '/api/works/billing/recharge/orders/order-e2e') return result({ success: true, data: order() }); if (path.startsWith('/api/works/billing/points/transactions?')) return result({ success: true, data: { has_more: false, items: state.paid ? [{ id: 'credit-e2e', kind: 'credit', description: '充值', status: 'credited', points: '500.00', reserved_points: '0.00', created_at: '2026-09-22T01:00:00Z', }] : [] }, }); return { ok: false, error: { message: 'Unexpected fixture request: ' + method + ' ' + path } }; }); }); await page.goto(applicationUrl, { waitUntil: 'domcontentloaded' }); await page.getByTestId('ai-module-option-programming').click(); await expect(page.getByTestId('main-layout')).toBeVisible(); await expect(page.getByTestId('sidebar-member-menu-trigger')).toContainText('词元点数用户'); await page.getByTestId('sidebar-member-menu-trigger').click(); await expect(page.getByTestId('sidebar-total-token-points')).toHaveText('0 点'); await expect(page.getByText('点数已用尽,充值后可继续使用。')).toBeVisible(); await page.getByTestId('sidebar-point-wallet-open').click(); const wallet = page.getByTestId('point-wallet-dialog'); await expect(wallet).toContainText('1 元充值 50 点'); await expect(wallet).not.toContainText('重置卡'); await wallet.getByRole('button', { name: /500 点\s*¥10.00/ }).click(); await expect(wallet.getByAltText('充值支付二维码')).toBeVisible(); await expect(page.getByTestId('point-recharge-checkout')).toContainText('¥10.00 · 500 点'); await expect(wallet.getByRole('button', { name: /1,000 点\s*¥20.00/ })).toHaveCount(0); await page.screenshot({ path: testInfo.outputPath('pending-permanent-points.png') }); // Reloading the Renderer must recover the existing server order using GET only. await page.reload({ waitUntil: 'domcontentloaded' }); await page.getByTestId('sidebar-member-menu-trigger').click(); await page.getByTestId('sidebar-point-wallet-open').click(); await wallet.getByRole('button', { name: '查看原订单' }).click(); await expect(wallet.getByAltText('充值支付二维码')).toBeVisible(); await app.evaluate(() => { (globalThis as FixtureGlobal).__pointWalletE2E.paid = true; }); await wallet.getByRole('button', { name: '刷新此订单' }).click(); await expect(page.getByTestId('point-recharge-checkout')).toContainText('已到账'); await expect(wallet.getByAltText('充值支付二维码')).toHaveCount(0); await expect(wallet).toContainText('500 点'); await wallet.getByRole('tab', { name: '收支记录' }).click(); await expect(wallet).toContainText('+500 点'); await expect(wallet).toContainText('已入账'); await page.screenshot({ path: testInfo.outputPath('credited-permanent-points.png') }); expect(await app.evaluate(() => (globalThis as FixtureGlobal).__pointWalletE2E.requests.filter( (request) => request === 'POST /api/works/billing/recharge/orders', ).length)).toBe(1); } catch (error) { const page = app.windows().at(-1); if (page && !page.isClosed()) { await page.screenshot({ path: testInfo.outputPath('wallet-failure.png') }); await testInfo.attach('wallet-failure-dom', { body: await page.locator('body').innerText(), contentType: 'text/plain' }); } await testInfo.attach('wallet-fixture-requests', { body: JSON.stringify(await app.evaluate(() => (globalThis as FixtureGlobal).__pointWalletE2E.requests)), contentType: 'application/json', }); throw error; } finally { await closeElectronApp(app); } }); });