mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-04 23:04:24 +00:00
516a4883fa
Clean up the navbar template and styling, and fix the navbar stopwatch, which navigated to the issue instead of opening its popup since https://github.com/go-gitea/gitea/pull/36965. 1. Add hover background to the create and user menus 2. Simplify navbar HTML and CSS and remove Fomantic styles 3. Render the notification and stopwatch icons once instead of separate mobile and desktop copies 4. Make the stopwatch a keyboard accessible button whose popup updates on push and closes when the stopwatch stops Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: bircni <bircni@icloud.com>
143 lines
6.1 KiB
TypeScript
143 lines
6.1 KiB
TypeScript
import {test, expect} from '@playwright/test';
|
|
import {loginUser, baseUrl, apiUserHeaders, apiCreateUser, apiCreateRepo, apiCreateIssue, apiStartStopwatch, apiCloseIssue, randomString} from './utils.ts';
|
|
|
|
// The /-/ws WebSocket pipeline is push-only: every event is fired by the server
|
|
// immediately on the DB write. These tests exercise that each event type
|
|
// (notification-count, stopwatches, logout) reaches a connected tab.
|
|
test.describe('events', () => {
|
|
test('notification count increases on new notification', async ({page, request}) => {
|
|
const owner = `ev-notif-owner-${randomString(8)}`;
|
|
const commenter = `ev-notif-commenter-${randomString(8)}`;
|
|
const repoName = `ev-notif-${randomString(8)}`;
|
|
|
|
await Promise.all([apiCreateUser(request, owner), apiCreateUser(request, commenter)]);
|
|
|
|
await Promise.all([
|
|
apiCreateRepo(request, {name: repoName, autoInit: false, headers: apiUserHeaders(owner)}),
|
|
loginUser(page, owner),
|
|
]);
|
|
await page.goto('/');
|
|
const badge = page.locator('#navbar .notification_count');
|
|
await expect(badge).toBeHidden();
|
|
|
|
await expect(page.locator('html[data-user-events-connected]')).toBeAttached();
|
|
|
|
await apiCreateIssue(request, {owner, repo: repoName, title: 'events-notif', headers: apiUserHeaders(commenter)});
|
|
await expect(badge).toBeVisible();
|
|
});
|
|
|
|
test('stopwatch appears via real-time push and stops from its popup', async ({page, request}) => {
|
|
const name = `ev-sw-push-${randomString(8)}`;
|
|
const headers = apiUserHeaders(name);
|
|
|
|
await apiCreateUser(request, name);
|
|
await Promise.all([
|
|
loginUser(page, name),
|
|
(async () => {
|
|
await apiCreateRepo(request, {name, headers});
|
|
await apiCreateIssue(request, {owner: name, repo: name, title: 'events stopwatch push test', headers});
|
|
})(),
|
|
]);
|
|
// Page loads before the stopwatch starts — the icon is hidden in the rendered HTML
|
|
await page.goto('/');
|
|
const stopwatch = page.getByTitle('Active Time Tracker');
|
|
// Element must exist in the DOM (just hidden); otherwise the push has nothing to reveal.
|
|
await expect(stopwatch).toHaveCount(1);
|
|
await expect(stopwatch).toBeHidden();
|
|
|
|
await expect(page.locator('html[data-user-events-connected]')).toBeAttached();
|
|
|
|
await apiStartStopwatch(request, name, name, 1, {headers});
|
|
await expect(stopwatch).toBeVisible();
|
|
|
|
await stopwatch.click();
|
|
await page.getByRole('button', {name: 'Stop Timer'}).click();
|
|
await expect(stopwatch).toBeHidden();
|
|
});
|
|
|
|
// Closing an issue stops the timer away from any stopwatch route handler.
|
|
test('stopwatch renders when already active and hides with its popup when the issue is closed', async ({page, request}) => {
|
|
const name = `ev-sw-close-${randomString(8)}`;
|
|
const headers = apiUserHeaders(name);
|
|
|
|
await apiCreateUser(request, name);
|
|
await Promise.all([
|
|
loginUser(page, name),
|
|
(async () => {
|
|
await apiCreateRepo(request, {name, autoInit: false, headers});
|
|
await apiCreateIssue(request, {owner: name, repo: name, title: 'events stopwatch close test', headers});
|
|
await apiStartStopwatch(request, name, name, 1, {headers});
|
|
})(),
|
|
]);
|
|
await page.goto('/');
|
|
const stopwatch = page.getByTitle('Active Time Tracker');
|
|
await expect(stopwatch).toBeVisible();
|
|
await expect(page.locator('html[data-user-events-connected]')).toBeAttached();
|
|
await stopwatch.click();
|
|
const stopButton = page.getByRole('button', {name: 'Stop Timer'});
|
|
await expect(stopButton).toBeVisible();
|
|
|
|
await apiCloseIssue(request, name, name, 1, {headers});
|
|
await expect(stopwatch).toBeHidden();
|
|
await expect(stopButton).toBeHidden();
|
|
});
|
|
|
|
// Repro for https://github.com/go-gitea/gitea/pull/36965#issuecomment-4321282667:
|
|
// clicking the sidebar "Start timer" button reportedly produced a blank page.
|
|
// Drives the actual UI button (not the API) so the link-action → JSONRedirect("")
|
|
// → fetchActionDoRedirect("") path is exercised end-to-end.
|
|
test('sidebar start timer button starts stopwatch without blanking the page', async ({page, request}) => {
|
|
const name = `ev-sw-ui-${randomString(8)}`;
|
|
const headers = apiUserHeaders(name);
|
|
|
|
await apiCreateUser(request, name);
|
|
await Promise.all([
|
|
loginUser(page, name),
|
|
(async () => {
|
|
await apiCreateRepo(request, {name, headers});
|
|
await apiCreateIssue(request, {owner: name, repo: name, title: 'sidebar start timer test', headers});
|
|
})(),
|
|
]);
|
|
await page.goto(`/${name}/${name}/issues/1`);
|
|
|
|
await page.getByRole('button', {name: 'Start timer'}).click();
|
|
|
|
// After the click the page reloads; the sidebar should now show the Stop/Discard
|
|
// controls and the navbar stopwatch icon should appear. If the page blanked,
|
|
// neither of these would be present.
|
|
await expect(page.getByRole('button', {name: 'Stop timer'})).toBeVisible();
|
|
await expect(page.getByRole('button', {name: 'Discard timer'})).toBeVisible();
|
|
await expect(page.getByTitle('Active Time Tracker')).toBeVisible();
|
|
});
|
|
|
|
test('logout propagation', async ({browser, request}) => {
|
|
const name = `ev-logout-${randomString(8)}`;
|
|
|
|
await apiCreateUser(request, name);
|
|
|
|
// Use a single context so both pages share the same session and SharedWorker
|
|
const context = await browser.newContext({baseURL: baseUrl()});
|
|
const page1 = await context.newPage();
|
|
const page2 = await context.newPage();
|
|
|
|
await loginUser(page1, name);
|
|
|
|
// Navigate page2 so it connects to the shared event stream
|
|
await page2.goto('/');
|
|
|
|
// Verify page2 is logged in
|
|
await expect(page2.getByRole('link', {name: 'Sign In'})).toBeHidden();
|
|
// Wait until page2's event stream is connected, otherwise the logout event
|
|
// can race the connection and be silently dropped.
|
|
await expect(page2.locator('html[data-user-events-connected]')).toBeAttached();
|
|
|
|
// Logout from page1 — this sends a logout event to all tabs
|
|
await page1.goto('/user/logout');
|
|
|
|
// page2 should be redirected via the logout event
|
|
await expect(page2.getByRole('link', {name: 'Sign In'})).toBeVisible();
|
|
|
|
await context.close();
|
|
});
|
|
});
|