mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-07-07 07:00:01 +02:00

- Fix issue dropdown breaking when currently selected issue is included in results. - Add `sort` parameter to `/issues/search` API. - Sort dropdown by relevance. - Make priority_repo_id work again. - Added E2E test. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8003 Reviewed-by: Shiny Nematoda <snematoda@noreply.codeberg.org> Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Danko Aleksejevs <danko@very.lv> Co-committed-by: Danko Aleksejevs <danko@very.lv>
352 lines
16 KiB
TypeScript
352 lines
16 KiB
TypeScript
// @watch start
|
|
// templates/repo/issue/view_content/**
|
|
// web_src/css/repo/issue-**
|
|
// web_src/js/features/repo-issue**
|
|
// @watch end
|
|
|
|
/* eslint playwright/expect-expect: ["error", { "assertFunctionNames": ["check_wip"] }] */
|
|
|
|
import {expect, type Page} from '@playwright/test';
|
|
import {save_visual, test} from './utils_e2e.ts';
|
|
|
|
test.use({user: 'user2'});
|
|
|
|
test.describe('Pull: Toggle WIP', () => {
|
|
const prTitle = 'pull5';
|
|
|
|
async function toggle_wip_to({page}, should: boolean) {
|
|
await page.waitForLoadState('domcontentloaded');
|
|
if (should) {
|
|
await page.getByText('Still in progress?').click();
|
|
} else {
|
|
await page.getByText('Ready for review?').click();
|
|
}
|
|
}
|
|
|
|
async function check_wip({page}, is: boolean) {
|
|
const elemTitle = 'h1';
|
|
const stateLabel = '.issue-state-label';
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await expect(page.locator(elemTitle)).toContainText(prTitle);
|
|
await expect(page.locator(elemTitle)).toContainText('#5');
|
|
if (is) {
|
|
await expect(page.locator(elemTitle)).toContainText('WIP');
|
|
await expect(page.locator(stateLabel)).toContainText('Draft');
|
|
} else {
|
|
await expect(page.locator(elemTitle)).not.toContainText('WIP');
|
|
await expect(page.locator(stateLabel)).toContainText('Open');
|
|
}
|
|
}
|
|
|
|
test.beforeEach(async ({page}) => {
|
|
const response = await page.goto('/user2/repo1/pulls/5');
|
|
expect(response?.status()).toBe(200); // Status OK
|
|
// ensure original title
|
|
await page.locator('#issue-title-edit-show').click();
|
|
await page.locator('#issue-title-editor input').fill(prTitle);
|
|
await page.getByText('Save').click();
|
|
await check_wip({page}, false);
|
|
});
|
|
|
|
test('simple toggle', async ({page}, workerInfo) => {
|
|
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');
|
|
// toggle to WIP
|
|
await toggle_wip_to({page}, true);
|
|
await check_wip({page}, true);
|
|
// remove WIP
|
|
await toggle_wip_to({page}, false);
|
|
await check_wip({page}, false);
|
|
});
|
|
|
|
test('manual edit', async ({page}, workerInfo) => {
|
|
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');
|
|
// manually edit title to another prefix
|
|
await page.locator('#issue-title-edit-show').click();
|
|
await page.locator('#issue-title-editor input').fill(`[WIP] ${prTitle}`);
|
|
await page.getByText('Save').click();
|
|
await check_wip({page}, true);
|
|
// remove again
|
|
await toggle_wip_to({page}, false);
|
|
await check_wip({page}, false);
|
|
});
|
|
|
|
test('maximum title length', async ({page}, workerInfo) => {
|
|
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');
|
|
// check maximum title length is handled gracefully
|
|
const maxLenStr = prTitle + 'a'.repeat(240);
|
|
await page.locator('#issue-title-edit-show').click();
|
|
await page.locator('#issue-title-editor input').fill(maxLenStr);
|
|
await page.getByText('Save').click();
|
|
await expect(page.locator('h1')).toContainText(maxLenStr);
|
|
await check_wip({page}, false);
|
|
await toggle_wip_to({page}, true);
|
|
await check_wip({page}, true);
|
|
await expect(page.locator('h1')).toContainText(maxLenStr);
|
|
await toggle_wip_to({page}, false);
|
|
await check_wip({page}, false);
|
|
await expect(page.locator('h1')).toContainText(maxLenStr);
|
|
});
|
|
});
|
|
|
|
test('Issue: Labels', async ({page}, workerInfo) => {
|
|
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}) {
|
|
const submitted = page.waitForResponse('/user2/repo1/issues/labels');
|
|
await page.locator('textarea').first().click(); // close via unrelated element
|
|
await submitted;
|
|
await page.waitForLoadState();
|
|
}
|
|
|
|
// select label list in sidebar only
|
|
const labelList = page.locator('.issue-content-right .labels-list a');
|
|
const response = await page.goto('/user2/repo1/issues/1');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
// restore initial state
|
|
await page.locator('.select-label').click();
|
|
const responsePromise = page.waitForResponse('/user2/repo1/issues/labels');
|
|
await page.getByText('Clear labels').click();
|
|
await responsePromise;
|
|
await expect(labelList.filter({hasText: 'label1'})).toBeHidden();
|
|
await expect(labelList.filter({hasText: 'label2'})).toBeHidden();
|
|
|
|
// add both labels
|
|
await page.locator('.select-label').click();
|
|
// label search could be tested this way:
|
|
// await page.locator('.select-label input').fill('label2');
|
|
await page.locator('.select-label .item').filter({hasText: 'label2'}).click();
|
|
await page.locator('.select-label .item').filter({hasText: 'label1'}).click();
|
|
await submitLabels({page});
|
|
await expect(labelList.filter({hasText: 'label2'})).toBeVisible();
|
|
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
|
|
|
|
// test removing label2 again
|
|
// due to a race condition, the page could still be "reloading",
|
|
// closing the dropdown after it was clicked.
|
|
// Retry the interaction as a group
|
|
// also see https://playwright.dev/docs/test-assertions#expecttopass
|
|
await expect(async () => {
|
|
await page.locator('.select-label').click();
|
|
await page.locator('.select-label .item').filter({hasText: 'label2'}).click();
|
|
}).toPass();
|
|
await submitLabels({page});
|
|
await expect(labelList.filter({hasText: 'label2'})).toBeHidden();
|
|
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
|
|
});
|
|
|
|
test('Issue: Assignees', async ({page}, workerInfo) => {
|
|
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
|
|
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a');
|
|
|
|
const response = await page.goto('/org3/repo3/issues/1');
|
|
expect(response?.status()).toBe(200);
|
|
// Clear all assignees
|
|
await page.locator('.select-assignees-modify.dropdown').click();
|
|
await page.locator('.select-assignees-modify.dropdown .no-select.item').click();
|
|
await expect(assigneesList.filter({hasText: 'user2'})).toBeHidden();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible();
|
|
await expect(page.locator('.select-assign-me')).toBeVisible();
|
|
|
|
// Assign other user (with searchbox)
|
|
await page.locator('.select-assignees-modify.dropdown').click();
|
|
await page.type('.select-assignees-modify .menu .search input', 'user4');
|
|
await expect(page.locator('.select-assignees-modify .menu .item').filter({hasText: 'user2'})).toBeHidden();
|
|
await expect(page.locator('.select-assignees-modify .menu .item').filter({hasText: 'user4'})).toBeVisible();
|
|
await page.locator('.select-assignees-modify .menu .item').filter({hasText: 'user4'}).click();
|
|
await page.locator('.select-assignees-modify.dropdown').click();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeVisible();
|
|
|
|
// remove user4
|
|
await page.locator('.select-assignees-modify.dropdown').click();
|
|
await page.locator('.select-assignees-modify .menu .item').filter({hasText: 'user4'}).click();
|
|
await page.locator('.select-assignees-modify.dropdown').click();
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
|
|
|
|
// Test assign me
|
|
await page.locator('.ui.assignees .select-assign-me').click();
|
|
await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible();
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
|
|
});
|
|
|
|
test('New Issue: Assignees', async ({page}, workerInfo) => {
|
|
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
|
|
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item');
|
|
|
|
const response = await page.goto('/org3/repo3/issues/new');
|
|
expect(response?.status()).toBe(200);
|
|
// preconditions
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible();
|
|
await expect(assigneesList.filter({hasText: 'user2'})).toBeHidden();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
|
|
|
|
// Assign other user (with searchbox)
|
|
await page.locator('.select-assignees.dropdown').click();
|
|
await page.fill('.select-assignees .menu .search input', 'user4');
|
|
await expect(page.locator('.select-assignees .menu .item').filter({hasText: 'user2'})).toBeHidden();
|
|
await expect(page.locator('.select-assignees .menu .item').filter({hasText: 'user4'})).toBeVisible();
|
|
await page.locator('.select-assignees .menu .item').filter({hasText: 'user4'}).click();
|
|
await page.locator('.select-assignees.dropdown').click();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeVisible();
|
|
await save_visual(page);
|
|
|
|
// remove user4
|
|
await page.locator('.select-assignees.dropdown').click();
|
|
await page.locator('.select-assignees .menu .item').filter({hasText: 'user4'}).click();
|
|
await page.locator('.select-assignees.dropdown').click();
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible();
|
|
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
|
|
|
|
// Test assign me
|
|
await page.locator('.ui.assignees .select-assign-me').click();
|
|
await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible();
|
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
|
|
|
|
await page.locator('.select-assignees.dropdown').click();
|
|
await page.fill('.select-assignees .menu .search input', '');
|
|
await page.locator('.select-assignees.dropdown .no-select.item').click();
|
|
await expect(page.locator('.select-assign-me')).toBeVisible();
|
|
await save_visual(page);
|
|
});
|
|
|
|
test('Issue: Milestone', async ({page}, workerInfo) => {
|
|
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');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const selectedMilestone = page.locator('.issue-content-right .select-milestone.list');
|
|
const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown');
|
|
await expect(selectedMilestone).toContainText('No milestone');
|
|
|
|
// Add milestone.
|
|
await milestoneDropdown.click();
|
|
await page.getByRole('option', {name: 'milestone1'}).click();
|
|
await expect(selectedMilestone).toContainText('milestone1');
|
|
await expect(page.locator('.timeline-item.event').last()).toContainText('user2 added this to the milestone1 milestone');
|
|
|
|
// Clear milestone.
|
|
await milestoneDropdown.click();
|
|
await page.getByText('Clear milestone', {exact: true}).click();
|
|
await expect(selectedMilestone).toContainText('No 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.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');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const selectedMilestone = page.locator('.issue-content-right .select-milestone.list');
|
|
const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown');
|
|
await expect(selectedMilestone).toContainText('No milestone');
|
|
await save_visual(page);
|
|
|
|
// Add milestone.
|
|
await milestoneDropdown.click();
|
|
await page.getByRole('option', {name: 'milestone1'}).click();
|
|
await expect(selectedMilestone).toContainText('milestone1');
|
|
await save_visual(page);
|
|
|
|
// Clear milestone.
|
|
await milestoneDropdown.click();
|
|
await page.getByText('Clear milestone', {exact: true}).click();
|
|
await expect(selectedMilestone).toContainText('No milestone');
|
|
await save_visual(page);
|
|
});
|
|
|
|
test.describe('Dependency dropdown', () => {
|
|
test.use({user: 'user11'});
|
|
test('Issue: Dependencies', async ({page}) => {
|
|
const response = await page.goto('/user11/dependency-test/issues/3');
|
|
expect(response?.status()).toBe(200);
|
|
|
|
const depsBlock = page.locator('.issue-content-right .depending');
|
|
const deleteDepBtn = page.locator('.issue-content-right .depending .delete-dependency-button');
|
|
|
|
const input = page.locator('#new-dependency-drop-list .search');
|
|
const current = page.locator('#new-dependency-drop-list .text').first();
|
|
const menu = page.locator('#new-dependency-drop-list .menu');
|
|
const items = page.locator('#new-dependency-drop-list .menu .item');
|
|
|
|
const confirmDelete = async () => {
|
|
const modal = page.locator('.modal.remove-dependency');
|
|
await expect(modal).toBeVisible();
|
|
await expect(modal).toContainText('This will remove the dependency from this issue');
|
|
await modal.locator('button.ok').click();
|
|
};
|
|
|
|
// A kludge to set the dropdown to the *wrong* value so it lets us select the correct one next.
|
|
const resetDropdown = async () => {
|
|
if (await current.textContent().then((s) => s.includes('#4'))) return;
|
|
await input.click();
|
|
await input.fill('unrelated');
|
|
await expect(items.first()).toContainText('unrelated');
|
|
await items.first().click();
|
|
await expect(current).toContainText('#4');
|
|
await input.click();
|
|
};
|
|
|
|
await expect(depsBlock).toBeVisible();
|
|
while (await deleteDepBtn.first().isVisible()) {
|
|
await deleteDepBtn.first().click(); // wipe added dependencies from any previously failed tests
|
|
await confirmDelete();
|
|
}
|
|
await expect(depsBlock).toContainText('No dependencies set');
|
|
|
|
await input.scrollIntoViewIfNeeded();
|
|
await input.click();
|
|
|
|
const first = 'first issue here';
|
|
const second = 'second issue here';
|
|
const newest = 'newest issue';
|
|
|
|
// Without query, it should show issues in the same repo, sorted by date, except current one.
|
|
await expect(menu).toBeVisible();
|
|
await expect(items).toHaveCount(4); // 5 issues in this repo, minus current one
|
|
await expect(items.first()).toContainText(newest);
|
|
await expect(items.last()).toContainText(first);
|
|
await resetDropdown();
|
|
|
|
// With query, it should search all repos, but show current repo issues first.
|
|
await input.fill('right');
|
|
await expect(items.first()).toContainText(second);
|
|
await expect.poll(() => items.count()).toBeGreaterThan(1); // there is an issue in user11/dependency-test-2 containing the word "right"
|
|
await resetDropdown();
|
|
|
|
// When entering an issue number, it should always show that one first, then all text matches.
|
|
await input.fill('1');
|
|
await expect(items.first()).toContainText(first);
|
|
await expect(items.nth(1)).toBeVisible();
|
|
await resetDropdown();
|
|
|
|
// Should behave the same with a prefix
|
|
await input.fill('#1');
|
|
await expect(items.first()).toContainText(first);
|
|
|
|
// Selecting an issue
|
|
await items.first().click();
|
|
await expect(current).toContainText(first);
|
|
|
|
// Add dependency
|
|
const link = page.locator('.issue-content-right .depending .dependency a.title');
|
|
await page.locator('.issue-content-right .depending button').click();
|
|
await expect(link).toHaveAttribute('href', '/user11/dependency-test/issues/1');
|
|
|
|
// Remove dependency
|
|
await expect(deleteDepBtn).toBeVisible();
|
|
await deleteDepBtn.click();
|
|
|
|
await confirmDelete();
|
|
|
|
await expect(depsBlock).toContainText('No dependencies set');
|
|
});
|
|
});
|