chore: remove webkit and mobile safari from playwright (#10103)

Webkit and Mobile safari are comically unreliable, will fail for unexplainable reasons and are very hard to run locally in comparison with the other supported platforms. I do not remember the last time where these two platforms were able to catch a regression where the other platforms did not.

I would like to stress, for the historical record, that many hours has been devoted into adjusting the tests and following best practices to make these two platforms more stable but despite those, IMO wasted, efforts these two platforms are causing many hours of wasted CPU time simply because they are flaky and make (new) contributors nervous if their change contains a regression or not.

To my knowledge, the tests are not broken for these two platforms. If you go to the issue tracker you will not find issues by users that use these two platforms and report that Forgejo is broken. It does not reflect reality.

This is the sunk cost fallacy, bite the bullet and agree that these platforms will not contribute positively to Forgejo's excellent test suite.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10103
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
Gusted 2025-11-13 17:23:08 +01:00 committed by Gusted
parent 8fea4c5829
commit 0737196842
22 changed files with 53 additions and 127 deletions

View file

@ -76,13 +76,6 @@ export default {
}, },
}, },
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
/* Test against mobile viewports. */ /* Test against mobile viewports. */
{ {
name: 'Mobile Chrome', name: 'Mobile Chrome',
@ -91,12 +84,6 @@ export default {
permissions: ['clipboard-read', 'clipboard-write'], permissions: ['clipboard-read', 'clipboard-write'],
}, },
}, },
{
name: 'Mobile Safari',
use: {
...devices['iPhone 12'],
},
},
], ],
/* Folder for test artifacts created during test execution such as screenshots, traces, etc. */ /* Folder for test artifacts created during test execution such as screenshots, traces, etc. */

View file

@ -269,17 +269,6 @@ If you want to iterate fast,
save your time and only run very selected tests. save your time and only run very selected tests.
Use only one browser. Use only one browser.
### Skip Safari if it doesn't work
Many contributors have issues getting Safari (webkit)
and especially Safari Mobile to work.
At the top of your test function, you can use:
~~~javascript
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile.');
~~~
### Don't forget the formatting. ### Don't forget the formatting.
When writing tests without modifying other frontend code, When writing tests without modifying other frontend code,

View file

