Fix clipboard fallback selection inside dialogs (#433)

This commit is contained in:
sganggs committed 2026-10-09 17:11:03 +08:00
1 parent e738b6ce33
commit 8a8db0eb94
5 files changed
+73 -21

No files matched your search

+2 -1
View File
@@ -380,8 +380,9 @@ describe('room: copy the invite without navigator.clipboard (LAN over http, iOS)
const ta = {
style: {}, value: '',
setAttribute(k) { calls.push(['attr', k]); },
focus() { document.activeElement = ta; },
select() { calls.push(['select']); },
setSelectionRange(a, b) { calls.push(['range', a, b]); },
setSelectionRange(a, b) { this.selectionStart = a; this.selectionEnd = b; calls.push(['range', a, b]); },
remove() { calls.push(['remove']); },
};
const saved = { document: globalThis.document, secure: globalThis.isSecureContext, nav: Object.getOwnPropertyDescriptor(globalThis, 'navigator') };
+38
View File
@@ -90,6 +90,44 @@ describe('Modal keyboard focus', { skip: !ENABLED && 'set SP_E2E=1 and have Chro
} finally { await page.close(); }
});
test('clipboard fallback stays in the active dialog, copies its payload, restores focus and cleans up on failure', async () => {
const page = await browser.newPage();
try {
await fixture(page);
await page.click('#open');
await page.waitForFunction(() => document.activeElement.id === 'first');
for (const nested of [false, true]) {
if (nested) {
await page.click('#inner-open');
await page.waitForFunction(() => document.activeElement.id === 'inner-first');
}
const result = await page.evaluate(async () => {
const { copyText } = await import('/js/ui/clipboard.js');
Object.defineProperty(window, 'isSecureContext', { configurable: true, value: false });
const prev = document.activeElement;
const count = document.querySelectorAll('textarea').length;
let copied;
const original = document.execCommand;
document.execCommand = (command) => {
const el = document.activeElement;
if (command !== 'copy') throw new Error('unexpected command');
copied = el.value.slice(el.selectionStart, el.selectionEnd);
return true;
};
const ok = await copyText('导出设置:\n{"language":"zh"}');
const restored = document.activeElement === prev;
document.execCommand = () => { throw new Error('denied'); };
const denied = await copyText('denied');
document.execCommand = original;
return { ok, copied, restored, denied, cleaned: document.querySelectorAll('textarea').length === count,
stillFocused: document.activeElement === prev };
});
assert.deepEqual(result, { ok: true, copied: '导出设置:\n{"language":"zh"}', restored: true,
denied: false, cleaned: true, stillFocused: true });
}
} finally { await page.close(); }
});
test('no controls: focus the dialog itself; imperative confirm retains autofocus and native Enter', async () => {
const page = await browser.newPage();
try {