tests/build-journey.spec.ts
Rama main · réplica de lectura del repositorio original.
import { expect } from "@playwright/test";
import { createJourneyScenario } from "../apps/backend/src/database/seeds";
import { loggedTest } from "./logged-test";
import { ensureTeamOwner, screenshot, waitForDiffListToSettle } from "./util";
loggedTest(
"reviews a change against the journey it happened in",
async ({ page, auth, team, project }) => {
await ensureTeamOwner({ team: team.team, user: auth.user });
const { build } = await createJourneyScenario({ projectId: project.id });
await page.goto(
`/${team.account.slug}/${project.name}/builds/${build.number}`,
);
// The button is clickable before the diffs land, and then it has nothing
// to open: wait for the list.
await expect(
page.getByRole("button", { name: "checkout/confirmation.png" }),
).toBeVisible();
await page
.getByRole("button", { name: /^(Start review|Browse snapshots)/ })
.click();
// The review opens on the biggest change, which is the last step of the
// purchase — so the journey is there to be opened, and its order is
// already known not to be the score's.
await expect(
page.getByRole("heading", { name: "checkout/confirmation.png" }),
).toBeVisible();
const toggle = page.getByRole("button", { name: "Journey" });
await expect(toggle).toBeVisible();
await toggle.click();
// The whole journey, in the order the test walked it — which is neither
// the alphabet (cart, confirmation, payment, shipping) nor the diff score
// (confirmation, payment). Cart and shipping did not change and are in it
// all the same: a change reads against the journey it happened in.
const drawer = page.getByTestId("journey-drawer");
await expect(drawer.getByRole("button")).toHaveText([
"1 · cart",
"2 · shipping",
"3 · payment",
"4 · confirmation",
]);
// The label is cut to fit, so it hands the whole key over on hover; the
// dot has no legend anywhere, so it explains itself.
// Base UI portals its tooltip out of the trigger and gives the box
// `role="presentation"`, so the portal is what there is to assert on.
const tooltip = page.locator("[data-base-ui-portal]");
await expect(async () => {
await page.mouse.move(0, 0);
await drawer.getByText("2 · shipping").hover();
await expect(tooltip).toHaveText("checkout/shipping", { timeout: 2_000 });
}).toPass();
await expect(async () => {
await page.mouse.move(0, 0);
await drawer.getByTestId("journey-step-attention").first().hover();
await expect(tooltip).toHaveText("Needs review: Changed", {
timeout: 2_000,
});
}).toPass();
await page.mouse.move(0, 0);
// Jumping to a step opens it, unchanged included.
await drawer.getByRole("button", { name: "1 · cart" }).click();
await expect(
page.getByRole("heading", { name: "checkout/cart.png" }),
).toBeVisible();
await expect(drawer.locator("[aria-current='step']")).toHaveText(
"1 · cart",
);
// Each jump scrolls the snapshot list to the step, and the next jump reads
// that scroll to decide whether it has to move at all. Settle between them
// or the walk below rests wherever the animation happened to be.
await waitForDiffListToSettle(page);
// ⇧→ walks to the next step, ⇧← back — across status sections.
await page.keyboard.press("Shift+ArrowRight");
await expect(
page.getByRole("heading", { name: "checkout/shipping.png" }),
).toBeVisible();
await waitForDiffListToSettle(page);
await page.keyboard.press("Shift+ArrowLeft");
await expect(
page.getByRole("heading", { name: "checkout/cart.png" }),
).toBeVisible();
await waitForDiffListToSettle(page);
await screenshot(page, "build-journey-drawer", {
replacements: {
[team.account.slug]: "acme",
},
});
// The click above left focus in the strip, and from there plain ← / →
// walk it instead. Focus goes along, so the next arrow starts from the
// step on screen.
await page.keyboard.press("ArrowRight");
await expect(
page.getByRole("heading", { name: "checkout/shipping.png" }),
).toBeVisible();
await waitForDiffListToSettle(page);
await page.keyboard.press("ArrowRight");
await expect(
page.getByRole("heading", { name: "checkout/payment.png" }),
).toBeVisible();
await waitForDiffListToSettle(page);
await page.keyboard.press("ArrowRight");
await expect(
page.getByRole("heading", { name: "checkout/confirmation.png" }),
).toBeVisible();
await waitForDiffListToSettle(page);
await page.keyboard.press("ArrowLeft");
await expect(
page.getByRole("heading", { name: "checkout/payment.png" }),
).toBeVisible();
const payment = drawer.getByRole("button", { name: "3 · payment" });
await expect(payment).toBeFocused();
await waitForDiffListToSettle(page);
// Read once the list has settled, long after the build hotkeys would have
// run had the ← reached them: they would have shown the baseline.
const baseline = page.getByRole("button", {
name: "Baseline",
exact: true,
});
await expect(baseline).toHaveAttribute("aria-pressed", "false");
// Out of the strip, ← / → switch the view mode again and the step stays.
await payment.blur();
const changes = page.getByRole("button", { name: "Changes", exact: true });
await page.keyboard.press("ArrowLeft");
await expect(baseline).toHaveAttribute("aria-pressed", "true");
await page.keyboard.press("ArrowRight");
await expect(changes).toHaveAttribute("aria-pressed", "true");
await expect(
page.getByRole("heading", { name: "checkout/payment.png" }),
).toBeVisible();
// A test that captures a single screen is regular visual testing, not a
// journey: nothing to open, and nothing offering to.
await page
.locator("[role='button'][data-index]")
.filter({ hasText: "account/settings.png" })
.click();
await expect(
page.getByRole("heading", { name: "account/settings.png" }),
).toBeVisible();
await expect(page.getByTestId("journey-drawer")).toHaveCount(0);
await expect(page.getByRole("button", { name: "Journey" })).toHaveCount(0);
},
);
loggedTest(
"walks the journey on screen when a step is marked",
async ({ page, auth, team, project }) => {
await ensureTeamOwner({ team: team.team, user: auth.user });
const { build } = await createJourneyScenario({ projectId: project.id });
await page.goto(
`/${team.account.slug}/${project.name}/builds/${build.number}`,
);
await expect(
page.getByRole("button", { name: "checkout/confirmation.png" }),
).toBeVisible();
await page
.getByRole("button", { name: /^(Start review|Browse snapshots)/ })
.click();
// The list goes confirmation, the account settings test, payment; the
// journey puts payment before confirmation.
const confirmationHeading = page.getByRole("heading", {
name: "checkout/confirmation.png",
});
const settingsHeading = page.getByRole("heading", {
name: "account/settings.png",
});
await expect(confirmationHeading).toBeVisible();
// With the journey closed, nothing on screen says the review follows it:
// marking moves down the list.
await page.keyboard.press("y");
await expect(settingsHeading).toBeVisible();
await page.keyboard.press("ControlOrMeta+z");
await expect(confirmationHeading).toBeVisible();
// With it open, marking moves on to the step still to review, from the
// last step back to the ones before it.
await page.getByRole("button", { name: "Journey" }).click();
await page.keyboard.press("y");
await expect(
page.getByRole("heading", { name: "checkout/payment.png" }),
).toBeVisible();
await expect(
page.getByTestId("journey-drawer").locator("[aria-current='step']"),
).toHaveText("3 · payment");
// Cart and shipping did not change, so the journey is reviewed: the review
// goes back to the diff it jumped over, which the list now has above.
await page.keyboard.press("y");
await expect(settingsHeading).toBeVisible();
},
);