Merge pull request #18331 from timvandermeij/integration-test-copy-pasting

Refactor the copy/paste logic in the integration tests and fix a race condition involving the `waitForEvent` integration test helper function
This commit is contained in:
Tim van der Meij 2024-06-26 16:20:57 +02:00 committed by GitHub
commit 6d579081c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 92 additions and 99 deletions

View File

@ -15,7 +15,7 @@
import { import {
closePages, closePages,
kbCopy, copy,
kbSelectAll, kbSelectAll,
loadAndWait, loadAndWait,
mockClipboard, mockClipboard,
@ -23,9 +23,11 @@ import {
} from "./test_utils.mjs"; } from "./test_utils.mjs";
const selectAll = async page => { const selectAll = async page => {
const promise = waitForEvent(page, "selectionchange"); await waitForEvent({
await kbSelectAll(page); page,
await promise; eventName: "selectionchange",
action: () => kbSelectAll(page),
});
await page.waitForFunction(() => { await page.waitForFunction(() => {
const selection = document.getSelection(); const selection = document.getSelection();
@ -55,10 +57,7 @@ describe("Copy and paste", () => {
); );
await selectAll(page); await selectAll(page);
const promise = waitForEvent(page, "copy"); await copy(page);
await kbCopy(page);
await promise;
await page.waitForFunction( await page.waitForFunction(
`document.querySelector('#viewerContainer').style.cursor !== "wait"` `document.querySelector('#viewerContainer').style.cursor !== "wait"`
); );
@ -159,10 +158,7 @@ describe("Copy and paste", () => {
); );
await selectAll(page); await selectAll(page);
const promise = waitForEvent(page, "copy"); await copy(page);
await kbCopy(page);
await promise;
await page.waitForFunction( await page.waitForFunction(
`document.querySelector('#viewerContainer').style.cursor !== "wait"` `document.querySelector('#viewerContainer').style.cursor !== "wait"`
); );

View File

@ -16,6 +16,8 @@
import { import {
awaitPromise, awaitPromise,
closePages, closePages,
copy,
copyToClipboard,
createPromise, createPromise,
dragAndDropAnnotation, dragAndDropAnnotation,
firstPageOnTop, firstPageOnTop,
@ -30,21 +32,19 @@ import {
kbBigMoveLeft, kbBigMoveLeft,
kbBigMoveRight, kbBigMoveRight,
kbBigMoveUp, kbBigMoveUp,
kbCopy,
kbGoToBegin, kbGoToBegin,
kbGoToEnd, kbGoToEnd,
kbModifierDown, kbModifierDown,
kbModifierUp, kbModifierUp,
kbPaste,
kbRedo, kbRedo,
kbSelectAll, kbSelectAll,
kbUndo, kbUndo,
loadAndWait, loadAndWait,
paste,
pasteFromClipboard, pasteFromClipboard,
scrollIntoView, scrollIntoView,
switchToEditor, switchToEditor,
waitForAnnotationEditorLayer, waitForAnnotationEditorLayer,
waitForEvent,
waitForSelectedEditor, waitForSelectedEditor,
waitForSerialized, waitForSerialized,
waitForStorageEntries, waitForStorageEntries,
@ -53,16 +53,6 @@ import {
} from "./test_utils.mjs"; } from "./test_utils.mjs";
import { PNG } from "pngjs"; import { PNG } from "pngjs";
const copyPaste = async page => {
let promise = waitForEvent(page, "copy");
await kbCopy(page);
await promise;
promise = waitForEvent(page, "paste");
await kbPaste(page);
await promise;
};
const selectAll = async page => { const selectAll = async page => {
await kbSelectAll(page); await kbSelectAll(page);
await page.waitForFunction( await page.waitForFunction(
@ -187,7 +177,8 @@ describe("FreeText Editor", () => {
); );
await waitForSelectedEditor(page, getEditorSelector(0)); await waitForSelectedEditor(page, getEditorSelector(0));
await copyPaste(page); await copy(page);
await paste(page);
await page.waitForSelector(getEditorSelector(1), { await page.waitForSelector(getEditorSelector(1), {
visible: true, visible: true,
}); });
@ -203,7 +194,8 @@ describe("FreeText Editor", () => {
expect(pastedContent).withContext(`In ${browserName}`).toEqual(content); expect(pastedContent).withContext(`In ${browserName}`).toEqual(content);
await copyPaste(page); await copy(page);
await paste(page);
await page.waitForSelector(getEditorSelector(2), { await page.waitForSelector(getEditorSelector(2), {
visible: true, visible: true,
}); });
@ -263,7 +255,8 @@ describe("FreeText Editor", () => {
); );
await waitForSelectedEditor(page, getEditorSelector(3)); await waitForSelectedEditor(page, getEditorSelector(3));
await copyPaste(page); await copy(page);
await paste(page);
await page.waitForSelector(getEditorSelector(4), { await page.waitForSelector(getEditorSelector(4), {
visible: true, visible: true,
}); });
@ -276,9 +269,7 @@ describe("FreeText Editor", () => {
); );
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
const promise = waitForEvent(page, "paste"); await paste(page);
await kbPaste(page);
await promise;
await page.waitForSelector(getEditorSelector(5 + i)); await page.waitForSelector(getEditorSelector(5 + i));
} }
@ -597,7 +588,8 @@ describe("FreeText Editor", () => {
.withContext(`In ${browserName}`) .withContext(`In ${browserName}`)
.toEqual([0, 1, 3]); .toEqual([0, 1, 3]);
await copyPaste(page); await copy(page);
await paste(page);
await page.waitForSelector(getEditorSelector(6), { await page.waitForSelector(getEditorSelector(6), {
visible: true, visible: true,
}); });
@ -1275,7 +1267,8 @@ describe("FreeText Editor", () => {
); );
await waitForSelectedEditor(page, getEditorSelector(1)); await waitForSelectedEditor(page, getEditorSelector(1));
await copyPaste(page); await copy(page);
await paste(page);
await page.waitForSelector(getEditorSelector(6), { await page.waitForSelector(getEditorSelector(6), {
visible: true, visible: true,
}); });
@ -3425,14 +3418,11 @@ describe("FreeText Editor", () => {
); );
await select(0); await select(0);
await pasteFromClipboard( await copyToClipboard(page, {
page, "text/html": "<b>Bold Foo</b>",
{ "text/plain": "Foo",
"text/html": "<b>Bold Foo</b>", });
"text/plain": "Foo", await pasteFromClipboard(page, `${editorSelector} .internal`);
},
`${editorSelector} .internal`
);
let lastText = data; let lastText = data;
@ -3442,14 +3432,11 @@ describe("FreeText Editor", () => {
expect(text).withContext(`In ${browserName}`).toEqual(lastText); expect(text).withContext(`In ${browserName}`).toEqual(lastText);
await select(3); await select(3);
await pasteFromClipboard( await copyToClipboard(page, {
page, "text/html": "<b>Bold Bar</b><br><b>Oof</b>",
{ "text/plain": "Bar\nOof",
"text/html": "<b>Bold Bar</b><br><b>Oof</b>", });
"text/plain": "Bar\nOof", await pasteFromClipboard(page, `${editorSelector} .internal`);
},
`${editorSelector} .internal`
);
await waitForTextChange(lastText, editorSelector); await waitForTextChange(lastText, editorSelector);
text = await getText(editorSelector); text = await getText(editorSelector);
@ -3457,13 +3444,8 @@ describe("FreeText Editor", () => {
expect(text).withContext(`In ${browserName}`).toEqual(lastText); expect(text).withContext(`In ${browserName}`).toEqual(lastText);
await select(0); await select(0);
await pasteFromClipboard( await copyToClipboard(page, { "text/html": "<b>basic html</b>" });
page, await pasteFromClipboard(page, `${editorSelector} .internal`);
{
"text/html": "<b>basic html</b>",
},
`${editorSelector} .internal`
);
// Nothing should change, so it's hard to wait on something. // Nothing should change, so it's hard to wait on something.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
@ -3477,15 +3459,12 @@ describe("FreeText Editor", () => {
const prevHTML = await getHTML(); const prevHTML = await getHTML();
// Try to paste an image. // Try to paste an image.
await pasteFromClipboard( await copyToClipboard(page, {
page, "image/png":
{ // 1x1 transparent png.
"image/png": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
// 1x1 transparent png. });
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==", await pasteFromClipboard(page, `${editorSelector} .internal`);
},
`${editorSelector} .internal`
);
// Nothing should change, so it's hard to wait on something. // Nothing should change, so it's hard to wait on something.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
@ -3505,14 +3484,11 @@ describe("FreeText Editor", () => {
}); });
const fooBar = "Foo\nBar\nOof"; const fooBar = "Foo\nBar\nOof";
await pasteFromClipboard( await copyToClipboard(page, {
page, "text/html": "<b>html</b>",
{ "text/plain": fooBar,
"text/html": "<b>html</b>", });
"text/plain": fooBar, await pasteFromClipboard(page, `${editorSelector} .internal`);
},
`${editorSelector} .internal`
);
await waitForTextChange("", editorSelector); await waitForTextChange("", editorSelector);
text = await getText(editorSelector); text = await getText(editorSelector);

View File

@ -17,6 +17,8 @@ import {
applyFunctionToEditor, applyFunctionToEditor,
awaitPromise, awaitPromise,
closePages, closePages,
copy,
copyToClipboard,
getEditorDimensions, getEditorDimensions,
getEditorSelector, getEditorSelector,
getFirstSerialized, getFirstSerialized,
@ -24,11 +26,10 @@ import {
getSerialized, getSerialized,
kbBigMoveDown, kbBigMoveDown,
kbBigMoveRight, kbBigMoveRight,
kbCopy,
kbPaste,
kbSelectAll, kbSelectAll,
kbUndo, kbUndo,
loadAndWait, loadAndWait,
paste,
pasteFromClipboard, pasteFromClipboard,
scrollIntoView, scrollIntoView,
serializeBitmapDimensions, serializeBitmapDimensions,
@ -78,12 +79,10 @@ const copyImage = async (page, imagePath, number) => {
const data = fs const data = fs
.readFileSync(path.join(__dirname, imagePath)) .readFileSync(path.join(__dirname, imagePath))
.toString("base64"); .toString("base64");
await pasteFromClipboard(
page, await copyToClipboard(page, { "image/png": `data:image/png;base64,${data}` });
{ "image/png": `data:image/png;base64,${data}` }, await pasteFromClipboard(page);
"",
500
);
await waitForImage(page, getEditorSelector(number)); await waitForImage(page, getEditorSelector(number));
}; };
@ -570,13 +569,13 @@ describe("Stamp Editor", () => {
await page1.click("#editorStamp"); await page1.click("#editorStamp");
await copyImage(page1, "../images/firefox_logo.png", 0); await copyImage(page1, "../images/firefox_logo.png", 0);
await kbCopy(page1); await copy(page1);
const [, page2] = pages2[i]; const [, page2] = pages2[i];
await page2.bringToFront(); await page2.bringToFront();
await page2.click("#editorStamp"); await page2.click("#editorStamp");
await kbPaste(page2); await paste(page2);
await waitForImage(page2, getEditorSelector(0)); await waitForImage(page2, getEditorSelector(0));
} }
@ -831,8 +830,8 @@ describe("Stamp Editor", () => {
); );
await page.waitForSelector(`${getEditorSelector(0)} .altText.done`); await page.waitForSelector(`${getEditorSelector(0)} .altText.done`);
await kbCopy(page); await copy(page);
await kbPaste(page); await paste(page);
await page.waitForSelector(`${getEditorSelector(1)} .altText.done`); await page.waitForSelector(`${getEditorSelector(1)} .altText.done`);
await waitForSerialized(page, 2); await waitForSerialized(page, 2);

View File

@ -186,13 +186,14 @@ async function getSpanRectFromText(page, pageNumber, text) {
); );
} }
async function waitForEvent( async function waitForEvent({
page, page,
eventName, eventName,
action,
selector = null, selector = null,
validator = null, validator = null,
timeout = 5000 timeout = 5000,
) { }) {
const handle = await page.evaluateHandle( const handle = await page.evaluateHandle(
(name, sel, validate, timeOut) => { (name, sel, validate, timeOut) => {
let callback = null, let callback = null,
@ -227,13 +228,15 @@ async function waitForEvent(
validator ? validator.toString() : null, validator ? validator.toString() : null,
timeout timeout
); );
await action();
const success = await awaitPromise(handle); const success = await awaitPromise(handle);
if (success === null) { if (success === null) {
console.log(`waitForEvent: ${eventName} didn't trigger within the timeout`); console.log(`waitForEvent: ${eventName} didn't trigger within the timeout`);
} else if (!success) { } else if (!success) {
console.log(`waitForEvent: ${eventName} triggered, but validation failed`); console.log(`waitForEvent: ${eventName} triggered, but validation failed`);
} }
return success;
} }
async function waitForStorageEntries(page, nEntries) { async function waitForStorageEntries(page, nEntries) {
@ -292,7 +295,15 @@ async function mockClipboard(pages) {
); );
} }
async function pasteFromClipboard(page, data, selector, timeout = 100) { async function copy(page) {
await waitForEvent({
page,
eventName: "copy",
action: () => kbCopy(page),
});
}
async function copyToClipboard(page, data) {
await page.evaluate(async dat => { await page.evaluate(async dat => {
const items = Object.create(null); const items = Object.create(null);
for (const [type, value] of Object.entries(dat)) { for (const [type, value] of Object.entries(dat)) {
@ -305,15 +316,25 @@ async function pasteFromClipboard(page, data, selector, timeout = 100) {
} }
await navigator.clipboard.write([new ClipboardItem(items)]); await navigator.clipboard.write([new ClipboardItem(items)]);
}, data); }, data);
}
async function paste(page) {
await waitForEvent({
page,
eventName: "paste",
action: () => kbPaste(page),
});
}
async function pasteFromClipboard(page, selector = null) {
const validator = e => e.clipboardData.items.length !== 0; const validator = e => e.clipboardData.items.length !== 0;
let hasPasteEvent = false; await waitForEvent({
while (!hasPasteEvent) { page,
// We retry to paste if nothing has been pasted before the timeout. eventName: "paste",
const promise = waitForEvent(page, "paste", selector, validator); action: () => kbPaste(page),
await kbPaste(page); selector,
hasPasteEvent = await promise; validator,
} });
} }
async function getSerialized(page, filter = undefined) { async function getSerialized(page, filter = undefined) {
@ -634,6 +655,8 @@ export {
clearInput, clearInput,
closePages, closePages,
closeSinglePage, closeSinglePage,
copy,
copyToClipboard,
createPromise, createPromise,
dragAndDropAnnotation, dragAndDropAnnotation,
firstPageOnTop, firstPageOnTop,
@ -654,7 +677,6 @@ export {
kbBigMoveLeft, kbBigMoveLeft,
kbBigMoveRight, kbBigMoveRight,
kbBigMoveUp, kbBigMoveUp,
kbCopy,
kbDeleteLastWord, kbDeleteLastWord,
kbFocusNext, kbFocusNext,
kbFocusPrevious, kbFocusPrevious,
@ -662,12 +684,12 @@ export {
kbGoToEnd, kbGoToEnd,
kbModifierDown, kbModifierDown,
kbModifierUp, kbModifierUp,
kbPaste,
kbRedo, kbRedo,
kbSelectAll, kbSelectAll,
kbUndo, kbUndo,
loadAndWait, loadAndWait,
mockClipboard, mockClipboard,
paste,
pasteFromClipboard, pasteFromClipboard,
scrollIntoView, scrollIntoView,
serializeBitmapDimensions, serializeBitmapDimensions,