forgejo/tests/e2e/issue-sidebar.test.e2e.ts
Otto Richter 40551de313 tests(e2e): Refactor various tests
Goals:

- speedup
- less flakiness
- best practices and more use
- documentation

config:
- sync ports in Makefile and playwright config
  (otherwise, some tests fail locally because they assert the full URL including the (wrong) port)
- even more generous timeouts
- limit workers to one again (because I finally understand how
  Playwright works)
- allow nested functions to group them together with the related test

all:

- deprecate waitForLoadState('networkidle')
  - it is discouraged as per https://playwright.dev/docs/api/class-page#page-wait-for-load-state
  - I could not find a usage that seems to require it actually (see
    added documentation in README)
  - adding an exception should be made explicitly
  - it does not do what you might expect anyway in most cases
- only log in when necessary

webauthn:

- verify that login is possible after disabling key
- otherwise, the cleanup was not necessary after the previous refactor to create a fresh user each

issue-sidebar / WIP toggle:

- split into smaller chunks
- restore original state first
- add missed assertion to fix race condition (not waiting
  before state was reached)
- explicitly toggle the state to detect mismatch earlier

issue-sidebar / labels:

- restore original state first
- better waiting for background request
2024-11-13 13:15:37 +01:00

272 lines
13 KiB
TypeScript

// @watch start
// templates/repo/issue/view_content/**
// web_src/css/repo/issue-**
// web_src/js/features/repo-issue**
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');
});
/* eslint-disable playwright/expect-expect */
// some tests are reported to have no assertions,
// which is not correct, because they use the global helper function
test.describe('Pull: Toggle WIP', () => {
const prTitle = 'pull5';
async function toggle_wip_to({page}, should) {
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) {
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 ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
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 ({browser}, 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 page = await login({browser}, workerInfo);
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 ({browser}, 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 page = await login({browser}, workerInfo);
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 ({browser}, 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 page = await login({browser}, workerInfo);
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);
});
});
/* eslint-enable playwright/expect-expect */
test('Issue: Labels', async ({browser}, 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}) {
const submitted = page.waitForResponse('/user2/repo1/issues/labels');
await page.locator('textarea').first().click(); // close via unrelated element
await submitted;
await page.waitForLoadState();
}
const page = await login({browser}, workerInfo);
// 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 ({browser}, 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 page = await login({browser}, workerInfo);
// 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 ({browser}, 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 page = await login({browser}, workerInfo);
// 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.type('.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();
// 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();
});
test('Issue: Milestone', async ({browser}, 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 page = await login({browser}, workerInfo);
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 ({browser}, 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 page = await login({browser}, workerInfo);
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');
// Add milestone.
await milestoneDropdown.click();
await page.getByRole('option', {name: 'milestone1'}).click();
await expect(selectedMilestone).toContainText('milestone1');
// Clear milestone.
await milestoneDropdown.click();
await page.getByText('Clear milestone', {exact: true}).click();
await expect(selectedMilestone).toContainText('No milestone');
});