// "Links to X pages" check: the real click router (src/inject/linkRouting.ts) in Chromium over a
// hand-built page shaped like X's markup (timeline cells, posts with a permalink, a quote, media,
// action buttons, a hover card, the nav rail). Per click it asserts whether X's own navigation was
// diverted to a new-window request (window.open, stubbed) and with which URL. Not a live X test.
//
// node scripts/link-routing-check.mjs
import { build } from 'esbuild';
import { fileURLToPath } from 'node:url';
import { chromium } from 'playwright';
const root = fileURLToPath(new URL('../', import.meta.url));
const bundle = (await build({
stdin: { contents: "export { installLinkRouting } from './src/inject/linkRouting.ts';", resolveDir: root, loader: 'ts' },
bundle: true, format: 'iife', globalName: 'BTLinks', write: false, target: 'chrome120',
})).outputFiles[0].text;
const PAGE = `
`;
const failures = [];
const check = (label, ok, detail = '') => {
console.log(`${ok ? 'ok ' : 'FAIL'} ${label}${detail ? ` (${detail})` : ''}`);
if (!ok) failures.push(label);
};
const browser = await chromium.launch();
const page = await browser.newPage();
await page.route('https://x.com/**', route => route.fulfill({ contentType: 'text/html', body: PAGE }));
await page.goto('https://x.com/home');
await page.addScriptTag({ content: bundle });
await page.evaluate(() => { window.routing = BTLinks.installLinkRouting(window); });
const click = async (selector, mode, options = {}) => {
await page.evaluate(mode => { window.opened = []; window.inPlace = []; window.routing.configure({ x: mode }); }, mode);
await page.click(selector, { force: true, ...options });
return page.evaluate(() => ({ opened: window.opened.map(entry => entry.url), inPlace: window.inPlace }));
};
const DIVERTED = [
['#author', 'https://x.com/playfishies'],
['#permalink', 'https://x.com/playfishies/status/111'],
['#text', 'https://x.com/playfishies/status/111'],
['#mention', 'https://x.com/CRTkafa'],
['#hashtag', 'https://x.com/hashtag/tauri'],
['#quote-text', 'https://x.com/CRTkafa/status/222'],
['#hover-name', 'https://x.com/playfishies'],
['#hover-following', 'https://x.com/playfishies/following'],
];
const IN_PLACE = ['#nav-home', '#nav-profile', '#tab', '#photo', '#like', '#reply', '#focal-text', '#usercell-link', '#external'];
for (const mode of ['pane', 'tab', 'browser']) {
for (const [selector, url] of DIVERTED) {
const result = await click(selector, mode);
check(`${mode}: ${selector} opens ${url}`, result.opened.length === 1 && result.opened[0] === url && result.inPlace.length === 0, JSON.stringify(result));
}
for (const selector of IN_PLACE) {
const result = await click(selector, mode);
check(`${mode}: ${selector} left to X`, result.opened.length === 0, JSON.stringify(result));
}
}
for (const [selector] of DIVERTED) {
const result = await click(selector, 'same');
check(`same: ${selector} left to X`, result.opened.length === 0 && result.inPlace.length === 1, JSON.stringify(result));
}
const modified = await click('#permalink', 'pane', { modifiers: ['Control'] });
check('Ctrl+click keeps X\'s own new-tab behaviour', modified.opened.length === 0, JSON.stringify(modified));
await page.evaluate(() => { const range = document.createRange(); range.selectNodeContents(document.getElementById('text')); getSelection().removeAllRanges(); getSelection().addRange(range); });
const selecting = await page.evaluate(() => { window.opened = []; window.routing.configure({ x: 'pane' }); document.getElementById('text').dispatchEvent(new MouseEvent('click', { bubbles: true, button: 0 })); return window.opened.length; });
check('a click that ends a text selection is not a navigation', selecting === 0);
await browser.close();
console.log(failures.length ? `\n${failures.length} failed` : '\nall passed');
process.exit(failures.length ? 1 : 0);