coulomb-social/scripts/test-copy-content.mjs

78 lines
3.6 KiB
JavaScript
Raw Normal View History

// Browser acceptance with synthetic content and intercepted clipboard writes.
import assert from 'node:assert/strict';
import { fileURLToPath } from 'node:url';
import { chromium } from 'playwright';
const browser = await chromium.launch({headless: true});
try {
const page = await browser.newPage();
const script = fileURLToPath(new URL('../coulomb_social/apps/spaces/static/spaces/copy-content.js', import.meta.url));
const fixture = `<nav>PRIVATE APP CHROME</nav>
<button id="copy-page-content">Copy content to clipboard</button>
<p id="copy-content-status" role="status"></p>
<textarea id="copy-content-manual" hidden></textarea>
<div id="page-content"><h1>Weekly call</h1><ul><li>Open point<ul><li>Nested follow-up</li></ul></li><li>Completed point</li></ul><h2>Protocol</h2><p>New notes with <strong>emphasis</strong>.</p></div>`;
// A secure origin enables the browser's native ClipboardItem implementation.
await page.route('https://clipboard.test/', route => route.fulfill({
contentType: 'text/html', body: fixture,
}));
await page.goto('https://clipboard.test/');
async function setup(mode) {
await page.setContent(fixture);
await page.evaluate(mode => {
window.receipts = [];
window.clipboardMode = mode;
Object.defineProperty(navigator, 'clipboard', {configurable: true, value: {
write: async items => {
if (window.clipboardMode !== 'rich') throw new Error('Rich clipboard denied');
const item = items[0];
window.receipts.push({html: await (await item.getType('text/html')).text(),
text: await (await item.getType('text/plain')).text()});
},
writeText: async text => {
if (window.clipboardMode === 'denied') throw new Error('Clipboard denied');
window.receipts.push({text});
},
}});
}, mode);
await page.addScriptTag({path: script});
}
for (const mode of ['rich', 'plain', 'denied']) {
await setup(mode);
await page.locator('#copy-page-content').click();
await page.waitForFunction(() => !document.getElementById('copy-page-content').disabled);
const result = await page.evaluate(() => ({
receipts: window.receipts,
status: document.getElementById('copy-content-status').textContent,
manual: document.getElementById('copy-content-manual').value,
hidden: document.getElementById('copy-content-manual').hidden,
selected: document.getElementById('copy-content-manual').selectionEnd,
}));
const text = mode === 'denied' ? result.manual : result.receipts[0].text;
assert.match(text, /Weekly call/);
assert.match(text, /- Open point/);
assert.match(text, / - Nested follow-up/);
assert.match(text, /- Completed point/);
assert.match(text, /New notes with emphasis/);
assert.doesNotMatch(text, /PRIVATE APP CHROME|Copy content to clipboard/);
if (mode === 'denied') {
assert.equal(result.receipts.length, 0);
assert.equal(result.hidden, false);
assert.equal(result.selected, result.manual.length);
assert.match(result.status, /Ctrl\+C or Cmd\+C/);
assert.doesNotMatch(result.status, /Content copied/);
} else {
assert.equal(result.receipts.length, 1);
assert.match(result.status, /Content copied/);
if (mode === 'rich') {
assert.match(result.receipts[0].html, /<ul><li>Open point/);
assert.match(result.receipts[0].html, /<strong>emphasis<\/strong>/);
assert.doesNotMatch(result.receipts[0].html, /PRIVATE APP CHROME/);
}
}
}
console.log('Clipboard browser checks passed: rich HTML, plain text, blocked/manual fallback.');
} finally {
await browser.close();
}