mirror of
https://codeberg.org/forgejo/forgejo
synced 2025-10-19 00:40:51 +02:00
Flaky e2e test failure: ``` 1) [Mobile Chrome] › tests/e2e/issue-comment-dropzone.test.e2e.ts:74:1 › Re-add images to dropzone on edit Error: expect(locator).toHaveCount(expected) failed Locator: locator('.dropzone').locator('.dz-preview') Expected: 1 Received: 0 Timeout: 3000ms Call log: - Expect "toHaveCount" with timeout 3000ms - waiting for locator('.dropzone').locator('.dz-preview') 7 × locator resolved to 0 elements - unexpected value "0" 87 | await expect(dropzone.locator('.files').first()).toHaveCount(1); 88 | const preview = dropzone.locator('.dz-preview'); > 89 | await expect(preview).toHaveCount(1); | ^ 90 | await expect(preview.locator('.dz-filename')).toHaveText('foo.png'); 91 | await expect(preview.locator('.octicon-copy')).toBeVisible(); 92 | await assertCopy(page, workerInfo, '; at /workspace/forgejo/forgejo/tests/e2e/issue-comment-dropzone.test.e2e.ts:89:25 ``` Observed on chromium and Mobile Chrome. I haven't been able to reproduce this test failure in local testing, but in examining the playwright test artifacts I noted that the browser is getting a `404 Not Found` error attempting to load a URL `(test server url)/uploading...` (where `...` is literally present in the URL). My theory is that the test is firing the paste event in `pasteImage` and not waiting for the XMLHttpRequest to complete the upload, and then saving the issue comment with the placeholder URL `uploading...` in the Markdown, causing a later failure. This patch adds two `waitForResponse` calls -- one when pasting the image to wait for the upload to complete, and one which is probably redundant which waits for the `/attachments` GET while editing the comment. If this test continues to be flaky, it may at least have a different error revealing more about its cause. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9660 Reviewed-by: Otto <otto@codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
107 lines
4 KiB
TypeScript
107 lines
4 KiB
TypeScript
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// @watch start
|
|
// web_src/js/features/common-global.js
|
|
// web_src/js/features/comp/Paste.js
|
|
// web_src/js/features/repo-issue.js
|
|
// web_src/js/features/repo-legacy.js
|
|
// @watch end
|
|
|
|
import {expect, type Locator, type Page, type TestInfo} from '@playwright/test';
|
|
import {test, dynamic_id} from './utils_e2e.ts';
|
|
import {screenshot} from './shared/screenshots.ts';
|
|
|
|
test.use({user: 'user2'});
|
|
|
|
async function pasteImage(el: Locator) {
|
|
await el.evaluate(async (el) => {
|
|
const base64 = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAHklEQVQoU2MUk1P7z0AGYBzViDvURgMHT4oaQoEDAFuJEu2fuGfhAAAAAElFTkSuQmCC`;
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
const response = await fetch(base64);
|
|
const blob = await response.blob();
|
|
|
|
el.focus();
|
|
|
|
let pasteEvent = new Event('paste', {bubbles: true, cancelable: true});
|
|
pasteEvent = Object.assign(pasteEvent, {
|
|
clipboardData: {
|
|
items: [
|
|
{
|
|
kind: 'file',
|
|
type: 'image/png',
|
|
getAsFile() {
|
|
return new File([blob], 'foo.png', {type: blob.type});
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
el.dispatchEvent(pasteEvent);
|
|
});
|
|
}
|
|
|
|
async function assertCopy(page: Page, workerInfo: TestInfo, startWith: string) {
|
|
const project = workerInfo.project.name;
|
|
if (project === 'webkit' || project === 'Mobile Safari') return;
|
|
|
|
const dropzone = page.locator('.dropzone');
|
|
const preview = dropzone.locator('.dz-preview');
|
|
const copyLink = preview.locator('.octicon-copy').locator('..');
|
|
await copyLink.click();
|
|
|
|
const clipboardContent = await page.evaluate(() => navigator.clipboard.readText());
|
|
expect(clipboardContent).toContain(startWith);
|
|
}
|
|
|
|
test('Paste image in new comment', async ({page}, workerInfo) => {
|
|
await page.goto('/user2/repo1/issues/new');
|
|
|
|
const waitForAttachmentUpload = page.waitForResponse((response) => {
|
|
return response.request().method() === 'POST' && response.url().endsWith('/attachments');
|
|
});
|
|
await pasteImage(page.locator('.markdown-text-editor'));
|
|
await waitForAttachmentUpload;
|
|
|
|
const dropzone = page.locator('.dropzone');
|
|
await expect(dropzone.locator('.files')).toHaveCount(1);
|
|
const preview = dropzone.locator('.dz-preview');
|
|
await expect(preview).toHaveCount(1);
|
|
await expect(preview.locator('.dz-filename')).toHaveText('foo.png');
|
|
await expect(preview.locator('.octicon-copy')).toBeVisible();
|
|
await assertCopy(page, workerInfo, ';
|
|
|
|
await screenshot(page, page.locator('.issue-content-left'));
|
|
});
|
|
|
|
test('Re-add images to dropzone on edit', async ({page}, workerInfo) => {
|
|
await page.goto('/user2/repo1/issues/new');
|
|
|
|
const issueTitle = dynamic_id();
|
|
await page.locator('#issue_title').fill(issueTitle);
|
|
const waitForAttachmentUpload = page.waitForResponse((response) => {
|
|
return response.request().method() === 'POST' && response.url().endsWith('/attachments');
|
|
});
|
|
await pasteImage(page.locator('.markdown-text-editor'));
|
|
await waitForAttachmentUpload;
|
|
await page.getByRole('button', {name: 'Create issue'}).click();
|
|
|
|
await expect(page).toHaveURL(/\/user2\/repo1\/issues\/\d+$/);
|
|
await page.click('.comment-container .context-menu');
|
|
const waitForAttachmentsLoad = page.waitForResponse((response) => {
|
|
return response.request().method() === 'GET' && response.url().endsWith('/attachments');
|
|
});
|
|
await page.click('.comment-container .menu > .edit-content');
|
|
await waitForAttachmentsLoad;
|
|
|
|
const dropzone = page.locator('.dropzone');
|
|
await expect(dropzone.locator('.files').first()).toHaveCount(1);
|
|
const preview = dropzone.locator('.dz-preview');
|
|
await expect(preview).toHaveCount(1);
|
|
await expect(preview.locator('.dz-filename')).toHaveText('foo.png');
|
|
await expect(preview.locator('.octicon-copy')).toBeVisible();
|
|
await assertCopy(page, workerInfo, ';
|
|
|
|
await screenshot(page, page.locator('.issue-content-left'));
|
|
});
|