0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-18 03:38:01 +02:00

Revert e2e repo create/delete to API calls, double timeouts

Revert createRepo/deleteRepo to API-based functions for test
reliability. The UI-based versions were flaky due to navigation
timing. Also double all playwright timeouts (local and CI), rename
API functions to apiX convention, and disable playwright/expect-expect
lint rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
silverwind 2026-02-18 03:21:28 +01:00
parent 79b4b2e0d4
commit 766ba0184a
No known key found for this signature in database
GPG Key ID: 2E62B41C93869443
8 changed files with 29 additions and 32 deletions

View File

@ -913,6 +913,7 @@ export default defineConfig([
files: ['tests/e2e/**/*.test.ts'],
rules: {
...playwright.configs['flat/recommended'].rules,
'playwright/expect-expect': [0],
},
},
{

View File

@ -7,15 +7,15 @@ export default defineConfig({
testMatch: /.*\.test\.ts/,
forbidOnly: Boolean(env.CI),
reporter: 'list',
timeout: env.CI ? 6000 : 2000,
timeout: env.CI ? 12000 : 6000,
expect: {
timeout: env.CI ? 3000 : 1000,
timeout: env.CI ? 6000 : 3000,
},
use: {
baseURL: env.E2E_URL?.replace?.(/\/$/g, ''),
locale: 'en-US',
actionTimeout: env.CI ? 3000 : 1000,
navigationTimeout: env.CI ? 6000 : 2000,
actionTimeout: env.CI ? 6000 : 3000,
navigationTimeout: env.CI ? 12000 : 6000,
},
projects: [
{

View File

@ -6,7 +6,7 @@ test('homepage', async ({page}) => {
await expect(page.getByRole('img', {name: 'Logo'})).toHaveAttribute('src', '/assets/img/logo.svg');
});
test('login and logout', async ({page}) => { // eslint-disable-line playwright/expect-expect
test('login and logout', async ({page}) => {
await login(page);
await logout(page);
});

View File

@ -1,14 +1,14 @@
import {env} from 'node:process';
import {test, expect} from '@playwright/test';
import {login, createRepo, deleteRepo} from './utils.ts';
import {login, apiCreateRepo, apiDeleteRepo} from './utils.ts';
test('create a milestone', async ({page}) => {
const repoName = `e2e-milestone-${Date.now()}`;
await login(page);
await createRepo(page, repoName);
await apiCreateRepo(page.request, {name: repoName});
await page.goto(`/${env.E2E_USER}/${repoName}/milestones/new`);
await page.getByPlaceholder('Title').fill('Test Milestone');
await page.getByRole('button', {name: 'Create Milestone'}).click();
await expect(page.locator('.milestone-list')).toContainText('Test Milestone');
await deleteRepo(page, env.E2E_USER, repoName);
await apiDeleteRepo(page.request, env.E2E_USER, repoName);
});

View File

@ -1,5 +1,5 @@
import {test, expect} from '@playwright/test';
import {login, deleteOrgApi} from './utils.ts';
import {login, apiDeleteOrg} from './utils.ts';
test('create an organization', async ({page}) => {
const orgName = `e2e-org-${Date.now()}`;
@ -9,5 +9,5 @@ test('create an organization', async ({page}) => {
await page.getByRole('button', {name: 'Create Organization'}).click();
await expect(page).toHaveURL(new RegExp(`/org/${orgName}`));
// delete via API because of issues related to form-fetch-action
await deleteOrgApi(page.request, orgName);
await apiDeleteOrg(page.request, orgName);
});

View File

@ -1,12 +1,11 @@
import {env} from 'node:process';
import {test, expect} from '@playwright/test';
import {login, createRepo, deleteRepo} from './utils.ts';
import {apiCreateRepo, apiDeleteRepo} from './utils.ts';
test('README renders on repository page', async ({page}) => {
const repoName = `e2e-readme-${Date.now()}`;
await login(page);
await createRepo(page, repoName);
await apiCreateRepo(page.request, {name: repoName});
await page.goto(`/${env.E2E_USER}/${repoName}`);
await expect(page.locator('#readme')).toContainText(repoName);
await deleteRepo(page, env.E2E_USER, repoName);
await apiDeleteRepo(page.request, env.E2E_USER, repoName);
});

View File

@ -1,6 +1,6 @@
import {env} from 'node:process';
import {test, expect} from '@playwright/test';
import {login, deleteRepo} from './utils.ts';
import {test} from '@playwright/test';
import {login, apiDeleteRepo} from './utils.ts';
test('create a repository', async ({page}) => {
const repoName = `e2e-repo-${Date.now()}`;
@ -8,6 +8,6 @@ test('create a repository', async ({page}) => {
await page.goto('/repo/create');
await page.locator('input[name="repo_name"]').fill(repoName);
await page.getByRole('button', {name: 'Create Repository'}).click();
await expect(page).toHaveURL(new RegExp(`/${env.E2E_USER}/${repoName}$`));
await deleteRepo(page, env.E2E_USER, repoName);
await page.waitForURL(new RegExp(`/${env.E2E_USER}/${repoName}$`));
await apiDeleteRepo(page.request, env.E2E_USER, repoName);
});

View File

@ -24,26 +24,23 @@ async function apiRetry(fn: () => Promise<{ok: () => boolean; status: () => numb
}
}
export async function createRepo(page: Page, name: string) {
await page.goto('/repo/create');
await page.locator('input[name="repo_name"]').fill(name);
await page.locator('input[name="auto_init"]').check();
await page.getByRole('button', {name: 'Create Repository'}).click();
export async function apiCreateRepo(requestContext: APIRequestContext, {name, autoInit = true}: {name: string; autoInit?: boolean}) {
await apiRetry(() => requestContext.post(`${apiBaseUrl()}/api/v1/user/repos`, {
headers: apiHeaders(),
data: {name, auto_init: autoInit},
}), 'apiCreateRepo');
}
export async function deleteRepo(page: Page, owner: string, name: string) {
await page.goto(`/${owner}/${name}/settings`);
await page.locator('button[data-modal="#delete-repo-modal"]').click();
const modal = page.locator('#delete-repo-modal');
await modal.locator('input[name="repo_name"]').fill(name);
await modal.getByRole('button', {name: 'Delete Repository'}).click();
await page.waitForURL('**/');
export async function apiDeleteRepo(requestContext: APIRequestContext, owner: string, name: string) {
await apiRetry(() => requestContext.delete(`${apiBaseUrl()}/api/v1/repos/${owner}/${name}`, {
headers: apiHeaders(),
}), 'apiDeleteRepo');
}
export async function deleteOrgApi(requestContext: APIRequestContext, name: string) {
export async function apiDeleteOrg(requestContext: APIRequestContext, name: string) {
await apiRetry(() => requestContext.delete(`${apiBaseUrl()}/api/v1/orgs/${name}`, {
headers: apiHeaders(),
}), 'deleteOrgApi');
}), 'apiDeleteOrg');
}
export async function clickDropdownItem(page: Page, trigger: Locator, itemText: string) {