test: make inquiry/observation Flow A visible (UI-driven with API fallback)
All checks were successful
Build and Push Docker Image / build (push) Successful in 28s
All checks were successful
Build and Push Docker Image / build (push) Successful in 28s
Flow A now drives the real screens - detail page, Send-to modal, dept-response page, Accept button - so the run is watchable in headed mode. Each visible step falls back to a direct POST if the UI doesn't persist, so the flow still completes reliably. Flows B/C unchanged.
This commit is contained in:
parent
23b6e239b5
commit
8c75baf30a
@ -28,6 +28,7 @@ interface KindCfg {
|
|||||||
label: string;
|
label: string;
|
||||||
module: string;
|
module: string;
|
||||||
seedKind: string;
|
seedKind: string;
|
||||||
|
detail: (id: string) => string;
|
||||||
sendTo: (id: string) => string;
|
sendTo: (id: string) => string;
|
||||||
sendToken: (id: string) => string;
|
sendToken: (id: string) => string;
|
||||||
deptResponse: (id: string) => string;
|
deptResponse: (id: string) => string;
|
||||||
@ -44,6 +45,7 @@ interface KindCfg {
|
|||||||
const KINDS: KindCfg[] = [
|
const KINDS: KindCfg[] = [
|
||||||
{
|
{
|
||||||
label: 'Inquiry', module: 'InquiryDeptResponse', seedKind: 'inquiry',
|
label: 'Inquiry', module: 'InquiryDeptResponse', seedKind: 'inquiry',
|
||||||
|
detail: (id) => `/inquiries/${id}/`,
|
||||||
sendTo: (id) => `/inquiries/${id}/send-to/`,
|
sendTo: (id) => `/inquiries/${id}/send-to/`,
|
||||||
sendToken: (id) => `/inquiries/${id}/transfer-to-department/`,
|
sendToken: (id) => `/inquiries/${id}/transfer-to-department/`,
|
||||||
deptResponse: (id) => `/inquiries/${id}/department-response/`,
|
deptResponse: (id) => `/inquiries/${id}/department-response/`,
|
||||||
@ -56,6 +58,7 @@ const KINDS: KindCfg[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Observation', module: 'ObservationDeptResponse', seedKind: 'observation',
|
label: 'Observation', module: 'ObservationDeptResponse', seedKind: 'observation',
|
||||||
|
detail: (id) => `/observations/${id}`,
|
||||||
sendTo: (id) => `/observations/${id}/send-to/`,
|
sendTo: (id) => `/observations/${id}/send-to/`,
|
||||||
sendToken: (id) => `/observations/${id}/send-to-department/`,
|
sendToken: (id) => `/observations/${id}/send-to-department/`,
|
||||||
deptResponse: (id) => `/observations/${id}/department-response/`,
|
deptResponse: (id) => `/observations/${id}/department-response/`,
|
||||||
@ -139,21 +142,72 @@ async function flowA(page: Page, K: KindCfg) {
|
|||||||
const s = seed(K.seedKind);
|
const s = seed(K.seedKind);
|
||||||
itemId = s.itemId;
|
itemId = s.itemId;
|
||||||
observe(M, 'A-seed', 'INFO', `${K.label} ${itemId}`, { role: PXT });
|
observe(M, 'A-seed', 'INFO', `${K.label} ${itemId}`, { role: PXT });
|
||||||
// 1. PX send (AJAX)
|
|
||||||
|
// Helper: try the visible UI path; if it doesn't achieve the expected state, fall back to a direct POST.
|
||||||
|
const ensureState = async (want: Record<string, string>, step: string, role: string, fallback?: () => Promise<void>) => {
|
||||||
|
const before = state(K.seedKind, itemId);
|
||||||
|
const okBefore = Object.entries(want).every(([k, v]) => (v === 'NOT_NONE' ? (before[k] && before[k] !== 'NONE') : before[k] === v));
|
||||||
|
if (!okBefore && fallback) { await fallback(); }
|
||||||
|
assertSt(M, K.seedKind, itemId, want, step, role);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. PX send - VISIBLE: detail page + Send-to modal
|
||||||
await login(page, PXT, M); await ensureAuth(page, PXT, M);
|
await login(page, PXT, M); await ensureAuth(page, PXT, M);
|
||||||
await postObs(page, M, `${BASE_URL}${K.sendTo(itemId)}`, { recipient_type: 'department', department_id: s.deptId, contact_person_id: s.champStaffId, note: 'E2E send' }, 'A-send', PXT);
|
await page.goto(`${BASE_URL}${K.detail(itemId)}`);
|
||||||
assertSt(M, K.seedKind, itemId, { [K.sentField]: K.sentWant }, 'A-send-state', PXT);
|
await page.waitForLoadState('domcontentloaded');
|
||||||
// 2. champion responds (logged-in)
|
await page.waitForTimeout(700);
|
||||||
|
await page.evaluate((args) => {
|
||||||
|
const fn = (window as any).showSendModal;
|
||||||
|
if (typeof fn === 'function') { try { fn(args.id, args.kind); } catch { /* ignore */ } }
|
||||||
|
const m = document.getElementById('sendToModal');
|
||||||
|
if (m) m.classList.remove('hidden');
|
||||||
|
}, { id: itemId, kind: K.seedKind });
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.locator('input[name="recipient_type"][value="department"]').first().check({ force: true }).catch(() => {});
|
||||||
|
await page.locator('select[name="department_id"]').first().selectOption({ value: s.deptId }).catch(() => {});
|
||||||
|
await page.waitForTimeout(1200);
|
||||||
|
await page.locator('select[name="contact_person_id"]').first().selectOption({ value: s.champStaffId }).catch(() => {});
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
await page.locator('#sendToModal button[type="submit"], #sendToModal button:not([type="button"])').first().click({ force: true }).catch(() => {});
|
||||||
|
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
|
||||||
|
await page.waitForTimeout(1200);
|
||||||
|
await ensureState({ [K.sentField]: K.sentWant }, 'A-send-state', PXT,
|
||||||
|
async () => { await postObs(page, M, `${BASE_URL}${K.sendTo(itemId)}`, { recipient_type: 'department', department_id: s.deptId, contact_person_id: s.champStaffId, note: 'E2E send' }, 'A-send-fallback', PXT); });
|
||||||
|
|
||||||
|
// 2. Champion responds - VISIBLE: dept-response page
|
||||||
await login(page, CHAMP, M); await ensureAuth(page, CHAMP, M);
|
await login(page, CHAMP, M); await ensureAuth(page, CHAMP, M);
|
||||||
await postObs(page, M, `${BASE_URL}${K.deptResponse(itemId)}`, { response_en: `E2E ${K.label} champion response (A)` }, 'A-champion-response', CHAMP);
|
await page.goto(`${BASE_URL}${K.deptResponse(itemId)}`);
|
||||||
assertSt(M, K.seedKind, itemId, { department_response_en_set: 'True', dept_response_acceptance_status: 'pending' }, 'A-champion-state', CHAMP);
|
await page.waitForLoadState('domcontentloaded');
|
||||||
// 3. PX accepts
|
await page.waitForTimeout(500);
|
||||||
|
const tb = await bodyHasTraceback(page);
|
||||||
|
if (!tb) {
|
||||||
|
const ta = page.locator('textarea[name="response_en"]').first();
|
||||||
|
if (await ta.count()) {
|
||||||
|
await ta.fill(`E2E ${K.label} champion response (A)`);
|
||||||
|
// submit the response form specifically
|
||||||
|
await page.locator('form[action*="department-response"] button[type="submit"], textarea[name="response_en"] ~ * button[type="submit"], button[type="submit"]').first().click({ force: true }).catch(() => {});
|
||||||
|
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await ensureState({ department_response_en_set: 'True', dept_response_acceptance_status: 'pending' }, 'A-champion-state', CHAMP,
|
||||||
|
async () => { await postObs(page, M, `${BASE_URL}${K.deptResponse(itemId)}`, { response_en: `E2E ${K.label} champion response (A)` }, 'A-champion-fallback', CHAMP); });
|
||||||
|
|
||||||
|
// 3. PX accepts - VISIBLE: click Accept on the detail page
|
||||||
await login(page, PXT, M); await ensureAuth(page, PXT, M);
|
await login(page, PXT, M); await ensureAuth(page, PXT, M);
|
||||||
await postObs(page, M, `${BASE_URL}${K.review(itemId)}`, { acceptance_status: 'acceptable' }, 'A-px-accept', PXT);
|
await page.goto(`${BASE_URL}${K.detail(itemId)}`);
|
||||||
assertSt(M, K.seedKind, itemId, { dept_response_acceptance_status: 'acceptable' }, 'A-accept-state', PXT);
|
await page.waitForLoadState('domcontentloaded');
|
||||||
// 4. resolve (inquiry needs a PX response first). Best-effort: some status
|
await page.waitForTimeout(600);
|
||||||
// machines (observation) can't jump straight to resolved; the dept-response
|
const acceptForm = page.locator('form').filter({ has: page.locator('input[name="acceptance_status"][value="acceptable"]') }).first();
|
||||||
// flow itself is already proven by the acceptance assertion above.
|
if (await acceptForm.count()) {
|
||||||
|
await acceptForm.locator('button[type="submit"]').first().click({ force: true }).catch(() => {});
|
||||||
|
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
}
|
||||||
|
await ensureState({ dept_response_acceptance_status: 'acceptable' }, 'A-accept-state', PXT,
|
||||||
|
async () => { await postObs(page, M, `${BASE_URL}${K.review(itemId)}`, { acceptance_status: 'acceptable' }, 'A-accept-fallback', PXT); });
|
||||||
|
|
||||||
|
// 4. Resolve (best-effort; some status machines can't jump to resolved)
|
||||||
if (K.pxRespond) await postObs(page, M, `${BASE_URL}${K.pxRespond(itemId)}`, { response_en: `E2E ${K.label} PX response` }, 'A-px-respond', PXT);
|
if (K.pxRespond) await postObs(page, M, `${BASE_URL}${K.pxRespond(itemId)}`, { response_en: `E2E ${K.label} PX response` }, 'A-px-respond', PXT);
|
||||||
await postForm(page, `${BASE_URL}${K.activate(itemId)}`, {});
|
await postForm(page, `${BASE_URL}${K.activate(itemId)}`, {});
|
||||||
await postObs(page, M, `${BASE_URL}${K.changeStatus(itemId)}`, { status: 'resolved', note: 'E2E resolved' }, 'A-resolve', PXT);
|
await postObs(page, M, `${BASE_URL}${K.changeStatus(itemId)}`, { status: 'resolved', note: 'E2E resolved' }, 'A-resolve', PXT);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user