mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-05 00:09:33 +09:00
Enable djlint's [H018](https://djlint.com/docs/linter/#h018), [H046](https://djlint.com/docs/linter/#h046) and [H054](https://djlint.com/docs/linter/#h054) rules, fix their findings and the keyboard navigation problems around them. H054 found links nested in `<button>` dropdowns and radio inputs nested in links, both invalid HTML. - "Add File" and "More Operations" in the repo file list become `div` dropdowns, the disabled state stays unfocusable - The diff whitespace menu marks the current option with a check and `aria-current` instead of radios, which made our dropdown aria patch announce it as a combobox - Sign-in moves "Forgot password?" below "Sign In" and drops the positive tabindexes from https://github.com/go-gitea/gitea/pull/31689, so the tab order follows the DOM: username, password, remember, sign in, forgot - The focus ring on primary, red and green buttons is near-white on dark themes and near-black on light ones, the primary-colored ring was invisible on primary buttons <img width="500" alt="Whitespace menu, before and after" src="https://github.com/user-attachments/assets/d22a423d-95e5-434c-aec4-59264c7132b7" /> <img width="600" alt="Sign-in form, main and this PR" src="https://github.com/user-attachments/assets/d6780690-d906-4791-8a2b-8d80fb03067a" /> <img width="215" height="132" alt="Keyboard-focused primary and red buttons, dark and light theme" src="https://github.com/user-attachments/assets/a2400b18-4802-4383-8560-7e8267a7f02f" /> --------- Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
import {platform} from 'node:process';
|
|
import {test, expect, type Page} from '@playwright/test';
|
|
import {apiCreateUser, loginUser, randomString, testUserPassword} from './utils.ts';
|
|
|
|
const signedIn = /^(?!.*\/user\/(login|webauthn))/; // the target of a finished login varies
|
|
|
|
async function registerKey(page: Page, nickname: string) {
|
|
await page.goto('/user/settings/security');
|
|
await page.getByRole('textbox', {name: 'Nickname'}).fill(nickname);
|
|
await page.getByRole('button', {name: 'Add Security Key'}).click();
|
|
}
|
|
|
|
async function signInWithPassword(page: Page, username: string) {
|
|
await page.context().clearCookies();
|
|
await page.goto('/user/login');
|
|
await page.getByLabel('Username or Email Address').fill(username);
|
|
await page.getByLabel('Password').fill(testUserPassword);
|
|
await page.getByRole('button', {name: 'Sign In', exact: true}).click();
|
|
}
|
|
|
|
// regression: credProtect level 3 hid the credential from the second-factor login
|
|
test('security key survives credProtect', async ({page, request, browserName}) => {
|
|
test.skip(browserName !== 'chromium', 'only the CDP authenticator emulates credProtect'); // eslint-disable-line playwright/no-skipped-test -- conditional skip, the reason is in the message
|
|
|
|
const username = `e2e-credprotect-${randomString(8)}`;
|
|
await apiCreateUser(request, username);
|
|
|
|
const cdp = await page.context().newCDPSession(page);
|
|
await cdp.send('WebAuthn.enable');
|
|
await cdp.send('WebAuthn.addVirtualAuthenticator', {options: {
|
|
protocol: 'ctap2',
|
|
ctap2Version: 'ctap2_1',
|
|
transport: 'usb',
|
|
hasResidentKey: true,
|
|
hasUserVerification: true,
|
|
hasCredBlob: true, // CDP only emulates credProtect together with credBlob
|
|
isUserVerified: true,
|
|
}});
|
|
|
|
await loginUser(page, username);
|
|
await registerKey(page, 'e2e-key');
|
|
await expect(page.getByText('e2e-key')).toBeVisible();
|
|
|
|
await registerKey(page, 'e2e-key-again');
|
|
await expect(page.locator('#webauthn-error-msg')).toContainText('already registered');
|
|
|
|
await signInWithPassword(page, username);
|
|
await expect(page).toHaveURL(signedIn);
|
|
});
|
|
|
|
// this authenticator has no credProtect, so it cannot replace the test above
|
|
test('security key signs in as second factor and as passkey', async ({page, request, browserName}) => {
|
|
test.skip(browserName === 'webkit' && platform === 'linux', 'linux webkit has no webauthn, playwright only stubs PublicKeyCredential'); // eslint-disable-line playwright/no-skipped-test -- conditional skip, the reason is in the message
|
|
|
|
const username = `e2e-passkey-${randomString(8)}`;
|
|
await apiCreateUser(request, username);
|
|
await page.context().credentials.install();
|
|
|
|
await loginUser(page, username);
|
|
await registerKey(page, 'e2e-key');
|
|
await expect(page.getByText('e2e-key')).toBeVisible();
|
|
|
|
await signInWithPassword(page, username);
|
|
await expect(page).toHaveURL(signedIn);
|
|
|
|
await page.context().clearCookies();
|
|
await page.goto('/user/login');
|
|
await page.getByText('Sign in with a passkey').click();
|
|
await expect(page).toHaveURL(signedIn);
|
|
});
|