@ -9,14 +9,13 @@
// routers/web/repo/actions/** // routers/web/repo/actions/**
// @watch end // @watch end
import {expect, type Page, type TestInfo} from '@playwright/test'; import {expect, type Page} from '@playwright/test';
import {test} from './utils_e2e.ts'; import {test} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts'; import {screenshot} from './shared/screenshots.ts';
const workflow_trigger_notification_text = 'This workflow has a workflow_dispatch event trigger.'; const workflow_trigger_notification_text = 'This workflow has a workflow_dispatch event trigger.';
async function dispatchSuccess(page: Page, testInfo: TestInfo) { async function dispatchSuccess(page: Page) {
test.skip(testInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
await page.locator('#workflow_dispatch_dropdown>button').click(); await page.locator('#workflow_dispatch_dropdown>button').click();
@ -49,9 +48,7 @@ test.describe('Workflow Authenticated user2', () => {
await screenshot(page, page.locator('div.ui.container').filter({hasText: 'All workflows'})); await screenshot(page, page.locator('div.ui.container').filter({hasText: 'All workflows'}));
}); });
test('dispatch error: missing inputs', async ({page}, testInfo) => { test('dispatch error: missing inputs', async ({page}) => {
test.skip(testInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
await page.locator('#workflow_dispatch_dropdown>button').click(); await page.locator('#workflow_dispatch_dropdown>button').click();
@ -70,8 +67,8 @@ test.describe('Workflow Authenticated user2', () => {
// no assertions as the login in this test case is extracted for reuse // no assertions as the login in this test case is extracted for reuse
// eslint-disable-next-line playwright/expect-expect // eslint-disable-next-line playwright/expect-expect
test('dispatch success', async ({page}, testInfo) => { test('dispatch success', async ({page}) => {
await dispatchSuccess(page, testInfo); await dispatchSuccess(page);
}); });
}); });
@ -100,7 +97,7 @@ async function simulatePollingInterval(page: Page) {
test.describe('workflow list dynamic refresh', () => { test.describe('workflow list dynamic refresh', () => {
test.use({user: 'user2'}); test.use({user: 'user2'});
test('refreshes on visibility change', async ({page}, testInfo) => { test('refreshes on visibility change', async ({page}) => {
// Test operates by creating two pages; one which is sitting idle on the workflows list (backgroundPage), and one // Test operates by creating two pages; one which is sitting idle on the workflows list (backgroundPage), and one
// which triggers a workflow dispatch. Then a document visibilitychange event is fired on the background page to // which triggers a workflow dispatch. Then a document visibilitychange event is fired on the background page to
// mimic a user returning to the tab on their browser, which should trigger the workflow list to refresh and display // mimic a user returning to the tab on their browser, which should trigger the workflow list to refresh and display
@ -110,7 +107,7 @@ test.describe('workflow list dynamic refresh', () => {
await backgroundPage.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await backgroundPage.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
// Mirror the `Workflow Authenticated user2 > dispatch success` test: // Mirror the `Workflow Authenticated user2 > dispatch success` test:
await dispatchSuccess(page, testInfo); await dispatchSuccess(page);
const latestDispatchedRun = await page.locator('.run-list>:first-child .flex-item-body>b').textContent(); const latestDispatchedRun = await page.locator('.run-list>:first-child .flex-item-body>b').textContent();
expect(latestDispatchedRun).toMatch(/^#/); // workflow ID, eg. "#53" expect(latestDispatchedRun).toMatch(/^#/); // workflow ID, eg. "#53"
@ -123,7 +120,7 @@ test.describe('workflow list dynamic refresh', () => {
await screenshot(backgroundPage, page.locator('div.ui.container').filter({hasText: 'All workflows'})); await screenshot(backgroundPage, page.locator('div.ui.container').filter({hasText: 'All workflows'}));
}); });
test('refreshes on interval', async ({page}, testInfo) => { test('refreshes on interval', async ({page}) => {
// Test operates by creating two pages; one which is sitting idle on the workflows list (backgroundPage), and one // Test operates by creating two pages; one which is sitting idle on the workflows list (backgroundPage), and one
// which triggers a workflow dispatch. After the polling, the page should refresh and show the newly dispatched // which triggers a workflow dispatch. After the polling, the page should refresh and show the newly dispatched
// workflow from the other page. // workflow from the other page.
@ -132,7 +129,7 @@ test.describe('workflow list dynamic refresh', () => {
await backgroundPage.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await backgroundPage.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
// Mirror the `Workflow Authenticated user2 > dispatch success` test: // Mirror the `Workflow Authenticated user2 > dispatch success` test:
await dispatchSuccess(page, testInfo); await dispatchSuccess(page);
const latestDispatchedRun = await page.locator('.run-list>:first-child .flex-item-body>b').textContent(); const latestDispatchedRun = await page.locator('.run-list>:first-child .flex-item-body>b').textContent();
expect(latestDispatchedRun).toMatch(/^#/); // workflow ID, eg. "#53" expect(latestDispatchedRun).toMatch(/^#/); // workflow ID, eg. "#53"
@ -141,13 +138,13 @@ test.describe('workflow list dynamic refresh', () => {
await screenshot(backgroundPage, page.locator('div.ui.container').filter({hasText: 'All workflows'})); await screenshot(backgroundPage, page.locator('div.ui.container').filter({hasText: 'All workflows'}));
}); });
test('post-refresh the dropdowns continue to operate', async ({page}, testInfo) => { test('post-refresh the dropdowns continue to operate', async ({page}) => {
// Verify that after the page is dynamically refreshed, the 'Actor', 'Status', and 'Run workflow' dropdowns work // Verify that after the page is dynamically refreshed, the 'Actor', 'Status', and 'Run workflow' dropdowns work
// correctly -- that the htmx morph hasn't messed up any JS event handlers. // correctly -- that the htmx morph hasn't messed up any JS event handlers.
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
// Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown // Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown
await dispatchSuccess(page, testInfo); await dispatchSuccess(page);
// Perform a dynamic refresh before checking the functionality of each dropdown. // Perform a dynamic refresh before checking the functionality of each dropdown.
await simulatePollingInterval(page); await simulatePollingInterval(page);
@ -171,14 +168,14 @@ test.describe('workflow list dynamic refresh', () => {
await expect(page.getByText('All Actors')).toBeVisible(); await expect(page.getByText('All Actors')).toBeVisible();
}); });
test('refresh does not break interacting with open drop-downs', async ({page}, testInfo) => { test('refresh does not break interacting with open drop-downs', async ({page}) => {
// Verify that if the polling refresh occurs while interacting with any multi-step dropdown on the page, the // Verify that if the polling refresh occurs while interacting with any multi-step dropdown on the page, the
// multi-step interaction continues to be visible and functional. This is implemented by preventing the refresh, // multi-step interaction continues to be visible and functional. This is implemented by preventing the refresh,
// but that isn't the subject of the test here -- as long as the dropdown isn't broken by the refresh, that's fine. // but that isn't the subject of the test here -- as long as the dropdown isn't broken by the refresh, that's fine.
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
// Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown // Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown
await dispatchSuccess(page, testInfo); await dispatchSuccess(page);
// Workflow run dialog // Workflow run dialog
await expect(page.locator('input[name="inputs[string2]"]')).toBeHidden(); await expect(page.locator('input[name="inputs[string2]"]')).toBeHidden();

View file

@ -66,8 +66,7 @@ test('Rename normal branch', async ({page}) => {
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
}); });
test('Rename default branch', async ({page}, workerInfo) => { test('Rename default branch', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name));
let response = await page.goto('/user2/repo1/branches'); let response = await page.goto('/user2/repo1/branches');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -11,9 +11,7 @@ import {expect} from '@playwright/test';
import {test} from './utils_e2e.ts'; import {test} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts'; import {screenshot} from './shared/screenshots.ts';
test('copy src file path to clipboard', async ({page}, workerInfo) => { test('copy src file path to clipboard', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Apple clipboard API addon - starting at just $499!');
const response = await page.goto('/user2/repo1/src/branch/master/README.md'); const response = await page.goto('/user2/repo1/src/branch/master/README.md');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
@ -24,9 +22,7 @@ test('copy src file path to clipboard', async ({page}, workerInfo) => {
await screenshot(page, page.getByText('Copied'), 50); await screenshot(page, page.getByText('Copied'), 50);
}); });
test('copy diff file path to clipboard', async ({page}, workerInfo) => { test('copy diff file path to clipboard', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Apple clipboard API addon - starting at just $499!');
const response = await page.goto('/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d/README.md'); const response = await page.goto('/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d/README.md');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -11,8 +11,7 @@ import {screenshot} from './shared/screenshots.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test('Create branch from commit', async ({page}, workerInfo) => { test('Create branch from commit', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name));
let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d'); let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
@ -40,8 +39,7 @@ test('Create branch from commit', async ({page}, workerInfo) => {
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
}); });
test('Create tag from commit', async ({page}, workerInfo) => { test('Create tag from commit', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name));
let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d'); let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -41,8 +41,7 @@ test('Dimmed modal', async ({page}) => {
await screenshot(page, page.locator('.ui.g-modal-confirm.delete.modal'), 50); await screenshot(page, page.locator('.ui.g-modal-confirm.delete.modal'), 50);
}); });
test('Dimmed overflow', async ({page}, workerInfo) => { test('Dimmed overflow', async ({page}) => {
test.skip(['Mobile Safari'].includes(workerInfo.project.name), 'Mouse wheel is not supported in mobile WebKit');
await page.goto('/user2/repo1/_new/master/'); await page.goto('/user2/repo1/_new/master/');
// Type in a file name. // Type in a file name.

View file

@ -8,7 +8,7 @@
// web_src/js/features/repo-legacy.js // web_src/js/features/repo-legacy.js
// @watch end // @watch end
import {expect, type Locator, type Page, type TestInfo} from '@playwright/test'; import {expect, type Locator, type Page} from '@playwright/test';
import {test, dynamic_id} from './utils_e2e.ts'; import {test, dynamic_id} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts'; import {screenshot} from './shared/screenshots.ts';
@ -42,10 +42,7 @@ async function pasteImage(el: Locator) {
}); });
} }
async function assertCopy(page: Page, workerInfo: TestInfo, startWith: string) { async function assertCopy(page: Page, startWith: string) {
const project = workerInfo.project.name;
if (project === 'webkit' || project === 'Mobile Safari') return;
const dropzone = page.locator('.dropzone'); const dropzone = page.locator('.dropzone');
const preview = dropzone.locator('.dz-preview'); const preview = dropzone.locator('.dz-preview');
const copyLink = preview.locator('.octicon-copy').locator('..'); const copyLink = preview.locator('.octicon-copy').locator('..');
@ -55,7 +52,7 @@ async function assertCopy(page: Page, workerInfo: TestInfo, startWith: string) {
expect(clipboardContent).toContain(startWith); expect(clipboardContent).toContain(startWith);
} }
test('Paste image in new comment', async ({page}, workerInfo) => { test('Paste image in new comment', async ({page}) => {
await page.goto('/user2/repo1/issues/new'); await page.goto('/user2/repo1/issues/new');
const waitForAttachmentUpload = page.waitForResponse((response) => { const waitForAttachmentUpload = page.waitForResponse((response) => {
@ -70,12 +67,12 @@ test('Paste image in new comment', async ({page}, workerInfo) => {
await expect(preview).toHaveCount(1); await expect(preview).toHaveCount(1);
await expect(preview.locator('.dz-filename')).toHaveText('foo.png'); await expect(preview.locator('.dz-filename')).toHaveText('foo.png');
await expect(preview.locator('.octicon-copy')).toBeVisible(); await expect(preview.locator('.octicon-copy')).toBeVisible();
await assertCopy(page, workerInfo, '![foo]('); await assertCopy(page, '![foo](');
await screenshot(page, page.locator('.issue-content-left')); await screenshot(page, page.locator('.issue-content-left'));
}); });
test('Re-add images to dropzone on edit', async ({page}, workerInfo) => { test('Re-add images to dropzone on edit', async ({page}) => {
await page.goto('/user2/repo1/issues/new'); await page.goto('/user2/repo1/issues/new');
const issueTitle = dynamic_id(); const issueTitle = dynamic_id();
@ -101,7 +98,7 @@ test('Re-add images to dropzone on edit', async ({page}, workerInfo) => {
await expect(preview).toHaveCount(1); await expect(preview).toHaveCount(1);
await expect(preview.locator('.dz-filename')).toHaveText('foo.png'); await expect(preview.locator('.dz-filename')).toHaveText('foo.png');
await expect(preview.locator('.octicon-copy')).toBeVisible(); await expect(preview.locator('.octicon-copy')).toBeVisible();
await assertCopy(page, workerInfo, '![foo]('); await assertCopy(page, '![foo](');
await screenshot(page, page.locator('.issue-content-left')); await screenshot(page, page.locator('.issue-content-left'));
}); });

View file

@ -18,7 +18,7 @@ for (const run of [
test.describe(`Create issue & comment`, () => { test.describe(`Create issue & comment`, () => {
// playwright/valid-title says: [error] Title must be a string // playwright/valid-title says: [error] Title must be a string
test(`${run.title}`, async ({browser}, workerInfo) => { test(`${run.title}`, async ({browser}, workerInfo) => {
test.skip(['Mobile Chrome', 'Mobile Safari'].includes(workerInfo.project.name), 'Mobile Chrome has trouble clicking Comment button with JS enabled, Mobile Safari is flaky and only passes on retry'); test.skip(['Mobile Chrome'].includes(workerInfo.project.name), 'Mobile Chrome has trouble clicking Comment button with JS enabled');
const issueTitle = dynamic_id(); const issueTitle = dynamic_id();
const issueContent = dynamic_id(); const issueContent = dynamic_id();
@ -117,8 +117,8 @@ test.describe('Button text replaced by JS', () => {
}); });
}); });
test('Hyperlink paste behaviour', async ({page}, workerInfo) => { test('Hyperlink paste behaviour', async ({page, isMobile}) => {
test.skip(['Mobile Safari', 'Mobile Chrome', 'webkit'].includes(workerInfo.project.name), 'Mobile clients seem to have very weird behaviour with this test, which I cannot confirm with real usage'); test.skip(isMobile, 'Mobile clients seem to have very weird behaviour with this test, which I cannot confirm with real usage');
await page.goto('/user2/repo1/issues/new'); await page.goto('/user2/repo1/issues/new');
await page.locator('textarea').click(); await page.locator('textarea').click();
// same URL // same URL

View file

@ -49,8 +49,7 @@ test.describe('Pull: Toggle WIP', () => {
await check_wip({page}, false); await check_wip({page}, false);
}); });
test('simple toggle', async ({page}, workerInfo) => { test('simple toggle', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
// toggle to WIP // toggle to WIP
await toggle_wip_to({page}, true); await toggle_wip_to({page}, true);
await check_wip({page}, true); await check_wip({page}, true);
@ -59,8 +58,7 @@ test.describe('Pull: Toggle WIP', () => {
await check_wip({page}, false); await check_wip({page}, false);
}); });
test('manual edit', async ({page}, workerInfo) => { test('manual edit', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
await page.goto('/user2/repo1/pulls/5'); await page.goto('/user2/repo1/pulls/5');
// manually edit title to another prefix // manually edit title to another prefix
await page.locator('#issue-title-edit-show').click(); await page.locator('#issue-title-edit-show').click();
@ -72,8 +70,7 @@ test.describe('Pull: Toggle WIP', () => {
await check_wip({page}, false); await check_wip({page}, false);
}); });
test('maximum title length', async ({page}, workerInfo) => { test('maximum title length', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
await page.goto('/user2/repo1/pulls/5'); await page.goto('/user2/repo1/pulls/5');
// check maximum title length is handled gracefully // check maximum title length is handled gracefully
const maxLenStr = prTitle + 'a'.repeat(240); const maxLenStr = prTitle + 'a'.repeat(240);
@ -91,9 +88,7 @@ test.describe('Pull: Toggle WIP', () => {
}); });
}); });
test('Issue: Labels', async ({page}, workerInfo) => { test('Issue: Labels', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
async function submitLabels({page}: {page: Page}) { async function submitLabels({page}: {page: Page}) {
const submitted = page.waitForResponse('/user2/repo1/issues/labels'); const submitted = page.waitForResponse('/user2/repo1/issues/labels');
await page.locator('textarea').first().click(); // close via unrelated element await page.locator('textarea').first().click(); // close via unrelated element
@ -138,8 +133,7 @@ test('Issue: Labels', async ({page}, workerInfo) => {
await expect(labelList.filter({hasText: 'label1'})).toBeVisible(); await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
}); });
test('Issue: Assignees', async ({page}, workerInfo) => { test('Issue: Assignees', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
// select label list in sidebar only // select label list in sidebar only
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a'); const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a');
@ -175,8 +169,7 @@ test('Issue: Assignees', async ({page}, workerInfo) => {
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden(); await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
}); });
test('New Issue: Assignees', async ({page}, workerInfo) => { test('New Issue: Assignees', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
// select label list in sidebar only // select label list in sidebar only
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item'); const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item');
@ -216,9 +209,7 @@ test('New Issue: Assignees', async ({page}, workerInfo) => {
await screenshot(page, page.locator('div.filter.menu[data-id="#assignee_ids"]'), 30); await screenshot(page, page.locator('div.filter.menu[data-id="#assignee_ids"]'), 30);
}); });
test('Issue: Milestone', async ({page}, workerInfo) => { test('Issue: Milestone', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
@ -239,9 +230,7 @@ test('Issue: Milestone', async ({page}, workerInfo) => {
await expect(page.locator('.timeline-item.event').last()).toContainText('user2 removed this from the milestone1 milestone'); await expect(page.locator('.timeline-item.event').last()).toContainText('user2 removed this from the milestone1 milestone');
}); });
test('New Issue: Milestone', async ({page}, workerInfo) => { test('New Issue: Milestone', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
const response = await page.goto('/user2/repo1/issues/new'); const response = await page.goto('/user2/repo1/issues/new');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -11,8 +11,7 @@ import {expect} from '@playwright/test';
import {test, test_context} from './utils_e2e.ts'; import {test, test_context} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts'; import {screenshot} from './shared/screenshots.ts';
test('Mismatched ROOT_URL', async ({browser}, workerInfo) => { test('Mismatched ROOT_URL', async ({browser}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'init script gets randomly ignored');
const context = await test_context(browser); const context = await test_context(browser);
const page = await context.newPage(); const page = await context.newPage();

View file

@ -13,9 +13,7 @@ import {screenshot} from './shared/screenshots.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test('Markdown image preview behaviour', async ({page}, workerInfo) => { test('Markdown image preview behaviour', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;');
// Editing the root README.md file for image preview // Editing the root README.md file for image preview
const editPath = '/user2/repo1/src/branch/master/README.md'; const editPath = '/user2/repo1/src/branch/master/README.md';
@ -374,9 +372,7 @@ test('Markdown insert table', async ({page}) => {
await screenshot(page); await screenshot(page);
}); });
test('Markdown insert link', async ({page}, workerInfo) => { test('Markdown insert link', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Unreliable in this test');
const response = await page.goto('/user2/repo1/issues/new'); const response = await page.goto('/user2/repo1/issues/new');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -6,8 +6,7 @@ import {expect} from '@playwright/test';
import {test} from './utils_e2e.ts'; import {test} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts'; import {screenshot} from './shared/screenshots.ts';
test('markup with #xyz-mode-only', async ({page}, workerInfo) => { test('markup with #xyz-mode-only', async ({page}) => {
test.skip(['webkit', 'Mobile Safari'].includes(workerInfo.project.name), 'Newest version contains a regression');
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -12,8 +12,7 @@ import {screenshot} from './shared/screenshots.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test('Dialog modal', async ({page}, workerInfo) => { test('Dialog modal', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'keyboard shortcuts do not work');
let response = await page.goto('/user2/repo1/_new/master', {waitUntil: 'domcontentloaded'}); let response = await page.goto('/user2/repo1/_new/master', {waitUntil: 'domcontentloaded'});
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -11,9 +11,7 @@ import {validate_form} from './shared/forms.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test('org team settings', async ({page}, workerInfo) => { test('org team settings', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Unreliable in this test');
const response = await page.goto('/org/org3/teams/team1/edit'); const response = await page.goto('/org/org3/teams/team1/edit');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -11,9 +11,7 @@ import {screenshot} from './shared/screenshots.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test('Follow and block actions', async ({page}, workerInfo) => { test('Follow and block actions', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Mobile Safari is unreliable in this test');
await page.goto('/user1'); await page.goto('/user1');
// Check if following and then unfollowing works. // Check if following and then unfollowing works.

View file

@ -16,8 +16,8 @@ import {validate_form} from './shared/forms.ts';
test.use({user: 'user2'}); test.use({user: 'user2'});
test.describe('Releases', () => { test.describe('Releases', () => {
test('External Release Attachments', async ({page, isMobile}, workerInfo) => { test('External Release Attachments', async ({page, isMobile}) => {
test.skip(isMobile || workerInfo.project.name === 'webkit'); test.skip(isMobile);
// Click "New Release" // Click "New Release"
await page.goto('/user2/repo2/releases'); await page.goto('/user2/repo2/releases');

View file

@ -37,9 +37,7 @@ async function assertSelectedLines(page: Page, nums: string[]) {
return pageAssertions(); return pageAssertions();
} }
test('Line Range Selection', async ({page}, workerInfo) => { test('Line Range Selection', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Unreliable in this test');
const filePath = '/user2/repo1/src/branch/master/README.md?display=source'; const filePath = '/user2/repo1/src/branch/master/README.md?display=source';
const response = await page.goto(filePath); const response = await page.goto(filePath);
@ -145,9 +143,7 @@ test('File folding', async ({page}) => {
await expect(diffFileBody).toBeVisible(); await expect(diffFileBody).toBeVisible();
}); });
test('Copy line permalink', async ({page}, workerInfo) => { test('Copy line permalink', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Apple clipboard API addon - starting at just $499!');
const response = await page.goto('/user2/repo1/src/branch/master/README.md?display=source#L1'); const response = await page.goto('/user2/repo1/src/branch/master/README.md?display=source#L1');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -26,9 +26,7 @@ test('Migration type seleciton screen', async ({page}) => {
await screenshot(page); await screenshot(page);
}); });
test('Migration Repo Name detection', async ({page}, workerInfo) => { test('Migration Repo Name detection', async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari');
await page.goto('/repo/migrate?service_type=2'); await page.goto('/repo/migrate?service_type=2');
const form = page.locator('form'); const form = page.locator('form');
@ -54,9 +52,7 @@ test('Migration Repo Name detection', async ({page}, workerInfo) => {
await screenshot(page); await screenshot(page);
}); });
test('Migration Progress Page', async ({page, browser}, workerInfo) => { test('Migration Progress Page', async ({page, browser}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari');
const repoName = dynamic_id(); const repoName = dynamic_id();
expect((await page.goto(`/user2/${repoName}`))?.status(), 'repo should not exist yet').toBe(404); expect((await page.goto(`/user2/${repoName}`))?.status(), 'repo should not exist yet').toBe(404);

View file

@ -93,8 +93,7 @@ test('New repo: initialize later', async ({page}) => {
await screenshot(page); await screenshot(page);
}); });
test('New repo: from template', async ({page}, workerInfo) => { test('New repo: from template', async ({page}) => {
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'WebKit browsers seem to have CORS issues with localhost here.');
const response = await page.goto('/repo/create'); const response = await page.goto('/repo/create');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);

View file

@ -9,9 +9,7 @@ import {screenshot} from './shared/screenshots.ts';
for (const searchTerm of ['space', 'consectetur']) { for (const searchTerm of ['space', 'consectetur']) {
for (const width of [null, 2560, 4000]) { for (const width of [null, 2560, 4000]) {
test(`Search for '${searchTerm}' and test for no overflow ${width && `on ${width}-wide viewport` || ''}`, async ({page, viewport}, workerInfo) => { test(`Search for '${searchTerm}' and test for no overflow ${width && `on ${width}-wide viewport` || ''}`, async ({page, viewport}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Fails as always, see https://codeberg.org/forgejo/forgejo/pulls/5326#issuecomment-2313275');
await page.setViewportSize({ await page.setViewportSize({
width: width ?? viewport.width, width: width ?? viewport.width,
height: 1440, // We're testing that we fit horizontally - vertical scrolling is fine. height: 1440, // We're testing that we fit horizontally - vertical scrolling is fine.
@ -24,15 +22,14 @@ for (const searchTerm of ['space', 'consectetur']) {
await page.getByPlaceholder('Search wiki').dispatchEvent('keyup'); await page.getByPlaceholder('Search wiki').dispatchEvent('keyup');
await expect(page.locator('#wiki-search a[href]')).toBeInViewport({ await expect(page.locator('#wiki-search a[href]')).toBeInViewport({
ratio: workerInfo.project.name === 'webkit' ? 0.9 : 1, ratio: 1,
}); });
await screenshot(page); await screenshot(page);
}); });
} }
} }
test(`Search results show titles (and not file names)`, async ({page}, workerInfo) => { test(`Search results show titles (and not file names)`, async ({page}) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Fails as always, see https://codeberg.org/forgejo/forgejo/pulls/5326#issuecomment-2313275');
await page.goto('/user2/repo1/wiki'); await page.goto('/user2/repo1/wiki');
await page.getByPlaceholder('Search wiki').fill('spaces'); await page.getByPlaceholder('Search wiki').fill('spaces');
await page.getByPlaceholder('Search wiki').click(); await page.getByPlaceholder('Search wiki').click();

View file

@ -83,9 +83,7 @@ func createSessions(t testing.TB) {
browsers := []string{ browsers := []string{
"chromium", "chromium",
"firefox", "firefox",
"webkit",
"Mobile Chrome", "Mobile Chrome",
"Mobile Safari",
} }
scopes := []string{ scopes := []string{
"shared", "shared",