mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-23 05:42:33 +09:00
test(e2e): add comment, release, star, PR and fork tests (#37800)
Adds Playwright e2e coverage for five high-value workflows, each driven through semantic locators with API-based setup: - comment on and close an issue - publish a release - star and watch a repository - create a pull request from the compare page - fork a repository Also passes `autoInit: false` in existing tests that only exercise DB-backed units (issues, reactions, milestones, projects, events), skipping an unused initial commit to speed up their setup and reduce parallel git contention. --- This PR was written with the help of Claude Opus 4.7 --------- Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: Nicolas <bircni@icloud.com>
This commit is contained in:
@@ -12,7 +12,7 @@ test.describe('events', () => {
|
||||
|
||||
// Create repo and login in parallel — repo is needed for the issue, login for the event stream
|
||||
await Promise.all([
|
||||
apiCreateRepo(request, {name: repoName, headers: apiUserHeaders(owner)}),
|
||||
apiCreateRepo(request, {name: repoName, autoInit: false, headers: apiUserHeaders(owner)}),
|
||||
loginUser(page, owner),
|
||||
]);
|
||||
await page.goto('/');
|
||||
@@ -36,7 +36,7 @@ test.describe('events', () => {
|
||||
await Promise.all([
|
||||
loginUser(page, name),
|
||||
(async () => {
|
||||
await apiCreateRepo(request, {name, headers});
|
||||
await apiCreateRepo(request, {name, autoInit: false, headers});
|
||||
await apiCreateIssue(request, {owner: name, repo: name, title: 'events stopwatch test', headers});
|
||||
await apiStartStopwatch(request, name, name, 1, {headers});
|
||||
})(),
|
||||
|
||||
18
tests/e2e/fork.test.ts
Normal file
18
tests/e2e/fork.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {env} from 'node:process';
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {login, apiCreateRepo, apiCreateUser, apiUserHeaders, randomString} from './utils.ts';
|
||||
|
||||
test('fork a repository', async ({page, request}) => {
|
||||
const upstream = `fork-owner-${randomString(8)}`;
|
||||
const repoName = `e2e-fork-${randomString(8)}`;
|
||||
await apiCreateUser(request, upstream);
|
||||
await Promise.all([
|
||||
apiCreateRepo(request, {name: repoName, headers: apiUserHeaders(upstream)}),
|
||||
login(page),
|
||||
]);
|
||||
await page.goto(`/${upstream}/${repoName}/fork`);
|
||||
|
||||
await page.getByRole('button', {name: 'Fork Repository'}).click();
|
||||
await page.waitForURL(new RegExp(`/${env.GITEA_TEST_E2E_USER}/${repoName}$`));
|
||||
await expect(page.getByRole('link', {name: `${upstream}/${repoName}`})).toBeVisible();
|
||||
});
|
||||
24
tests/e2e/issue-comment.test.ts
Normal file
24
tests/e2e/issue-comment.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {env} from 'node:process';
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {login, apiCreateRepo, apiCreateIssue, randomString} from './utils.ts';
|
||||
|
||||
test('comment on and close an issue', async ({page, request}) => {
|
||||
const repoName = `e2e-issue-comment-${randomString(8)}`;
|
||||
const owner = env.GITEA_TEST_E2E_USER;
|
||||
await apiCreateRepo(request, {name: repoName, autoInit: false});
|
||||
await Promise.all([
|
||||
apiCreateIssue(request, {owner, repo: repoName, title: 'Comment test'}),
|
||||
login(page),
|
||||
]);
|
||||
await page.goto(`/${owner}/${repoName}/issues/1`);
|
||||
|
||||
const body = `e2e-comment-${randomString(8)}`;
|
||||
await page.getByPlaceholder('Leave a comment').fill(body);
|
||||
// exact match: the status button reads "Close with Comment" while the box has content, which substring-matches "Comment"
|
||||
await page.getByRole('button', {name: 'Comment', exact: true}).click();
|
||||
await expect(page.locator('.comment-body').filter({hasText: body})).toBeVisible();
|
||||
|
||||
// posting reloaded the page with an empty box, so the status button now reads "Close Issue"
|
||||
await page.getByRole('button', {name: 'Close Issue'}).click();
|
||||
await expect(page.getByRole('button', {name: 'Reopen Issue'})).toBeVisible();
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import {login, apiCreateRepo, apiCreateIssue, apiDeleteRepo, createProject, crea
|
||||
test('assign issue to project and change column', async ({page}) => {
|
||||
const repoName = `e2e-issue-project-${randomString(8)}`;
|
||||
const user = env.GITEA_TEST_E2E_USER;
|
||||
await Promise.all([login(page), apiCreateRepo(page.request, {name: repoName})]);
|
||||
await Promise.all([login(page), apiCreateRepo(page.request, {name: repoName, autoInit: false})]);
|
||||
await page.goto(`/${user}/${repoName}/projects/new`);
|
||||
await page.locator('input[name="title"]').fill('Kanban Board');
|
||||
await page.getByRole('button', {name: 'Create Project'}).click();
|
||||
@@ -33,7 +33,7 @@ test('create a project', async ({page}) => {
|
||||
const projectTitle = 'Test Project';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Navigate to new project page
|
||||
@@ -62,7 +62,7 @@ test('assign issue to multiple projects via sidebar', async ({page}) => {
|
||||
const issueTitle = 'Test issue for multiple projects';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects via UI
|
||||
@@ -112,7 +112,7 @@ test('create issue with multiple projects pre-selected', async ({page}) => {
|
||||
const issueTitle = 'Issue with multiple projects';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects via UI
|
||||
@@ -163,7 +163,7 @@ test('filter issues by multiple projects in issue list', async ({page}) => {
|
||||
const project2Title = 'Filter Project B';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects via UI
|
||||
@@ -229,7 +229,7 @@ test('remove issue from one project keeping others', async ({page}) => {
|
||||
const issueTitle = 'Issue to modify projects';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects via UI
|
||||
@@ -288,7 +288,7 @@ test('filter issues with no project using project=-1', async ({page}) => {
|
||||
const projectTitle = 'Some Project';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create a project via UI
|
||||
@@ -349,7 +349,7 @@ test('close project and view in closed projects list', async ({page}) => {
|
||||
const closedProjectTitle = 'Project To Close';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects via UI
|
||||
@@ -404,7 +404,7 @@ test('select projects on new issue page shows in sidebar', async ({page}) => {
|
||||
const project2Title = 'Project Two';
|
||||
|
||||
await login(page);
|
||||
await apiCreateRepo(page.request, {name: repoName});
|
||||
await apiCreateRepo(page.request, {name: repoName, autoInit: false});
|
||||
|
||||
try {
|
||||
// Create two projects
|
||||
|
||||
@@ -5,7 +5,7 @@ import {apiCreateRepo, apiCreateIssue, assertNoJsError, randomString} from './ut
|
||||
test('mermaid diagram in issue', async ({page, request}) => {
|
||||
const repoName = `e2e-mermaid-${randomString(8)}`;
|
||||
const owner = env.GITEA_TEST_E2E_USER;
|
||||
await apiCreateRepo(request, {name: repoName});
|
||||
await apiCreateRepo(request, {name: repoName, autoInit: false});
|
||||
const body = '```mermaid\nflowchart LR\n Alpha --> Beta\n Beta --> Gamma\n```\n';
|
||||
const {index} = await apiCreateIssue(request, {owner, repo: repoName, title: 'mermaid test', body});
|
||||
await page.goto(`/${owner}/${repoName}/issues/${index}`);
|
||||
|
||||
@@ -4,7 +4,7 @@ import {login, apiCreateRepo, randomString} from './utils.ts';
|
||||
|
||||
test('create a milestone', async ({page}) => {
|
||||
const repoName = `e2e-milestone-${randomString(8)}`;
|
||||
await Promise.all([login(page), apiCreateRepo(page.request, {name: repoName})]);
|
||||
await Promise.all([login(page), apiCreateRepo(page.request, {name: repoName, autoInit: false})]);
|
||||
await page.goto(`/${env.GITEA_TEST_E2E_USER}/${repoName}/milestones/new`);
|
||||
await page.getByPlaceholder('Title').fill('Test Milestone');
|
||||
await page.getByRole('button', {name: 'Create Milestone'}).click();
|
||||
|
||||
23
tests/e2e/pr-create.test.ts
Normal file
23
tests/e2e/pr-create.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {env} from 'node:process';
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {login, apiCreateRepo, apiCreateFile, randomString} from './utils.ts';
|
||||
|
||||
test('create a pull request from the compare page', async ({page, request}) => {
|
||||
const repoName = `e2e-pr-create-${randomString(8)}`;
|
||||
const owner = env.GITEA_TEST_E2E_USER;
|
||||
await apiCreateRepo(request, {name: repoName});
|
||||
await Promise.all([
|
||||
apiCreateFile(request, owner, repoName, 'feat.txt', 'feature content\n', {branch: 'main', newBranch: 'feat'}),
|
||||
login(page),
|
||||
]);
|
||||
// expand=1 renders the PR form directly, skipping the "New Pull Request" toggle click
|
||||
await page.goto(`/${owner}/${repoName}/compare/main...feat?expand=1`);
|
||||
|
||||
const title = `e2e-pr-${randomString(8)}`;
|
||||
await page.getByPlaceholder('Title').fill(title);
|
||||
await page.getByRole('button', {name: 'Create Pull Request'}).click();
|
||||
|
||||
// commit, not full load: the PR title heading is server-rendered, so the assertion can resolve before the heavy diff/timeline finishes
|
||||
await page.waitForURL(new RegExp(`/${owner}/${repoName}/pulls/\\d+$`), {waitUntil: 'commit'});
|
||||
await expect(page.getByRole('heading', {name: title})).toBeVisible();
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import {login, apiCreateRepo, apiCreateIssue, randomString} from './utils.ts';
|
||||
test('toggle issue reactions', async ({page, request}) => {
|
||||
const repoName = `e2e-reactions-${randomString(8)}`;
|
||||
const owner = env.GITEA_TEST_E2E_USER;
|
||||
await apiCreateRepo(request, {name: repoName});
|
||||
await apiCreateRepo(request, {name: repoName, autoInit: false});
|
||||
await Promise.all([
|
||||
apiCreateIssue(request, {owner, repo: repoName, title: 'Reaction test'}),
|
||||
login(page),
|
||||
|
||||
19
tests/e2e/release.test.ts
Normal file
19
tests/e2e/release.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {env} from 'node:process';
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {login, apiCreateRepo, randomString} from './utils.ts';
|
||||
|
||||
test('create a release', async ({page, request}) => {
|
||||
const repoName = `e2e-release-${randomString(8)}`;
|
||||
const owner = env.GITEA_TEST_E2E_USER;
|
||||
await Promise.all([apiCreateRepo(request, {name: repoName}), login(page)]);
|
||||
await page.goto(`/${owner}/${repoName}/releases/new`);
|
||||
|
||||
const tag = `v1.0.0-${randomString(8)}`;
|
||||
const title = `e2e-release-${randomString(8)}`;
|
||||
await page.getByLabel('Tag name').fill(tag);
|
||||
await page.getByLabel('Release title').fill(title);
|
||||
await page.getByRole('button', {name: 'Publish Release'}).click();
|
||||
|
||||
await page.waitForURL(new RegExp(`/${owner}/${repoName}/releases$`));
|
||||
await expect(page.locator('.release-list-title')).toContainText(title);
|
||||
});
|
||||
20
tests/e2e/repo-star-watch.test.ts
Normal file
20
tests/e2e/repo-star-watch.test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {test, expect} from '@playwright/test';
|
||||
import {login, apiCreateRepo, apiCreateUser, apiUserHeaders, randomString} from './utils.ts';
|
||||
|
||||
test('star and watch a repository', async ({page, request}) => {
|
||||
const owner = `sw-owner-${randomString(8)}`;
|
||||
const repoName = `e2e-star-watch-${randomString(8)}`;
|
||||
await apiCreateUser(request, owner);
|
||||
await Promise.all([
|
||||
apiCreateRepo(request, {name: repoName, autoInit: false, headers: apiUserHeaders(owner)}),
|
||||
login(page),
|
||||
]);
|
||||
await page.goto(`/${owner}/${repoName}`);
|
||||
|
||||
// exact match so "Star"/"Watch" don't also match "Unstar"/"Unwatch"
|
||||
await page.getByRole('button', {name: 'Star', exact: true}).click();
|
||||
await expect(page.getByRole('button', {name: 'Unstar'})).toBeVisible();
|
||||
|
||||
await page.getByRole('button', {name: 'Watch', exact: true}).click();
|
||||
await expect(page.getByRole('button', {name: 'Unwatch'})).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user