Files
SnapOtter/tests/e2e-editor/editor-options-bar.spec.ts
SnapOtter 3120e6708d fix(editor): apply layer effects + object flip, add Beta badge, repair e2e specs
While getting the editor e2e suite green, three "stale test" failures turned
out to be real bugs (per the reporter's hunch that tests might be catching
real issues):

- Layer effects (drop shadow, glows) never applied. The panel wrote effects
  into `attrs.effects` through updateObject, but the panel and renderer both
  read the object's top-level `effects`, so the toggle never persisted. Add a
  dedicated `setObjectEffects` store action and route the panel through it.
- Object flip (transform tool) did nothing. No object renderer applied
  `scaleX`/`scaleY`, and the flip negated scale without compensating position.
  Apply scale in the renderers and flip in place: mirror points for stroke
  objects, negate scale + shift position for sized objects.

(The paint-bucket / pixel-tool coordinate bug and the broken-at-non-100%-zoom
export were fixed in the preceding #259 change.)

Also adds a small "Beta" badge to the editor (welcome heading + nav link) and
repairs ~18 stale editor e2e specs whose selectors/assertions had drifted from
the current UI: the options bar is `h-9` not `h-10` (added a stable
`data-testid`), the menu bar is `h-8`/`bg-background`, the flip button
aria-labels are lowercase, the welcome "Image Editor" heading collides with an
sr-only `<h1>`, the color-picker tabs need a role-scoped selector, and the
magic-wand / flip tests now use deterministic setup and assert the actual
effect instead of fragile screenshot diffs.
2026-06-17 14:21:35 +08:00

106 lines
4.1 KiB
TypeScript

import { createNewDocument, expect, selectTool, test } from "./helpers";
test.describe("Editor Options Bar", () => {
test.beforeEach(async ({ editorPage: page }) => {
await createNewDocument(page);
});
test("eyedropper options show sample size when eyedropper selected", async ({
editorPage: page,
}) => {
await selectTool(page, "eyedropper");
// The options bar should display the Sample label and dropdown
await expect(page.getByText("Sample:")).toBeVisible();
const sampleDropdown = page.locator("[data-testid='sample-size-dropdown']");
await expect(sampleDropdown).toBeVisible();
// Should default to "Point (1x1)"
await expect(sampleDropdown).toContainText("Point (1x1)");
// Clicking the dropdown should reveal size options
await sampleDropdown.click();
await page.waitForTimeout(300);
await expect(page.getByText("3x3 Average")).toBeVisible();
await expect(page.getByText("5x5 Average")).toBeVisible();
});
test("transform options show position/size when transform selected", async ({
editorPage: page,
}) => {
await selectTool(page, "transform");
// The options bar should show X, Y, W, H, Rotation inputs
await expect(page.locator("#transform-x")).toBeVisible();
await expect(page.locator("#transform-y")).toBeVisible();
await expect(page.locator("#transform-w")).toBeVisible();
await expect(page.locator("#transform-h")).toBeVisible();
await expect(page.locator("#transform-rotation")).toBeVisible();
// Flip buttons should be visible
await expect(page.locator("button[aria-label='Flip horizontal']")).toBeVisible();
await expect(page.locator("button[aria-label='Flip vertical']")).toBeVisible();
// Aspect ratio lock button should be visible
const lockBtn = page.locator("button[aria-label*='aspect ratio']");
await expect(lockBtn).toBeVisible();
});
test("brush options show size, opacity, hardness", async ({ editorPage: page }) => {
await selectTool(page, "brush");
const optionsBar = page.locator('[data-testid="editor-options-bar"]');
// Size, Opacity, and Hardness labels should be in the options bar
await expect(optionsBar.getByText("Size")).toBeVisible();
await expect(optionsBar.getByText("Opacity")).toBeVisible();
await expect(optionsBar.getByText("Hardness")).toBeVisible();
// Each should have a range slider and a number input
const sizeSlider = optionsBar
.locator("label")
.filter({ hasText: "Size" })
.locator("input[type='range']");
await expect(sizeSlider).toBeVisible();
const opacitySlider = optionsBar
.locator("label")
.filter({ hasText: "Opacity" })
.locator("input[type='range']");
await expect(opacitySlider).toBeVisible();
const hardnessSlider = optionsBar
.locator("label")
.filter({ hasText: "Hardness" })
.locator("input[type='range']");
await expect(hardnessSlider).toBeVisible();
});
test("selection options show mode dropdown", async ({ editorPage: page }) => {
await selectTool(page, "marquee-rect");
await page.waitForTimeout(500);
// The options bar should show Type and Mode sections
await expect(page.getByText("Type:")).toBeVisible();
await expect(page.getByText("Mode:")).toBeVisible();
// Type buttons: Rect, Ellipse, Lasso (use getByRole for robust matching)
await expect(page.getByRole("button", { name: "Rectangular" })).toBeVisible();
await expect(page.getByRole("button", { name: "Elliptical" })).toBeVisible();
await expect(page.getByRole("button", { name: "Lasso" }).first()).toBeVisible();
// Rect should be active since we selected marquee-rect
await expect(page.getByRole("button", { name: "Rectangular" })).toHaveAttribute(
"aria-pressed",
"true",
);
// Mode buttons: New, Add, Sub
await expect(page.getByRole("button", { name: "New Selection" })).toBeVisible();
await expect(page.getByRole("button", { name: "Add to Selection" })).toBeVisible();
await expect(page.getByRole("button", { name: "Subtract from Selection" })).toBeVisible();
});
});