fix(init-wallet): URL-fallback на coopname + harness scenario chairman dashboard
- init-wallet: info.coopname может быть undefined из-за race между login и
loadSystemInfo. Когда login завершается быстрее, init-wallet вызывает
loadUserWallet({coopname: undefined}) → GraphQL 400 → catch → reload →
фронт никогда не редиректит после login. URL-fallback извлекает coopname
из window.location.hash/pathname как safety net.
- harness scenarios/marketplace/chairman/dashboard-overview.mjs: обзорный
сценарий по 5 marketplace URL-ам (/marketplace, /marketplace/catalog,
/marketplace/orders, /chairman, /chairman/approval-requests). Без
waitForURL — фронт сейчас не делает redirect после login.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,17 @@ function walletInitTimeoutPromise(): Promise<never> {
|
||||
});
|
||||
}
|
||||
|
||||
// URL-based fallback на случай если info.coopname ещё не подгрузился
|
||||
// из getSystemInfo (race с login: init-wallet стартует до того как
|
||||
// useSystemStore.loadSystemInfo() заполнил info из бэка).
|
||||
function getCoopnameFromUrl(): string {
|
||||
if (typeof window === 'undefined') return '';
|
||||
const src = window.location.hash || window.location.pathname || '';
|
||||
// hash вида "#/voskhod/..." или path "/voskhod/..."
|
||||
const m = src.replace(/^#/, '').match(/^\/?([a-z0-9_-]+)\b/i);
|
||||
return m ? m[1] : '';
|
||||
}
|
||||
|
||||
export function useInitWalletProcess() {
|
||||
const session = useSessionStore();
|
||||
const wallet = useWalletStore();
|
||||
@@ -60,8 +71,12 @@ export function useInitWalletProcess() {
|
||||
// вернёт пустой кошелёк, а UI начнёт показывать подписи документов,
|
||||
// которых юзер на этом этапе подписывать не должен.
|
||||
if (session.isFullyActive) {
|
||||
// info.coopname может быть undefined если getSystemInfo ещё не
|
||||
// отдала ответ (race с login). URL-fallback гарантирует, что мы
|
||||
// не вызовем loadUserWallet({coopname: undefined}) → GraphQL 400.
|
||||
const coopname = info.coopname || getCoopnameFromUrl();
|
||||
await wallet.loadUserWallet({
|
||||
coopname: info.coopname,
|
||||
coopname,
|
||||
username: session.username,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Сценарий: Стол председателя кооператива → обзорная экскурсия по marketplace URLs.
|
||||
// Делает login как chairman (`ant`/`ivanov@example.com`), затем напрямую
|
||||
// goto к каждому marketplace-маршруту и снимает что вышло.
|
||||
//
|
||||
// На текущем стенде session.loadComplete зависает на 5с (init-wallet падает),
|
||||
// поэтому waitForURL после клика «Войти» бесполезен — используем waitForTimeout
|
||||
// и goto. После клика «Войти» token успевает сохраниться в IndexedDB, что
|
||||
// позволяет последующим goto'ам захватить authenticated layout (хедер).
|
||||
|
||||
import { cleanViteOverlays, env } from '../../../lib/harness.mjs';
|
||||
|
||||
export const meta = {
|
||||
title: 'Стол председателя ПК: экскурсия по marketplace',
|
||||
docPath: 'new/marketplace/chairman/dashboard.md',
|
||||
assetsDir: 'assets/new/marketplace/chairman/dashboard',
|
||||
role: 'chairman',
|
||||
};
|
||||
|
||||
const ROUTES = [
|
||||
{ name: '01-marketplace-main', path: '/marketplace', caption: 'Главная marketplace' },
|
||||
{ name: '02-marketplace-catalog', path: '/marketplace/catalog', caption: 'Каталог витрины' },
|
||||
{ name: '03-marketplace-orders', path: '/marketplace/orders', caption: 'Мои заказы' },
|
||||
{ name: '04-chairman', path: '/chairman', caption: 'Стол председателя' },
|
||||
{ name: '05-chairman-approvals', path: '/chairman/approval-requests', caption: 'Запросы одобрений' },
|
||||
];
|
||||
|
||||
export default async ({ page, shot, context }) => {
|
||||
// --- Login (без ожидания редиректа — фронт не редиректит на этом стенде)
|
||||
await page.goto(`${env.BASE_URL}/${env.COOPNAME}/auth/signin`, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
||||
await page.waitForSelector('button:has-text("Войти")', { timeout: 60000 });
|
||||
await cleanViteOverlays(page);
|
||||
await page.locator('label:has-text("электронную почту")').locator('input').fill(env.CHAIRMAN_EMAIL);
|
||||
await page.locator('label:has-text("ключ доступа")').locator('input').fill(env.CHAIRMAN_WIF);
|
||||
await cleanViteOverlays(page);
|
||||
await page.locator('button:has-text("Войти")').click();
|
||||
|
||||
// Подождать сохранения токенов в IndexedDB (login API + globalStore.setTokens)
|
||||
await page.waitForTimeout(4000);
|
||||
await shot(page, '00-after-login', 'Состояние сразу после клика «Войти» (фронт ещё не редиректит автоматически — баг с реактивностью info.coopname).');
|
||||
|
||||
// --- Обзорная экскурсия по marketplace маршрутам
|
||||
for (const route of ROUTES) {
|
||||
const url = `${env.BASE_URL}/${env.COOPNAME}${route.path}`;
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||
await page.waitForTimeout(5000);
|
||||
await cleanViteOverlays(page);
|
||||
await shot(page, route.name, `${route.caption}: ${url.replace(env.BASE_URL, '')}`);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user