Files
kandev/apps/web/e2e/tests/plugins/mobile-plugin-settings-row.spec.ts
T
2026-08-16 16:31:28 +01:00

57 lines
2.9 KiB
TypeScript

/**
* E2E: a plugin row opens its settings page from a phone.
*
* The row used to carry its link on the plugin name alone, styled like the
* headings around it and underlined only on hover, so a touch device got no
* affordance at all. The whole card is the target now, with a chevron. This is
* the phone half of that: `e2e/playwright.config.ts` routes only
* `mobile-*.spec.ts` at the Pixel 5 project, so the desktop coverage in
* plugins.spec.ts cannot catch phone wrapping, stacking, or hit-target
* regressions in the row.
*
* One test rather than several: installing the fixture plugin is the expensive
* part of the setup and every assertion here is about the same rendered row.
*/
import { expect, test } from "../../fixtures/test-base";
import { PLUGIN_ID, installFixturePlugin } from "../../helpers/plugin-fixture";
test("mobile plugin row: whole card opens settings, controls still act", async ({ testPage }) => {
test.setTimeout(180_000);
await installFixturePlugin(testPage);
const pluginRow = testPage.getByTestId(`plugin-row-${PLUGIN_ID}`);
await expect(pluginRow).toBeVisible({ timeout: 30_000 });
// The fixture declares a required api_token nobody has filled in, so the row
// advertises the page it wants opened. It has to survive the phone's badge
// wrapping, not just fit on a desktop title row.
await expect(pluginRow.getByTestId(`plugin-setup-required-${PLUGIN_ID}`)).toBeVisible({
timeout: 15_000,
});
// The overlay link is the whole affordance on touch, where nothing hovers,
// so it has to actually cover the card. It is inset by the row's 1px border,
// hence the 2px slack; a `space-y-*` margin landing on it once cost the
// bottom strip of the card.
const linkBox = await pluginRow.getByTestId(`plugin-row-link-${PLUGIN_ID}`).boundingBox();
const rowBox = await pluginRow.boundingBox();
if (!linkBox || !rowBox) throw new Error("plugin row or its overlay link has no layout box");
expect(linkBox.width).toBeGreaterThanOrEqual(rowBox.width - 2);
expect(linkBox.height).toBeGreaterThanOrEqual(rowBox.height - 2);
// Every control sits above the overlay. If one slipped below it the tap would
// navigate instead of acting, and on a phone there is no hover state to
// reveal which is which.
const disable = pluginRow.getByRole("button", { name: "Disable" });
expect((await disable.boundingBox())?.height).toBeGreaterThanOrEqual(44);
await disable.tap();
await expect(pluginRow.getByText("Disabled", { exact: true })).toBeVisible({ timeout: 15_000 });
await expect(testPage).toHaveURL(/\/settings\/plugins$/);
// Tapping the card body rather than the name is the whole point of the
// change; keep clear of the row's own controls.
await pluginRow.tap({ position: { x: 12, y: 12 } });
await expect(testPage).toHaveURL(new RegExp(`/settings/plugins/${PLUGIN_ID}$`));
await expect(testPage.getByTestId(`plugin-detail-${PLUGIN_ID}`)).toBeVisible();
});