4aa6174968
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m39s
Add the "Кошелёк" section to the settings hub: context-visible chip balances, active benefits (no-ads term/forever, hints) and a storefront of chip-priced values and money-priced chip packs. Guests have no wallet; the Google Play build hides the money purchases behind a RuStore stub; a web purchase that would draw VK/Telegram chips warns first. Add the catalog read path the storefront needs — a context-projected GET /api/v1/user/wallet/catalog (payments service + store, gateway op wallet.catalog, FBS Catalog/CatalogProduct/CatalogAtom, client decode) — plus the client leg for the existing wallet.get/buy ops. Value spends reuse the existing spend path; the chip-pack purchase (money order flow) arrives with payment intake, so its action is a disabled placeholder for now. Covered by Go unit (catalog projection) + integration (/wallet/catalog over Postgres), vitest (formatting, spendable selection, web-spend warning, GP flag, codec + gateway encode round-trips) and Playwright mock e2e (render, guest-hidden, GP stub, warning) on Chromium + WebKit.
100 lines
4.6 KiB
TypeScript
100 lines
4.6 KiB
TypeScript
import { expect, test, type Page } from './fixtures';
|
|
|
|
// The Wallet section against the mock transport (no backend). The mock seeds a web/native (direct)
|
|
// account holding a direct + vk chip segment and a small catalog (two chip-priced values and one
|
|
// rouble-priced chip pack — see lib/mock/client.ts), so the storefront, the store-compliance
|
|
// warning and the Google Play stub are all exercisable without a backend.
|
|
|
|
async function loginLobby(page: Page): Promise<void> {
|
|
await page.goto('/');
|
|
await page.getByRole('button', { name: /guest/i }).click();
|
|
await expect(page.getByText('Your turn')).toBeVisible();
|
|
}
|
|
|
|
async function openSettings(page: Page): Promise<void> {
|
|
await page.getByRole('button', { name: /Settings/ }).click(); // lobby ⚙️ tab
|
|
await expect(page.locator('.pane')).toHaveCount(1); // let the slide settle
|
|
}
|
|
|
|
async function openWallet(page: Page): Promise<void> {
|
|
await openSettings(page);
|
|
await page.getByRole('button', { name: 'Wallet', exact: true }).click();
|
|
await expect(page.getByTestId('wallet')).toBeVisible();
|
|
}
|
|
|
|
test('wallet: balances, benefits and the storefront render for a durable account', async ({ page }) => {
|
|
await loginLobby(page);
|
|
await openWallet(page);
|
|
|
|
// Balances: the seeded direct ("Web") and vk segments.
|
|
await expect(page.getByRole('heading', { name: 'Balance' })).toBeVisible();
|
|
await expect(page.getByText('Web', { exact: true })).toBeVisible();
|
|
await expect(page.getByText('VK', { exact: true })).toBeVisible();
|
|
|
|
// Benefits + the storefront: two values priced in chips, one pack priced in roubles.
|
|
await expect(page.getByRole('heading', { name: 'Benefits' })).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Store' })).toBeVisible();
|
|
await expect(page.getByTestId('product')).toHaveCount(3);
|
|
const pack = page.locator('[data-kind="pack"]');
|
|
await expect(pack).toContainText('₽');
|
|
// The pack purchase (money intake) is not wired yet, so its action is the disabled 'Soon'.
|
|
await expect(pack.getByRole('button', { name: 'Soon' })).toBeDisabled();
|
|
});
|
|
|
|
test('wallet: the tab sits between Friends and About', async ({ page }) => {
|
|
await loginLobby(page);
|
|
await openSettings(page);
|
|
const labels = await page.locator('.tab .lbl').allInnerTexts();
|
|
const iFriends = labels.indexOf('Friends');
|
|
const iWallet = labels.indexOf('Wallet');
|
|
const iAbout = labels.indexOf('Info');
|
|
expect(iFriends).toBeGreaterThanOrEqual(0);
|
|
expect(iWallet).toBe(iFriends + 1);
|
|
expect(iAbout).toBe(iWallet + 1);
|
|
});
|
|
|
|
test('wallet: a guest has no wallet (nor friends) tab', async ({ page }) => {
|
|
await page.goto('/?guest');
|
|
await page.getByRole('button', { name: /guest/i }).click();
|
|
await expect(page.getByText('Your turn')).toBeVisible();
|
|
await openSettings(page);
|
|
await expect(page.getByRole('button', { name: 'Wallet', exact: true })).toBeHidden();
|
|
await expect(page.getByRole('button', { name: 'Friends', exact: true })).toBeHidden();
|
|
});
|
|
|
|
test('wallet: the Google Play build hides the money purchases behind the RuStore stub', async ({ page }) => {
|
|
await page.goto('/?gp');
|
|
await page.getByRole('button', { name: /guest/i }).click();
|
|
await expect(page.getByText('Your turn')).toBeVisible();
|
|
await openWallet(page);
|
|
|
|
// The chip pack is gone; the stub points at the RuStore build. Spending earned chips still works.
|
|
await expect(page.getByTestId('gp-stub')).toBeVisible();
|
|
await expect(page.locator('[data-kind="pack"]')).toHaveCount(0);
|
|
await expect(page.locator('[data-kind="value"]').first().getByTestId('buy')).toBeVisible();
|
|
});
|
|
|
|
test('wallet: a web spend that would draw store chips warns first, then completes', async ({ page }) => {
|
|
await loginLobby(page);
|
|
await openWallet(page);
|
|
|
|
// The 300-chip value drains direct (120) then reaches into vk (180) → a store segment → warn.
|
|
await page.locator('[data-pid="val-noads-30"]').getByTestId('buy').click();
|
|
await expect(page.getByTestId('warn')).toBeVisible();
|
|
await page.getByTestId('warn-confirm').click();
|
|
await expect(page.getByTestId('warn')).toBeHidden();
|
|
// The spend applied: the no-ads benefit now shows an end date.
|
|
await expect(page.getByTestId('wallet')).toContainText('No ads until');
|
|
});
|
|
|
|
test('wallet: a spend the direct segment covers alone does not warn', async ({ page }) => {
|
|
await loginLobby(page);
|
|
await openWallet(page);
|
|
|
|
// The 100-chip value is covered by the direct segment (120) alone → no store draw, no warning.
|
|
await page.locator('[data-pid="val-hints-50"]').getByTestId('buy').click();
|
|
await expect(page.getByTestId('warn')).toBeHidden();
|
|
// The hints benefit rose from 5 to 55.
|
|
await expect(page.getByTestId('wallet')).toContainText('Hints 55');
|
|
});
|