|
|
/**
|
|
|
* One-shot Playwright probe to verify the Toast dismiss flow emits
|
|
|
* `data-event*` perceptual signals on the item element.
|
|
|
*
|
|
|
* Usage:
|
|
|
* 1. `npm run dev` in another terminal (defaults to port 5173)
|
|
|
* 2. `node --import tsx/esm scripts/probe-toast-dismiss.ts`
|
|
|
*
|
|
|
* Output: list of mutations seen during the dismiss interaction. If the
|
|
|
* runtime + semantic engine are wired correctly, you should see at minimum:
|
|
|
* - data-event="dismiss" added
|
|
|
* - data-event-id="sig-N" added
|
|
|
* - data-event-phase="active" added
|
|
|
* - data-event-family="emerge" added
|
|
|
* …followed shortly after by their removal as the cleanup tick runs.
|
|
|
*/
|
|
|
import { chromium } from 'playwright';
|
|
|
|
|
|
const URL = process.env.PROBE_URL ?? 'http://localhost:5173/uix/components/toast';
|
|
|
|
|
|
async function main() {
|
|
|
const browser = await chromium.launch();
|
|
|
const page = await browser.newPage();
|
|
|
|
|
|
const consoleLines: string[] = [];
|
|
|
page.on('console', (msg) => consoleLines.push(`[${msg.type()}] ${msg.text()}`));
|
|
|
|
|
|
await page.goto(URL, { waitUntil: 'networkidle' });
|
|
|
|
|
|
// 1. Trigger a default toast (the demo page has an "Add default" button).
|
|
|
const triggerBtn = page.getByRole('button', { name: /^default$/i }).first();
|
|
|
await triggerBtn.click();
|
|
|
|
|
|
// 2. Wait for the item to mount.
|
|
|
const item = page.locator('[data-toast-item]').first();
|
|
|
await item.waitFor({ state: 'attached', timeout: 5000 });
|
|
|
|
|
|
// 3. Install a MutationObserver in the page context that watches the item.
|
|
|
await page.evaluate(() => {
|
|
|
const item = document.querySelector('[data-toast-item]') as HTMLElement | null;
|
|
|
if (!item) {
|
|
|
(window as unknown as Record<string, unknown>).__probeResult = {
|
|
|
error: 'item not found'
|
|
|
};
|
|
|
return;
|
|
|
}
|
|
|
const captured: Array<{
|
|
|
time: number;
|
|
|
attr: string;
|
|
|
oldValue: string | null;
|
|
|
newValue: string | null;
|
|
|
}> = [];
|
|
|
const start = performance.now();
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
|
for (const m of mutations) {
|
|
|
if (m.type !== 'attributes' || !m.attributeName) continue;
|
|
|
captured.push({
|
|
|
time: Math.round(performance.now() - start),
|
|
|
attr: m.attributeName,
|
|
|
oldValue: m.oldValue,
|
|
|
newValue: (m.target as HTMLElement).getAttribute(m.attributeName)
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
observer.observe(item, { attributes: true, attributeOldValue: true });
|
|
|
(window as unknown as Record<string, unknown>).__probeStop = () => {
|
|
|
observer.disconnect();
|
|
|
return captured;
|
|
|
};
|
|
|
});
|
|
|
|
|
|
// 4. Click the close button (× inside the toast item).
|
|
|
const closeBtn = item.locator('[data-toast-close]').first();
|
|
|
await closeBtn.click();
|
|
|
|
|
|
// 5. Wait long enough for the dismiss + cleanup to complete (~5 frames).
|
|
|
await page.waitForTimeout(200);
|
|
|
|
|
|
// 6. Pull the captured mutations out.
|
|
|
const result = (await page.evaluate(() => {
|
|
|
const stop = (window as unknown as { __probeStop?: () => unknown; __probeResult?: unknown })
|
|
|
.__probeStop;
|
|
|
const errResult = (window as unknown as { __probeResult?: unknown }).__probeResult;
|
|
|
if (errResult) return errResult;
|
|
|
return stop ? stop() : { error: 'observer not installed' };
|
|
|
})) as Array<{ time: number; attr: string; oldValue: string | null; newValue: string | null }>;
|
|
|
|
|
|
console.log('\n=== Console messages ===');
|
|
|
for (const line of consoleLines) console.log(line);
|
|
|
|
|
|
console.log('\n=== Mutations on the toast item during dismiss ===');
|
|
|
if (Array.isArray(result) && result.length === 0) {
|
|
|
console.log('(none)');
|
|
|
} else if (Array.isArray(result)) {
|
|
|
for (const m of result) {
|
|
|
console.log(
|
|
|
`+${m.time}ms ${m.attr}: ${JSON.stringify(m.oldValue)} → ${JSON.stringify(m.newValue)}`
|
|
|
);
|
|
|
}
|
|
|
const sawSignal = result.some((m) => m.attr === 'data-event' && m.newValue === 'dismiss');
|
|
|
console.log(`\nSignal observed: ${sawSignal ? '✅ YES' : '❌ NO'}`);
|
|
|
} else {
|
|
|
console.log('error result:', result);
|
|
|
}
|
|
|
|
|
|
await browser.close();
|
|
|
}
|
|
|
|
|
|
main().catch((err) => {
|
|
|
console.error(err);
|
|
|
process.exit(1);
|
|
|
});
|