Files
makelore/tests/e2e/permanent-point-wallet.spec.ts
T

131 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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('shows the child recipient, reopens its frozen unpaid order, 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: 'child-wallet', userId: 'child-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.getByTestId('point-recharge-recipient')).toHaveText('充值到:小麦(child-wallet)');
await expect(wallet).toContainText('请由家长登录孩子的账号完成付款');
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);
}
});
});