mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 10:12:44 +00:00
What's new opens an adaptive sheet — bottom sheet on compact, centred card on desktop — that fetches CHANGELOG.md from the repository and renders it with the app's own chrome. The desktop update callout, the sidebar help menu, and a new row in Settings → About all open it; the sheet header links out to paseo.sh/changelog. The parser commits to two facts only: `##` starts a release and `###` starts a section. Section titles are read from the document rather than matched against a list, and everything under a section passes through verbatim to the Markdown renderer, so a future callout, block quote, code fence or image needs no parser change. Fences are tracked because a fenced sample is the one thing that can fool a line scan. The document is fetched rather than bundled because the app has to describe a release that shipped after it was built, which is the update callout's whole purpose. It goes straight to the repository rather than through a host: the notes describe the app, and routing them through a daemon would make them depend on which host happens to be connected. Nothing is fetched or parsed until the sheet opens. The latest release renders as soon as there is one and the rest of the first page follows in a transition, so React slices that work around the opening animation instead of committing hundreds of list items at once.
168 lines
6.3 KiB
TypeScript
168 lines
6.3 KiB
TypeScript
import { expect, test, type Page } from "../support/fixtures";
|
|
import { gotoAppShell, openSettings } from "../support/helpers/app";
|
|
import { openSettingsSection } from "../support/helpers/settings";
|
|
import { openWhatsNew, release, serveChangelog } 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 }) => {
|
|
// A callout, a section name the app has never seen, and a fenced sample whose
|
|
// contents look like a release heading.
|
|
await serveChangelog(page, [
|
|
"# Changelog",
|
|
"",
|
|
"## 9.1.0 - 2026-03-04",
|
|
"",
|
|
"Headline release note.",
|
|
"",
|
|
"> [!WARNING]",
|
|
"> Read this before upgrading.",
|
|
"",
|
|
"### Sparkles",
|
|
"",
|
|
"- Added a brand new thing",
|
|
"",
|
|
"```md",
|
|
"## 0.0.0 - 1999-01-01",
|
|
"```",
|
|
"",
|
|
"## 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");
|
|
|
|
await expect(latest.getByText("March 4, 2026", { exact: true })).toBeVisible();
|
|
await expect(latest.getByText("Headline release note.")).toBeVisible();
|
|
await expect(latest.getByText("Read this before upgrading.")).toBeVisible();
|
|
await expect(latest.getByText("Sparkles", { exact: true })).toBeVisible();
|
|
await expect(latest.getByText("Added a brand new thing")).toBeVisible();
|
|
await expect(release(sheet, "9.0.0")).toBeVisible();
|
|
await expect(release(sheet, "0.0.0")).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("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);
|
|
});
|
|
});
|