Files
editor/e2e/layers-list.spec.ts
T
HarelM c3dd253737 test: migrate e2e to Playwright and component test to Vitest browser mode
Replace Cypress with Playwright for the end-to-end suite and drop
@shellygo/cypress-test-utils in favour of a hand-written MaputnikDriver
page object that keeps the fluent then(...).shouldX() assertion style
(now async). The InputAutocomplete component test moves to Vitest browser
mode using the Playwright provider.

- e2e/maputnik-driver.ts: driver + MaputnikAssertable over Playwright
- e2e/{fixtures,coverage,global-setup,global-teardown}.ts: test fixture,
  istanbul coverage collection, and nyc report generation
- playwright.config.ts / vitest.config.ts
- Code coverage preserved: dev server is istanbul-instrumented, per-test
  window.__coverage__ is merged via nyc into coverage/coverage-final.json
- CI: Cypress jobs replaced with Playwright; docker e2e runs against the
  container via E2E_NO_WEBSERVER
- Remove Cypress deps, config and support files; update docs and .nycrc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:42:32 +03:00

369 lines
14 KiB
TypeScript

import { test, setupMaputnik } from "./fixtures";
test.describe("layers list", () => {
setupMaputnik();
test.beforeEach(async ({ driver }) => {
await driver.when.setStyle("both");
await driver.when.modal.open();
});
test.describe("ops", () => {
let id: string;
test.beforeEach(async ({ driver }) => {
id = await driver.when.modal.fillLayers({ type: "background" });
});
test("should update layers in local storage", async ({ driver }) => {
await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "background" }],
});
});
test.describe("when clicking delete", () => {
test.beforeEach(async ({ driver }) => {
await driver.when.click("layer-list-item:" + id + ":delete");
});
test("should empty layers in local storage", async ({ driver }) => {
await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [],
});
});
});
test.describe("when clicking duplicate", () => {
test.beforeEach(async ({ driver }) => {
await driver.when.click("layer-list-item:" + id + ":copy");
});
test("should add copy layer in local storage", async ({ driver }) => {
await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{ id: id + "-copy", type: "background" },
{ id, type: "background" },
],
});
});
});
test.describe("when clicking hide", () => {
test.beforeEach(async ({ driver }) => {
await driver.when.click("layer-list-item:" + id + ":toggle-visibility");
});
test("should update visibility to none in local storage", async ({ driver }) => {
await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "background", layout: { visibility: "none" } }],
});
});
test.describe("when clicking show", () => {
test.beforeEach(async ({ driver }) => {
await driver.when.click("layer-list-item:" + id + ":toggle-visibility");
});
test("should update visibility to visible in local storage", async ({ driver }) => {
await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "background", layout: { visibility: "visible" } }],
});
});
});
test.describe("when selecting a layer", () => {
let secondId: string;
test.beforeEach(async ({ driver }) => {
await driver.when.modal.open();
secondId = await driver.when.modal.fillLayers({
id: "second-layer",
type: "background",
});
});
test("should show the selected layer in the editor", async ({ driver }) => {
const { get, when, then } = driver;
await when.realClick("layer-list-item:" + secondId);
await then(get.elementByTestId("layer-editor.layer-id.input")).shouldHaveValue(secondId);
await when.realClick("layer-list-item:" + id);
await then(get.elementByTestId("layer-editor.layer-id.input")).shouldHaveValue(id);
});
});
});
});
test.describe("background", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "background" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "background" }],
});
});
test.skip("modify", () => {});
});
test.describe("fill", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "fill", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "fill", source: "example" }],
});
});
// TODO: Change source
test.skip("change source", () => {});
});
test.describe("line", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "line", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "line", source: "example" }],
});
});
test("groups", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.open();
const id1 = await when.modal.fillLayers({ id: "aa", type: "line", layer: "example" });
await when.modal.open();
const id2 = await when.modal.fillLayers({ id: "aa-2", type: "line", layer: "example" });
await when.modal.open();
const id3 = await when.modal.fillLayers({ id: "b", type: "line", layer: "example" });
await then(get.elementByTestId("layer-list-item:" + id1)).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:" + id2)).shouldNotBeVisible();
await then(get.elementByTestId("layer-list-item:" + id3)).shouldBeVisible();
await when.click("layer-list-group:aa-0");
await then(get.elementByTestId("layer-list-item:" + id1)).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:" + id2)).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:" + id3)).shouldBeVisible();
await when.click("layer-list-item:" + id2);
await when.click("skip-target-layer-editor");
await when.click("menu-move-layer-down");
await then(get.elementByTestId("layer-list-group:aa-0")).shouldNotExist();
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{ id: "aa", type: "line", source: "example" },
{ id: "b", type: "line", source: "example" },
{ id: "aa-2", type: "line", source: "example" },
],
});
});
});
test.describe("symbol", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "symbol", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "symbol", source: "example" }],
});
});
test("should show spec info when hovering and clicking single line property", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.fillLayers({ type: "symbol", layer: "example" });
await when.hover("spec-field-container:text-rotate");
await then(get.elementByTestId("field-doc-button-Rotate")).shouldBeVisible();
await when.click("field-doc-button-Rotate", 0);
await then(get.elementByTestId("spec-field-doc")).shouldContainText("Rotates the ");
});
test("should show spec info when hovering and clicking multi line property", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.fillLayers({ type: "symbol", layer: "example" });
await when.hover("spec-field-container:text-offset");
await then(get.elementByTestId("field-doc-button-Offset")).shouldBeVisible();
await when.click("field-doc-button-Offset", 0);
await then(get.elementByTestId("spec-field-doc")).shouldContainText("Offset distance");
});
test("should hide spec info when clicking a second time", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.fillLayers({ type: "symbol", layer: "example" });
await when.hover("spec-field-container:text-rotate");
await then(get.elementByTestId("field-doc-button-Rotate")).shouldBeVisible();
await when.click("field-doc-button-Rotate", 0);
await when.wait(200);
await when.click("field-doc-button-Rotate", 0);
await then(get.elementByTestId("spec-field-doc")).shouldNotBeVisible();
});
});
test.describe("raster", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "raster", layer: "raster" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "raster", source: "raster" }],
});
});
});
test.describe("circle", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "circle", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "circle", source: "example" }],
});
});
});
test.describe("fill extrusion", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "fill-extrusion", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "fill-extrusion", source: "example" }],
});
});
});
test.describe("hillshade", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "hillshade", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "hillshade", source: "example" }],
});
});
test("set hillshade illumination direction array", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "hillshade", layer: "example" });
await when.collapseGroupInLayerEditor();
await when.collapseGroupInLayerEditor(1);
await when.setValueToPropertyArray("spec-field:hillshade-illumination-direction", "1");
await when.addValueToPropertyArray("spec-field:hillshade-illumination-direction", "2");
await when.addValueToPropertyArray("spec-field:hillshade-illumination-direction", "3");
await when.addValueToPropertyArray("spec-field:hillshade-illumination-direction", "4");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
id,
type: "hillshade",
source: "example",
paint: { "hillshade-illumination-direction": [1, 2, 3, 4] },
},
],
});
});
test("set hillshade highlight color array", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "hillshade", layer: "example" });
await when.collapseGroupInLayerEditor();
await when.setValueToPropertyArray("spec-field:hillshade-highlight-color", "blue");
await when.addValueToPropertyArray("spec-field:hillshade-highlight-color", "#00ff00");
await when.addValueToPropertyArray("spec-field:hillshade-highlight-color", "rgba(255, 255, 0, 1)");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
id,
type: "hillshade",
source: "example",
paint: {
"hillshade-highlight-color": ["blue", "#00ff00", "rgba(255, 255, 0, 1)"],
},
},
],
});
});
});
test.describe("color-relief", () => {
test("add", async ({ driver }) => {
const { get, when, then } = driver;
const id = await when.modal.fillLayers({ type: "color-relief", layer: "example" });
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, type: "color-relief", source: "example" }],
});
});
test("adds elevation expression when clicking the elevation button", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.fillLayers({ type: "color-relief", layer: "example" });
await when.collapseGroupInLayerEditor();
await when.click("make-elevation-function");
await then(
get.element("[data-wd-key='spec-field-container:color-relief-color'] .cm-line")
).shouldBeVisible();
});
});
test.describe("groups", () => {
test("simple", async ({ driver }) => {
const { get, when, then } = driver;
await when.setStyle("geojson");
await when.modal.open();
await when.modal.fillLayers({ id: "foo", type: "background" });
await when.modal.open();
await when.modal.fillLayers({ id: "foo_bar", type: "background" });
await when.modal.open();
await when.modal.fillLayers({ id: "foo_bar_baz", type: "background" });
await then(get.elementByTestId("layer-list-item:foo")).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:foo_bar")).shouldNotBeVisible();
await then(get.elementByTestId("layer-list-item:foo_bar_baz")).shouldNotBeVisible();
await when.click("layer-list-group:foo-0");
await then(get.elementByTestId("layer-list-item:foo")).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:foo_bar")).shouldBeVisible();
await then(get.elementByTestId("layer-list-item:foo_bar_baz")).shouldBeVisible();
});
});
test.describe("drag and drop", () => {
test("move layer should update local storage", async ({ driver }) => {
const { get, when, then } = driver;
await when.modal.open();
const firstId = await when.modal.fillLayers({ id: "a", type: "background" });
await when.modal.open();
const secondId = await when.modal.fillLayers({ id: "b", type: "background" });
await when.modal.open();
const thirdId = await when.modal.fillLayers({ id: "c", type: "background" });
await when.dragAndDropWithWait("layer-list-item:" + firstId, "layer-list-item:" + thirdId);
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{ id: secondId, type: "background" },
{ id: thirdId, type: "background" },
{ id: firstId, type: "background" },
],
});
});
});
test.describe("sticky header", () => {
test("should keep header visible when scrolling layer list", async ({ driver }) => {
const { get, when, then } = driver;
// Setup: Create multiple layers to enable scrolling
for (let i = 0; i < 20; i++) {
await when.modal.open();
await when.modal.fillLayers({ id: `layer-${i}`, type: "background" });
}
await when.wait(500);
const header = get.elementByTestId("layer-list.header");
await then(header).shouldBeVisible();
// Scroll the layer list container
await get.elementByTestId("layer-list").evaluate((el) => el.scrollTo(0, el.scrollHeight));
await when.wait(200);
await then(header).shouldBeVisible();
await then(get.elementByTestId("layer-list:add-layer")).shouldBeVisible();
});
});
});