mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-10 18:35:17 +00:00
A release entry now opens with a short list of highlights above its first section. The What's new sheet, the website changelog and the GitHub release body show those and keep the sections, the full record, behind one control, so a 100-bullet release reads as ten lines. CHANGELOG.md stays plain Markdown because the app and the website render it without HTML, including apps already installed. Each surface folds the sections itself; only the release body gets a <details> block, written by Release Notes Sync. A release with nothing before its first section shows its sections as before. Adds the 0.16.0-beta.1 entry in the new format, with its Chinese translation, and the highlight writing rules in docs/release.md.
235 lines
9.0 KiB
TypeScript
235 lines
9.0 KiB
TypeScript
import { expect, test, type Page } from "../support/fixtures";
|
|
import { gotoAppShell, openSettings } from "../support/helpers/app";
|
|
import { openSettingsSection } from "../support/helpers/settings";
|
|
import {
|
|
allChangesToggle,
|
|
hideAllChanges,
|
|
openWhatsNew,
|
|
release,
|
|
serveChangelog,
|
|
showAllChanges,
|
|
} from "../support/helpers/changelog";
|
|
|
|
const DISCORD_DESTINATION =
|
|
/^https:\/\/(?:discord\.gg\/jz8T2uahpH|discord\.com\/invite\/jz8T2uahpH)(?:[/?#]|$)/;
|
|
const GITHUB_ISSUE_DESTINATION =
|
|
/^https:\/\/github\.com\/(?:getpaseo\/paseo\/issues\/new(?:\/choose)?(?:[/?#]|$)|login\?return_to=https%3A%2F%2Fgithub\.com%2Fgetpaseo%2Fpaseo%2Fissues%2Fnew$)/;
|
|
const CHANGELOG_DESTINATION = /^https:\/\/paseo\.sh\/changelog(?:[/?#]|$)/;
|
|
// The name and the version are separate cells of a key/value row, so they meet with no space
|
|
// between them in the row's text content.
|
|
const APP_VERSION = /^Paseo\s*v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
|
|
async function openHelpMenu(page: Page): Promise<void> {
|
|
await page.getByTestId("sidebar-help").click();
|
|
await expect(page.getByTestId("sidebar-help-menu")).toBeVisible();
|
|
}
|
|
|
|
async function expectDiagnosticReport(page: Page): Promise<void> {
|
|
const sheet = page.getByTestId("app-diagnostic-sheet");
|
|
await expect(sheet).toBeVisible();
|
|
await expect(sheet.getByRole("button", { name: "Copy diagnostic" })).toBeEnabled();
|
|
await expect(page.getByText(/App version:/).first()).toBeVisible();
|
|
}
|
|
|
|
async function closeSheet(page: Page, testID: string): Promise<void> {
|
|
const sheet = page.getByTestId(testID);
|
|
await sheet.getByLabel("Close").click();
|
|
await expect(sheet).not.toBeVisible();
|
|
}
|
|
|
|
async function expectExternalPage(
|
|
page: Page,
|
|
actionTestID: string,
|
|
expectedUrl: RegExp,
|
|
): Promise<void> {
|
|
const popupPromise = page.waitForEvent("popup");
|
|
await page.getByTestId(actionTestID).click();
|
|
const popup = await popupPromise;
|
|
expect(popup.url()).toMatch(expectedUrl);
|
|
await popup.close();
|
|
}
|
|
|
|
test("opens troubleshooting and support destinations", async ({ page }) => {
|
|
await gotoAppShell(page);
|
|
await expect(page.getByTestId("sidebar-help")).toBeVisible();
|
|
|
|
await test.step("opens diagnostics and keyboard shortcuts", async () => {
|
|
await openHelpMenu(page);
|
|
await expect(page.getByText("Help", { exact: true })).toBeVisible();
|
|
await expect(page.getByText("Report an issue", { exact: true })).toBeVisible();
|
|
await expect(page.getByText("What's new", { exact: true })).toBeVisible();
|
|
await expect(page.getByTestId("sidebar-help-version")).toHaveText(APP_VERSION);
|
|
|
|
await page.getByTestId("sidebar-help-diagnostics").click();
|
|
await expectDiagnosticReport(page);
|
|
await closeSheet(page, "app-diagnostic-sheet");
|
|
|
|
await openHelpMenu(page);
|
|
await page.getByTestId("sidebar-help-shortcuts").click();
|
|
await expect(page.getByTestId("keyboard-shortcuts-dialog")).toBeVisible();
|
|
await closeSheet(page, "keyboard-shortcuts-dialog");
|
|
});
|
|
|
|
await test.step("opens support pages", async () => {
|
|
await openHelpMenu(page);
|
|
await expectExternalPage(page, "sidebar-help-discord", DISCORD_DESTINATION);
|
|
|
|
await openHelpMenu(page);
|
|
await expectExternalPage(page, "sidebar-help-github", GITHUB_ISSUE_DESTINATION);
|
|
});
|
|
});
|
|
|
|
test("renders the changelog in the app and links the website", async ({ page }) => {
|
|
// Highlights, a section name the app has never seen, a callout, and a fenced
|
|
// sample whose contents look like a release heading.
|
|
await serveChangelog(page, [
|
|
"# Changelog",
|
|
"",
|
|
"## 9.1.0 - 2026-03-04",
|
|
"",
|
|
"- **Background tasks:** Paseo now shows the tasks an agent is running.",
|
|
"",
|
|
"### Sparkles",
|
|
"",
|
|
"- Added a brand new thing",
|
|
"",
|
|
"> [!WARNING]",
|
|
"> Read this before upgrading.",
|
|
"",
|
|
"```md",
|
|
"## 0.0.0 - 1999-01-01",
|
|
"```",
|
|
"",
|
|
"### Fixed",
|
|
"",
|
|
"- Fixed a crash",
|
|
"",
|
|
"## 9.0.0 - 2026-02-01",
|
|
"",
|
|
"### Fixed",
|
|
"",
|
|
"- Fixed an older thing",
|
|
"",
|
|
]);
|
|
await gotoAppShell(page);
|
|
|
|
const sheet = await openWhatsNew(page);
|
|
const latest = release(sheet, "9.1.0");
|
|
const older = release(sheet, "9.0.0");
|
|
|
|
await test.step("leads a release with its highlights and tucks the rest away", async () => {
|
|
await expect(latest.getByText("March 4, 2026", { exact: true })).toBeVisible();
|
|
await expect(latest.getByText("Paseo now shows the tasks an agent is running.")).toBeVisible();
|
|
await expect(latest.getByText("Added a brand new thing")).toHaveCount(0);
|
|
await expect(latest.getByText("Fixed a crash")).toHaveCount(0);
|
|
});
|
|
|
|
await test.step("shows every section on request and tucks them away again", async () => {
|
|
await showAllChanges(latest);
|
|
await expect(latest.getByText("Sparkles", { exact: true })).toBeVisible();
|
|
await expect(latest.getByText("Added a brand new thing")).toBeVisible();
|
|
await expect(latest.getByText("Read this before upgrading.")).toBeVisible();
|
|
await expect(latest.getByText("Fixed a crash")).toBeVisible();
|
|
await expect(release(sheet, "0.0.0")).toHaveCount(0);
|
|
|
|
await hideAllChanges(latest);
|
|
await expect(latest.getByText("Added a brand new thing")).toHaveCount(0);
|
|
await expect(latest.getByText("Paseo now shows the tasks an agent is running.")).toBeVisible();
|
|
});
|
|
|
|
await test.step("shows a release without highlights whole", async () => {
|
|
await expect(older.getByText("Fixed an older thing")).toBeVisible();
|
|
await expect(allChangesToggle(older)).toHaveCount(0);
|
|
});
|
|
|
|
await expectExternalPage(page, "changelog-open-website", CHANGELOG_DESTINATION);
|
|
await closeSheet(page, "changelog-sheet");
|
|
});
|
|
|
|
test("searches keyboard shortcuts from the sidebar help menu", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(navigator, "platform", { get: () => "MacIntel" });
|
|
});
|
|
await gotoAppShell(page);
|
|
await openHelpMenu(page);
|
|
await page.getByTestId("sidebar-help-shortcuts").click();
|
|
|
|
const dialog = page.getByTestId("keyboard-shortcuts-dialog");
|
|
const search = page.getByPlaceholder("Search shortcuts");
|
|
|
|
await search.fill("command+n");
|
|
await expect(dialog.getByText("New workspace", { exact: true })).toBeVisible();
|
|
|
|
await search.fill("interrupt");
|
|
|
|
await expect(dialog.getByText("Interrupt agent", { exact: true })).toBeVisible();
|
|
await expect(dialog.getByText("New workspace", { exact: true })).toHaveCount(0);
|
|
|
|
await search.fill("no matching shortcut");
|
|
await expect(dialog.getByText("No results found", { exact: true })).toBeVisible();
|
|
|
|
await search.fill("");
|
|
await expect(dialog.getByText("New workspace", { exact: true })).toBeVisible();
|
|
});
|
|
|
|
test("keeps diagnostics available from Settings after globalizing the sheet", async ({ page }) => {
|
|
await gotoAppShell(page);
|
|
await openSettings(page);
|
|
await openSettingsSection(page, "diagnostics");
|
|
|
|
await page.getByRole("button", { name: "Run", exact: true }).click();
|
|
await expectDiagnosticReport(page);
|
|
});
|
|
|
|
test.describe("compact sidebar help", () => {
|
|
test.use({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true });
|
|
|
|
test("footer buttons provide finger-sized targets and accept taps beyond the glyph", async ({
|
|
page,
|
|
}) => {
|
|
await gotoAppShell(page);
|
|
await openCompactSidebar(page);
|
|
await expectFingerSizedFooterTargets(page);
|
|
await tapHelpNearTargetEdge(page);
|
|
await expect(page.getByTestId("sidebar-help-menu")).toBeVisible();
|
|
await page.getByTestId("sidebar-help-diagnostics").click();
|
|
await expectDiagnosticReport(page);
|
|
});
|
|
|
|
test("offers diagnostics without advertising disabled keyboard shortcuts", async ({ page }) => {
|
|
await gotoAppShell(page);
|
|
await page.getByRole("button", { name: "Open menu", exact: true }).click();
|
|
|
|
await openHelpMenu(page);
|
|
await expect(page.getByTestId("sidebar-help-shortcuts")).toHaveCount(0);
|
|
await page.getByTestId("sidebar-help-diagnostics").click();
|
|
await expectDiagnosticReport(page);
|
|
});
|
|
});
|
|
|
|
async function openCompactSidebar(page: Page): Promise<void> {
|
|
await page.getByRole("button", { name: "Open menu", exact: true }).click();
|
|
await expect(page.getByTestId("sidebar-footer-bottom-line")).toBeVisible();
|
|
}
|
|
|
|
const MIN_TARGET_SIZE = 44;
|
|
|
|
async function expectFingerSizedFooterTargets(page: Page): Promise<void> {
|
|
const buttons = page.getByTestId("sidebar-footer-bottom-line").getByRole("button");
|
|
await expect(buttons).toHaveCount(5);
|
|
for (const button of await buttons.all()) {
|
|
const bounds = await button.boundingBox();
|
|
// Layout can report a 44pt box as 43.99999, so compare at a hundredth of a point.
|
|
expect(bounds?.width).toBeGreaterThanOrEqual(MIN_TARGET_SIZE - 0.01);
|
|
expect(bounds?.height).toBeGreaterThanOrEqual(MIN_TARGET_SIZE - 0.01);
|
|
// Glyphs stay at the composer toolbar size (some optically smaller); only the target grows.
|
|
const glyphWidth = await button.locator("svg").first().getAttribute("width");
|
|
expect(Number(glyphWidth)).toBeLessThanOrEqual(20);
|
|
}
|
|
}
|
|
|
|
async function tapHelpNearTargetEdge(page: Page): Promise<void> {
|
|
const button = page.getByRole("button", { name: "Help and support", exact: true });
|
|
await button.tap({ position: { x: 40, y: 40 } });
|
|
}
|