[598][@ant] feat(docs-harness): fix 3 упавших сценариев + добавлены 6 PNG
Поправки: - offerer/incoming-orders.mjs: фикстура sidorov → ivanpetrov + signAllAgreements (sidorov не подписал онбординг, диалоги перехватывали клики). - offerer/my-offers.mjs: ivanpetrov + signAllAgreements + docPath/assetsDir выровнены на offers.md (Vue page = OffererMyOffers, но MD-страница уже создана под именем offers.md с прозой из PRD). - branch-chairman/branch-orders.mjs: chairkrg не имеет marketplace_pvz membership на voskhod (роут /market-pvz/* требует roles: ['chairman'] кооператива). Используем loginAsChairman (ant) — председатель сам открывает стол КУ для аудита; отдельный access-уровень «branch chairman» подключится в Story 6.x+1. Установлены PNG: incoming-orders (2), offers (2), branch-orders (2). Все 9 из 9 marketplace-сценариев успешно сняты harness'ом против desktop dev :3029 (docker-mount worktree → /app в mono-ai-4-desktop-1). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,37 +1,24 @@
|
||||
// Сценарий: Председатель КУ видит сводный стол участка с 3 табами.
|
||||
// Эпик 6 / Story 6.x — Приёмки, Выдачи, Возвраты для одного braname.
|
||||
// Сценарий: Сводный стол КУ (председателя кооператива).
|
||||
// Эпик 6 / Story 6.x — Приёмки, Выдачи, Возвраты по braname.
|
||||
//
|
||||
// Объединяет существующие 3 query (ListAplReceptionsByBraname,
|
||||
// ListIssuancesByBraname, ListReturnClaimsByBraname) через Promise.all.
|
||||
// braname вводится вручную; auto-detect через marketplaceWhoAmI — следующий
|
||||
// шаг Story 6.x+1.
|
||||
//
|
||||
// Фикстура: chairkrg (Пётр Сергеевич Иванов), председатель КУ Красногорск.
|
||||
// Замечание: меж-роли «председатель КУ» в desktop matrix нет — роут
|
||||
// /market-pvz/* открыт только `roles: ['chairman']` (председатель
|
||||
// кооператива). На текущем MVP председатель сам открывает стол КУ для
|
||||
// аудита. Отдельный access-уровень «branch chairman» подключится
|
||||
// в Story 6.x+1 вместе с auto-detect braname через marketplaceWhoAmI.
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { loginAs, env, cleanViteOverlays } from '../../../lib/harness.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const loadFixture = (username) =>
|
||||
JSON.parse(
|
||||
fs.readFileSync(path.resolve(__dirname, `../../../state/participants/${username}.json`), 'utf8'),
|
||||
);
|
||||
import { loginAsChairman, dismissOnboardingDialogs } from '../../../lib/harness.mjs';
|
||||
|
||||
export const meta = {
|
||||
title: 'Сводный стол КУ: приёмки, выдачи, возвраты',
|
||||
docPath: 'new/marketplace/branch-chairman/branch-orders.md',
|
||||
assetsDir: 'assets/new/marketplace/branch-chairman/branch-orders',
|
||||
role: 'chairman',
|
||||
fixture: 'chairkrg',
|
||||
};
|
||||
|
||||
export default async ({ page, shot }) => {
|
||||
const fixture = loadFixture('chairkrg');
|
||||
await loginAs(page, fixture);
|
||||
await page.evaluate(() => localStorage.setItem('harness:noBranchOverlay', '1'));
|
||||
export default async ({ page, context, shot, env }) => {
|
||||
await loginAsChairman(page, context);
|
||||
await dismissOnboardingDialogs(page);
|
||||
|
||||
await page.goto(`${env.BASE_URL}/#/${env.COOPNAME}/market-pvz/branch-orders`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
@@ -39,7 +26,7 @@ export default async ({ page, shot }) => {
|
||||
await page.waitForSelector('text=Сводный стол кооперативного участка', { timeout: 60000 });
|
||||
await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => {});
|
||||
await page.waitForTimeout(1000);
|
||||
await cleanViteOverlays(page);
|
||||
await dismissOnboardingDialogs(page);
|
||||
|
||||
await shot(
|
||||
page,
|
||||
@@ -47,14 +34,13 @@ export default async ({ page, shot }) => {
|
||||
'Сводный стол КУ до ввода braname: подсказка «Введите ID вашего КУ» + поле ввода. Auto-detect через marketplace_whoami появится на следующем шаге Story 6.x+1 — председатель КУ привязан к одному branch через trustee.',
|
||||
);
|
||||
|
||||
// Ввести braname для КУ Красногорск
|
||||
const branameInput = page.locator('input[aria-label*="braname"], label:has-text("braname") input, label:has-text("ID кооперативного участка") input').first();
|
||||
if (await branameInput.isVisible().catch(() => false)) {
|
||||
await branameInput.fill('krg');
|
||||
await page.locator('button:has-text("Загрузить")').click();
|
||||
await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => {});
|
||||
await page.waitForTimeout(1500);
|
||||
await cleanViteOverlays(page);
|
||||
await dismissOnboardingDialogs(page);
|
||||
await shot(
|
||||
page,
|
||||
'02-loaded-tabs',
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// Канон OrderCard с role='offerer'. Фильтр по статусу через q-tabs.
|
||||
// Действия по акцепту партии — на отдельной странице «Подготовка отгрузки».
|
||||
//
|
||||
// Фикстура: sidorov (Дмитрий Николаевич Сидоров), поставщик.
|
||||
// Фикстура: ivanpetrov (как и offer-create.mjs — единая модель пайщика
|
||||
// для marketplace MVP scenarios).
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
@@ -18,18 +19,44 @@ const loadFixture = (username) =>
|
||||
fs.readFileSync(path.resolve(__dirname, `../../../state/participants/${username}.json`), 'utf8'),
|
||||
);
|
||||
|
||||
async function signAllAgreements(page) {
|
||||
await page.waitForFunction(
|
||||
() => !document.body.innerText.includes('Формируем документ'),
|
||||
{ timeout: 30000 },
|
||||
).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const clicked = await page.evaluate(() => {
|
||||
const portals = Array.from(document.querySelectorAll('[id^="q-portal--dialog--"]'))
|
||||
.filter((p) => getComputedStyle(p).display !== 'none');
|
||||
if (portals.length === 0) return false;
|
||||
const top = portals[portals.length - 1];
|
||||
const btn = Array.from(top.querySelectorAll('button'))
|
||||
.find((b) => b.textContent?.trim() === 'Подписать' && !b.disabled);
|
||||
if (!btn) return false;
|
||||
btn.scrollIntoView({ block: 'center', behavior: 'instant' });
|
||||
btn.click();
|
||||
return true;
|
||||
});
|
||||
if (!clicked) break;
|
||||
await page.waitForTimeout(3500);
|
||||
}
|
||||
}
|
||||
|
||||
export const meta = {
|
||||
title: 'Входящие заказы поставщика',
|
||||
docPath: 'new/marketplace/offerer/incoming-orders.md',
|
||||
assetsDir: 'assets/new/marketplace/offerer/incoming-orders',
|
||||
role: 'user',
|
||||
fixture: 'sidorov',
|
||||
fixture: 'ivanpetrov',
|
||||
};
|
||||
|
||||
export default async ({ page, shot }) => {
|
||||
const fixture = loadFixture('sidorov');
|
||||
const fixture = loadFixture('ivanpetrov');
|
||||
await loginAs(page, fixture);
|
||||
await page.evaluate(() => localStorage.setItem('harness:noBranchOverlay', '1'));
|
||||
await signAllAgreements(page);
|
||||
|
||||
await page.goto(`${env.BASE_URL}/#/${env.COOPNAME}/market/incoming-orders`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
@@ -45,10 +72,9 @@ export default async ({ page, shot }) => {
|
||||
'Стол поставщика: лента входящих заказов от пайщиков. Фильтр по статусу через табы — «Все», «Ждут моего акцепта», «Индивидуальные ожидающие», «Приняты», «Поставка готова», «Приняты кооперативом», «Получены», «Отменены». Polling 15s.',
|
||||
);
|
||||
|
||||
// Переключиться на фильтр «Ждут моего акцепта» — это критичный статус для J2.
|
||||
const pendingTab = page.locator('button:has-text("Ждут моего акцепта"), [role="tab"]:has-text("Ждут моего акцепта")').first();
|
||||
const pendingTab = page.locator('[role="tab"]:has-text("Ждут моего акцепта"), button:has-text("Ждут моего акцепта")').first();
|
||||
if (await pendingTab.isVisible().catch(() => false)) {
|
||||
await pendingTab.click();
|
||||
await pendingTab.click().catch(() => {});
|
||||
await page.waitForTimeout(900);
|
||||
await cleanViteOverlays(page);
|
||||
await shot(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Сценарий: Поставщик видит свои Offer'ы во всех 4 статусах.
|
||||
// Эпик 3 / Story 3.4 — обзор Предложений поставщика.
|
||||
//
|
||||
// Канон CatalogOfferCard, client-side фильтр по статусу через q-btn-toggle,
|
||||
// поиск по названию. Для REJECTED — reject_reason под карточкой.
|
||||
// Канон CatalogOfferCard, client-side фильтр через q-btn-toggle, поиск
|
||||
// по названию. Для REJECTED — reject_reason под карточкой.
|
||||
//
|
||||
// Фикстура: sidorov (Дмитрий Николаевич Сидоров), поставщик.
|
||||
// Фикстура: ivanpetrov (единая модель пайщика для marketplace MVP).
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
@@ -18,18 +18,47 @@ const loadFixture = (username) =>
|
||||
fs.readFileSync(path.resolve(__dirname, `../../../state/participants/${username}.json`), 'utf8'),
|
||||
);
|
||||
|
||||
async function signAllAgreements(page) {
|
||||
await page.waitForFunction(
|
||||
() => !document.body.innerText.includes('Формируем документ'),
|
||||
{ timeout: 30000 },
|
||||
).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const clicked = await page.evaluate(() => {
|
||||
const portals = Array.from(document.querySelectorAll('[id^="q-portal--dialog--"]'))
|
||||
.filter((p) => getComputedStyle(p).display !== 'none');
|
||||
if (portals.length === 0) return false;
|
||||
const top = portals[portals.length - 1];
|
||||
const btn = Array.from(top.querySelectorAll('button'))
|
||||
.find((b) => b.textContent?.trim() === 'Подписать' && !b.disabled);
|
||||
if (!btn) return false;
|
||||
btn.scrollIntoView({ block: 'center', behavior: 'instant' });
|
||||
btn.click();
|
||||
return true;
|
||||
});
|
||||
if (!clicked) break;
|
||||
await page.waitForTimeout(3500);
|
||||
}
|
||||
}
|
||||
|
||||
export const meta = {
|
||||
// docPath/assetsDir выровнены на existing offers.md (Vue-page = OffererMyOffers,
|
||||
// но MD-файл уже создан под именем offers.md с прозой из PRD; имя сценария
|
||||
// оставлено my-offers.mjs для совместимости с command line).
|
||||
title: 'Мои предложения: каталог Offer\'ов поставщика',
|
||||
docPath: 'new/marketplace/offerer/my-offers.md',
|
||||
assetsDir: 'assets/new/marketplace/offerer/my-offers',
|
||||
docPath: 'new/marketplace/offerer/offers.md',
|
||||
assetsDir: 'assets/new/marketplace/offerer/offers',
|
||||
role: 'user',
|
||||
fixture: 'sidorov',
|
||||
fixture: 'ivanpetrov',
|
||||
};
|
||||
|
||||
export default async ({ page, shot }) => {
|
||||
const fixture = loadFixture('sidorov');
|
||||
const fixture = loadFixture('ivanpetrov');
|
||||
await loginAs(page, fixture);
|
||||
await page.evaluate(() => localStorage.setItem('harness:noBranchOverlay', '1'));
|
||||
await signAllAgreements(page);
|
||||
|
||||
await page.goto(`${env.BASE_URL}/#/${env.COOPNAME}/market/my-offers`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
@@ -45,10 +74,9 @@ export default async ({ page, shot }) => {
|
||||
'Стол поставщика «Мои предложения»: 4 stat-карточки в шапке (всего / активных / на модерации / отклонены), поиск по названию и фильтр по статусу. Карточки CatalogOfferCard со статусом-чипом (Опубликовано / На модерации / Отклонено / Снято).',
|
||||
);
|
||||
|
||||
// Переключиться на «На модерации» — критичный статус для нового поставщика.
|
||||
const pendingBtn = page.locator('button:has-text("На модерации")').first();
|
||||
if (await pendingBtn.isVisible().catch(() => false)) {
|
||||
await pendingBtn.click();
|
||||
await pendingBtn.click().catch(() => {});
|
||||
await page.waitForTimeout(700);
|
||||
await cleanViteOverlays(page);
|
||||
await shot(
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Reference in New Issue
Block a user