forgejo/tests/e2e/user-settings.test.e2e.ts
Mathieu Fenniak aef91ab1a3 ui: move "New access token" to a separate UI page (#11659)
We are updating the user's personal access token page (`/user/settings/applications`) to allow the creation of repo-specific tokens, adding a third option to "Repository and Organization Access".  In preparation for this new UI, this PR moves the creation of access tokens to a new page accessed by "New access token".

This also resolves a pet-peeve: the "Select permissions" dropdown on the inline edit form hides a *required* input for an access token.  This section is expanded on the new dedicated page.  (The Vue component used here is replaced with a JS-free alternative as well.  This form component used to lose selected values when an error occurred, and it didn't make sense as a Vue component, so it has been translated into an HTML template instead.)

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests for Go changes

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Tests for JavaScript changes

- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [x] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11659
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2026-03-18 22:33:14 +01:00

167 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @watch start
// templates/user/settings/**.tmpl
// web_src/css/{form,user}.css
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.ts';
import {screenshot} from './shared/screenshots.ts';
import {validate_form} from './shared/forms.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');
});
test('User: Profile settings', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings');
await page.getByLabel('Full name').fill('SecondUser');
const pronounsInput = page.locator('input[list="pronouns"]');
await expect(pronounsInput).toHaveAttribute('placeholder', 'Unspecified');
await pronounsInput.click();
const pronounsList = page.locator('datalist#pronouns');
const pronounsOptions = pronounsList.locator('option');
const pronounsValues = await pronounsOptions.evaluateAll((opts) => opts.map((opt: HTMLOptionElement) => opt.value));
expect(pronounsValues).toEqual(['he/him', 'she/her', 'they/them', 'it/its', 'any pronouns']);
await pronounsInput.fill('she/her');
await page.getByPlaceholder('Tell others a little bit').fill('I am a playwright test running for several seconds.');
await page.getByPlaceholder('Tell others a little bit').press('Tab');
await page.getByLabel('Website').fill('https://forgejo.org');
await page.getByPlaceholder('Share your approximate').fill('on a computer chip');
await page.getByLabel('User visibility').click();
await page.getByLabel('Visible only to signed-in').click();
await page.getByLabel('Hide email address Email address will').uncheck();
await page.getByLabel('Hide activity from profile').check();
await validate_form({page}, 'fieldset');
await screenshot(page);
await page.getByRole('button', {name: 'Update profile'}).click();
await expect(page.getByText('Your profile has been updated.')).toBeVisible();
await page.getByRole('link', {name: 'public activity'}).click();
await expect(page.getByText('Your activity is only visible')).toBeVisible();
await screenshot(page);
await page.goto('/user2');
await expect(page.getByText('SecondUser')).toBeVisible();
await expect(page.getByText('on a computer chip')).toBeVisible();
await expect(page.locator('li').filter({hasText: 'user2@example.com'})).toBeVisible();
await expect(page.locator('li').filter({hasText: 'https://forgejo.org'})).toBeVisible();
await expect(page.getByText('I am a playwright test')).toBeVisible();
await screenshot(page);
await page.goto('/user/settings');
await page.locator('input[list="pronouns"]').fill('rob/ot');
await page.getByLabel('User visibility').click();
await page.getByLabel('Visible to everyone').click();
await page.getByLabel('Hide email address Email address will').check();
await page.getByLabel('Hide activity from profile').uncheck();
await expect(page.getByText('Your profile has been updated.')).toBeHidden();
await validate_form({page}, 'fieldset');
await screenshot(page);
await page.getByRole('button', {name: 'Update profile'}).click();
await expect(page.getByText('Your profile has been updated.')).toBeVisible();
await page.goto('/user2');
await expect(page.getByText('SecondUser')).toBeVisible();
await expect(page.locator('li').filter({hasText: 'user2@example.com'})).toBeHidden();
await page.goto('/user2?tab=activity');
await expect(page.getByText('Your activity is visible to everyone')).toBeVisible();
});
test('User: Storage overview', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings/storage_overview');
await page.waitForLoadState();
await page.getByLabel('Git LFS 8 KiB').nth(1).hover({position: {x: 250, y: 2}});
await expect(page.getByText('Git LFS 8 KiB')).toBeVisible();
// Show/hide legend by clicking on the bar
await expect(page.locator('.stats ul').nth(1)).toBeHidden();
await expect(page.getByText('Git LFS 8 KiB').nth(1)).toBeHidden();
await page.locator('.stats summary').nth(1).click();
await expect(page.locator('.stats ul').nth(1)).toBeVisible();
await expect(page.getByText('Git LFS 8 KiB').nth(1)).toBeVisible();
await screenshot(page);
await page.locator('.stats summary').nth(1).click();
await expect(page.locator('.stats ul').nth(1)).toBeHidden();
await expect(page.getByText('Git LFS 8 KiB').nth(1)).toBeHidden();
await screenshot(page);
});
test('User: Canceling adding SSH key clears inputs', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings/keys');
await page.locator('#add-ssh-button').click();
await page.getByLabel('Key name').fill('MyAwesomeKey');
await page.locator('#ssh-key-content').fill('Wront key material');
await page.getByRole('button', {name: 'Cancel'}).click();
await page.locator('#add-ssh-button').click();
const keyName = page.getByLabel('Key name');
await expect(keyName).toHaveValue('');
const content = page.locator('#ssh-key-content');
await expect(content).toHaveValue('');
});
test('User: Canceling adding GPG key clears input', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings/keys');
await page.locator('.show-panel[data-panel="#add-gpg-key-panel"]').click();
const gpgKeyContent = page.locator('#gpg-key-content');
await gpgKeyContent.fill('Wront key material');
await page.locator('.hide-panel[data-panel="#add-gpg-key-panel"]').click();
await expect(gpgKeyContent).toHaveValue('');
});
test('User: Add access token', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings/applications');
await page.getByRole('link', {name: 'New access token'}).click();
await page.locator('#scoped-access-submit').click();
await page.locator('#name:invalid').isVisible();
await page.selectOption('#access-token-scope-activitypub', 'read:activitypub');
await page.locator('#scoped-access-submit').click();
await page.locator('#name:invalid').isVisible();
await expect(page.locator('#access-token-scope-activitypub')).toHaveValue('read:activitypub');
const tokenName = globalThis.crypto.randomUUID();
await page.locator('#name').fill(tokenName);
await page.locator('#scoped-access-submit').click();
await page.getByText(tokenName).isVisible();
});
test('User: Add access token validation error', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
await page.goto('/user/settings/applications');
await page.getByRole('link', {name: 'New access token'}).click();
await page.getByRole('button', {name: 'Generate token'}).click();
await page.locator('#name:invalid').isVisible();
await page.getByRole('textbox', {name: 'Token name *'}).fill('Token A');
await page.getByRole('combobox', {name: 'activitypub'}).selectOption('read:activitypub');
await page.getByRole('radio', {name: 'Public only'}).click();
await page.getByRole('button', {name: 'Generate token'}).click();
await page.getByText('has been used as an application name already.').isVisible();
// validate that selected options (public-only, activitypub) are still selected.
await expect(page.getByRole('radio', {name: 'Public only'})).toBeChecked();
await expect(page.getByRole('combobox', {name: 'activitypub'})).toHaveValue('read:activitypub');
});