morphit/apps/web/scripts/order-detail-expired-ui-smoke.ts

131 lines
5.4 KiB
TypeScript

/**
* order-detail-expired-ui-smoke (cp438)
*
* The order-detail page (`[permlink]`) trusted the order's STORED status,
* which the indexer keeps at 'live' until a sweep (expiry is enforced at query
* time via expires_at ≤ now — see orderExpiry.ts). So an order the public
* orderbook had already dropped still rendered on its own URL with a green
* "Live" pill, a broken "Expires in Expiring now" pill, and a "Cancel this
* order" button — none of which make sense once expired.
*
* This pins the fix: the page reads EFFECTIVE status (via the shared
* orderExpiry helpers + a live nowMs ticker), so an expired order shows the
* "Expired" pill, hides the expires-in countdown, and offers Re-list instead
* of Cancel. The Re-list prefill is built by the SHARED builder that
* /my/orders also uses — the two must not drift.
*
* Static source scan (the page pulls $-aliases the bare runner can't resolve).
*/
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const webRoot = join(here, '..');
const detail = readFileSync(
join(webRoot, 'src/routes/[lang]/[x+40][account=account]/[permlink=permlink]/+page.svelte'),
'utf8'
);
const myOrders = readFileSync(
join(webRoot, 'src/routes/[lang]/my/orders/+page.svelte'),
'utf8'
);
const relistMod = readFileSync(join(webRoot, 'src/lib/orders/relist.ts'), 'utf8');
const enLocale = JSON.parse(readFileSync(join(webRoot, 'src/lib/i18n/locales/en.json'), 'utf8'));
let passed = 0;
let failed = 0;
function check(label: string, cond: boolean): void {
if (cond) {
passed++;
console.log(` \u2713 ${label}`);
} else {
failed++;
console.log(` \u2717 ${label}`);
}
}
console.log('\norder-detail-expired-ui smoke:\n');
// ─── the detail page reasons about EFFECTIVE status, not stored status ──────
check(
'detail page imports the shared expiry helpers (isOrderExpired/isOrderLive)',
/import\s*\{[^}]*isOrderExpired[^}]*isOrderLive[^}]*\}\s*from\s*'\$lib\/orders\/orderExpiry'/.test(
detail
)
);
check(
'detail page has a live nowMs ticker (so status flips at the expiry moment)',
/let nowMs = \$state\(Date\.now\(\)\)/.test(detail) &&
/setInterval\(\s*\(\)\s*=>\s*\{\s*nowMs = Date\.now\(\)/.test(detail)
);
check(
'the status pill reads effectiveStatus(order), not the raw stored status',
/statusChipClasses\(effectiveStatus\(order\)\)/.test(detail) &&
/statusLabel\(effectiveStatus\(order\)\)/.test(detail) &&
/function effectiveStatus\(o: OrderRecord\)/.test(detail) &&
/isOrderExpired\(o, nowMs\) \? 'expired' : o\.status/.test(detail)
);
check(
'the "expires in" pill is gated on isOrderLive (hidden once expired — kills "Expires in Expiring now")',
/\{#if isOrderLive\(order, nowMs\) && order\.expires_at\}/.test(detail)
);
check(
'no raw `order.status === \'live\'` gates remain (all use the effective helpers)',
!/order\.status === 'live'/.test(detail)
);
// ─── owner actions: Cancel while live, Re-list once expired OR cancelled ─────
check(
'owner-actions card shows for live OR expired OR cancelled',
/\{#if isOwner && \(isOrderLive\(order, nowMs\) \|\| isOrderExpired\(order, nowMs\) \|\| order\.status === 'cancelled'\)\}/.test(
detail
)
);
check(
'expired-or-cancelled branch offers Re-list (action_relist), not Cancel',
/\{#if isOrderExpired\(order, nowMs\) \|\| order\.status === 'cancelled'\}[\s\S]*?action_relist[\s\S]*?\{:else if withinEditWindow/.test(
detail
)
);
check(
'Re-list uses the shared prefill builder + key (no inline duplication)',
/import\s*\{[^}]*buildRelistPrefill[^}]*RELIST_PREFILL_KEY[^}]*\}\s*from\s*'\$lib\/orders\/relist'/.test(
detail
) && /safeSession\.set\(RELIST_PREFILL_KEY, JSON\.stringify\(buildRelistPrefill\(order\)\)\)/.test(detail)
);
// ─── /my/orders re-lists via the SAME shared builder (no drift) ─────────────
check(
'/my/orders re-lists via the shared buildRelistPrefill (extracted, not inline)',
/import\s*\{[^}]*buildRelistPrefill[^}]*\}\s*from\s*'\$lib\/orders\/relist'/.test(myOrders) &&
/buildRelistPrefill\(o\)/.test(myOrders) &&
// the old ~50-line inline price_model mapping is gone from /my/orders
!/if \(obj\.kind === 'spread' && typeof obj\.percent === 'number'\)/.test(myOrders)
);
// ─── the shared module is well-formed ───────────────────────────────────────
check(
'relist module exports buildRelistPrefill + RELIST_PREFILL_KEY',
/export function buildRelistPrefill\(o: OrderRecord\): RelistPrefill/.test(relistMod) &&
/export const RELIST_PREFILL_KEY = 'morphit\.post\.prefill'/.test(relistMod)
);
check(
'relist prefill defaults a FRESH 30-day expiry + reason:relist (never re-signs the old order)',
/expiresDays: 30/.test(relistMod) && /reason: 'relist'/.test(relistMod)
);
// ─── the reused i18n key exists in every locale ─────────────────────────────
check(
'en carries my_orders.order.action_relist (reused for the detail-page Re-list)',
typeof enLocale?.my_orders?.order?.action_relist === 'string' &&
enLocale.my_orders.order.action_relist.length > 0
);
console.log('');
if (failed === 0) {
console.log(`\u2713 all ${passed} order-detail-expired-ui scenarios passed`);
} else {
console.log(`\u2717 ${failed} failed, ${passed} passed`);
process.exit(1);
}