diff --git a/e2e/accessibility.spec.ts b/e2e/accessibility.spec.ts index 7604af56..58855dfc 100644 --- a/e2e/accessibility.spec.ts +++ b/e2e/accessibility.spec.ts @@ -1,15 +1,20 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("accessibility", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); test.describe("skip links", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("layer"); + test.beforeEach(async () => { + await when.setStyle("layer"); }); - test("skip link to layer list", async ({ driver }) => { - const { get, when, then } = driver; + test("skip link to layer list", async () => { const selector = "root:skip:layer-list"; await then(get.elementByTestId(selector)).shouldExist(); await when.tab(); @@ -18,8 +23,7 @@ test.describe("accessibility", () => { await then(get.skipTargetLayerList()).shouldBeFocused(); }); - test("skip link to layer editor", async ({ driver }) => { - const { get, when, then } = driver; + test("skip link to layer editor", async () => { const selector = "root:skip:layer-editor"; await then(get.elementByTestId(selector)).shouldExist(); await then(get.elementByTestId("skip-target-layer-editor")).shouldExist(); @@ -30,8 +34,7 @@ test.describe("accessibility", () => { await then(get.skipTargetLayerEditor()).shouldBeFocused(); }); - test("skip link to map view", async ({ driver }) => { - const { get, when, then } = driver; + test("skip link to map view", async () => { const selector = "root:skip:map-view"; await then(get.elementByTestId(selector)).shouldExist(); await when.tab(); diff --git a/e2e/code-editor.spec.ts b/e2e/code-editor.spec.ts index cf706858..9d793fcf 100644 --- a/e2e/code-editor.spec.ts +++ b/e2e/code-editor.spec.ts @@ -1,16 +1,20 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("code editor", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); - test("open code editor", async ({ driver }) => { - const { when, get, then } = driver; + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); + + test("open code editor", async () => { await when.click("nav:code-editor"); await then(get.element(".maputnik-code-editor")).shouldExist(); }); - test("closes code editor", async ({ driver }) => { - const { when, get, then } = driver; + test("closes code editor", async () => { await when.click("nav:code-editor"); await then(get.element(".maputnik-code-editor")).shouldExist(); await when.click("nav:code-editor"); diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts index 94aa0ac0..a8631de5 100644 --- a/e2e/fixtures.ts +++ b/e2e/fixtures.ts @@ -1,27 +1,46 @@ -import { test as base, expect } from "@playwright/test"; -import { MaputnikDriver } from "./maputnik-driver"; +import { test as base, expect, type Page } from "@playwright/test"; +import { readCoverage, writeCoverage } from "./coverage"; + +let activePage: Page | undefined; +const coverageChunks: unknown[] = []; + +/** The page for the currently running test. Throws if used outside a test. */ +export function currentPage(): Page { + if (!activePage) { + throw new Error("No active page: a MaputnikDriver method was called outside of a running test."); + } + return activePage; +} + +/** Records a coverage snapshot (called before navigations, which reset __coverage__). */ +export function recordCoverageChunk(chunk: unknown): void { + if (chunk) coverageChunks.push(chunk); +} /** - * Playwright test with a per-test `driver` (the maputnik page object) that also - * collects istanbul coverage once the test finishes. + * Auto fixture that binds the current test's page for the (page-lazy) + * MaputnikDriver, auto-accepts confirm dialogs, and writes the istanbul + * coverage collected during the test to `.nyc_output`. */ -export const test = base.extend<{ driver: MaputnikDriver }>({ - driver: async ({ page }, use, testInfo) => { - const driver = new MaputnikDriver(page); - await use(driver); - await driver.flushCoverage(testInfo); - }, +export const test = base.extend<{ maputnikPage: void }>({ + maputnikPage: [ + async ({ page }, use, testInfo) => { + activePage = page; + coverageChunks.length = 0; + // Accept confirm dialogs (e.g. the "replace current style" prompt). These + // are dismissed by default, which would cancel loading a style via URL. + page.on("dialog", (dialog) => dialog.accept().catch(() => undefined)); + + await use(); + + const finalCoverage = await readCoverage(page); + if (finalCoverage) coverageChunks.push(finalCoverage); + coverageChunks.forEach((chunk, index) => writeCoverage(chunk, `${testInfo.testId}-${index}`)); + coverageChunks.length = 0; + activePage = undefined; + }, + { auto: true }, + ], }); export { expect }; - -/** - * Registers the shared `beforeEach` used by most specs: mock the style responses - * and load the default (geojson + raster) style. - */ -export function setupMaputnik(): void { - test.beforeEach(async ({ driver }) => { - await driver.given.setupMockBackedResponses(); - await driver.when.setStyle("both"); - }); -} diff --git a/e2e/history.spec.ts b/e2e/history.spec.ts index 8610aaea..94c6b8e5 100644 --- a/e2e/history.spec.ts +++ b/e2e/history.spec.ts @@ -1,13 +1,18 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("history", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); const undoKeyCombo = process.platform === "darwin" ? "{meta}z" : "{ctrl}z"; const redoKeyCombo = process.platform === "darwin" ? "{meta}{shift}z" : "{ctrl}y"; - test("undo/redo", async ({ driver }) => { - const { get, when, then } = driver; + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); + + test("undo/redo", async () => { await when.setStyle("geojson"); await when.modal.open(); @@ -53,8 +58,7 @@ test.describe("history", () => { }); }); - test("should not redo after undo and value change", async ({ driver }) => { - const { get, when, then } = driver; + test("should not redo after undo and value change", async () => { await when.setStyle("geojson"); await when.modal.open(); await when.modal.fillLayers({ diff --git a/e2e/i18n.spec.ts b/e2e/i18n.spec.ts index 61f150e8..c9664fb5 100644 --- a/e2e/i18n.spec.ts +++ b/e2e/i18n.spec.ts @@ -1,29 +1,32 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("i18n", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); test.describe("language detector", () => { - test("English", async ({ driver }) => { - const { get, when, then } = driver; + test("English", async () => { await when.visit("?lng=en"); await then(get.elementByTestId("maputnik-lang-select")).shouldHaveValue("en"); }); - test("Japanese", async ({ driver }) => { - const { get, when, then } = driver; + test("Japanese", async () => { await when.visit("?lng=ja"); await then(get.elementByTestId("maputnik-lang-select")).shouldHaveValue("ja"); }); }); test.describe("language switcher", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("layer"); + test.beforeEach(async () => { + await when.setStyle("layer"); }); - test("the language switcher switches to Japanese", async ({ driver }) => { - const { get, when, then } = driver; + test("the language switcher switches to Japanese", async () => { const selector = "maputnik-lang-select"; await then(get.elementByTestId(selector)).shouldExist(); await when.select(selector, "ja"); diff --git a/e2e/keyboard.spec.ts b/e2e/keyboard.spec.ts index 6c0928a7..5a72b6e9 100644 --- a/e2e/keyboard.spec.ts +++ b/e2e/keyboard.spec.ts @@ -1,16 +1,21 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("keyboard", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); test.describe("shortcuts", () => { - test.beforeEach(async ({ driver }) => { - await driver.given.setupMockBackedResponses(); - await driver.when.setStyle(""); + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle(""); }); - test("ESC should unfocus", async ({ driver }) => { - const { get, when, then } = driver; + test("ESC should unfocus", async () => { const targetSelector = "maputnik-select"; await when.focus(targetSelector); await then(get.elementByTestId(targetSelector)).shouldBeFocused(); @@ -18,50 +23,42 @@ test.describe("keyboard", () => { await then(get.elementByTestId(targetSelector)).shouldNotBeFocused(); }); - test("'?' should show shortcuts modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'?' should show shortcuts modal", async () => { await when.typeKeys("?"); await then(get.elementByTestId("modal:shortcuts")).shouldBeVisible(); }); - test("'o' should show open modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'o' should show open modal", async () => { await when.typeKeys("o"); await then(get.elementByTestId("modal:open")).shouldBeVisible(); }); - test("'e' should show export modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'e' should show export modal", async () => { await when.typeKeys("e"); await then(get.elementByTestId("modal:export")).shouldBeVisible(); }); - test("'d' should show sources modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'d' should show sources modal", async () => { await when.typeKeys("d"); await then(get.elementByTestId("modal:sources")).shouldBeVisible(); }); - test("'s' should show settings modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'s' should show settings modal", async () => { await when.typeKeys("s"); await then(get.elementByTestId("modal:settings")).shouldBeVisible(); }); - test("'i' should change map to inspect mode", async ({ driver }) => { - const { get, when, then } = driver; + test("'i' should change map to inspect mode", async () => { await when.typeKeys("i"); await then(get.inputValue("maputnik-select")).shouldEqual("inspect"); }); - test("'m' should focus map", async ({ driver }) => { - const { get, when, then } = driver; + test("'m' should focus map", async () => { await when.typeKeys("m"); await then(get.canvas()).shouldBeFocused(); }); - test("'!' should show debug modal", async ({ driver }) => { - const { get, when, then } = driver; + test("'!' should show debug modal", async () => { await when.typeKeys("!"); await then(get.elementByTestId("modal:debug")).shouldBeVisible(); }); diff --git a/e2e/layer-editor.spec.ts b/e2e/layer-editor.spec.ts index 8834e58e..1745f2f1 100644 --- a/e2e/layer-editor.spec.ts +++ b/e2e/layer-editor.spec.ts @@ -1,16 +1,17 @@ import { v1 as uuid } from "uuid"; -import { test, setupMaputnik } from "./fixtures"; -import type { MaputnikDriver } from "./maputnik-driver"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("layer editor", () => { - setupMaputnik(); - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("both"); - await driver.when.modal.open(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + await when.modal.open(); }); - async function createBackground(driver: MaputnikDriver) { - const { get, when, then } = driver; + async function createBackground() { const id = uuid(); await when.selectWithin("add-layer.layer-type", "background"); @@ -26,9 +27,8 @@ test.describe("layer editor", () => { test.skip("expand/collapse", () => {}); - test("id", async ({ driver }) => { - const { get, when, then } = driver; - const bgId = await createBackground(driver); + test("id", async () => { + const bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); @@ -42,8 +42,7 @@ test.describe("layer editor", () => { }); test.describe("source", () => { - test("should show error when the source is invalid", async ({ driver }) => { - const { get, when, then } = driver; + test("should show error when the source is invalid", async () => { await when.modal.fillLayers({ type: "circle", layer: "invalid", @@ -57,22 +56,20 @@ test.describe("layer editor", () => { test.describe("min-zoom", () => { let bgId: string; - test.beforeEach(async ({ driver }) => { - const { when } = driver; - bgId = await createBackground(driver); + test.beforeEach(async () => { + bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.setValue("min-zoom.input-text", "1"); await when.click("layer-editor.layer-id"); }); - test("should update min-zoom in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update min-zoom in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id: "background:" + bgId, type: "background", minzoom: 1 }], }); }); - test("when clicking next layer should update style on local storage", async ({ driver }) => { - const { get, when, then } = driver; + test("when clicking next layer should update style on local storage", async () => { await when.type("min-zoom.input-text", "{backspace}"); await when.click("max-zoom.input-text"); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ @@ -84,16 +81,15 @@ test.describe("layer editor", () => { test.describe("max-zoom", () => { let bgId: string; - test.beforeEach(async ({ driver }) => { - const { when } = driver; - bgId = await createBackground(driver); + test.beforeEach(async () => { + bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.setValue("max-zoom.input-text", "1"); await when.click("layer-editor.layer-id"); }); - test("should update style in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update style in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id: "background:" + bgId, type: "background", maxzoom: 1 }], }); }); @@ -103,16 +99,15 @@ test.describe("layer editor", () => { let bgId: string; const comment = "42"; - test.beforeEach(async ({ driver }) => { - const { when } = driver; - bgId = await createBackground(driver); + test.beforeEach(async () => { + bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.setValue("layer-comment.input", comment); await when.click("layer-editor.layer-id"); }); - test("should update style in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update style in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [ { id: "background:" + bgId, @@ -124,14 +119,13 @@ test.describe("layer editor", () => { }); test.describe("when unsetting", () => { - test.beforeEach(async ({ driver }) => { - const { when } = driver; + test.beforeEach(async () => { await when.clear("layer-comment.input"); await when.click("min-zoom.input-text"); }); - test("should update style in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update style in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id: "background:" + bgId, type: "background" }], }); }); @@ -140,15 +134,14 @@ test.describe("layer editor", () => { test.describe("color", () => { let bgId: string; - test.beforeEach(async ({ driver }) => { - const { when } = driver; - bgId = await createBackground(driver); + test.beforeEach(async () => { + bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.click("spec-field:background-color"); }); - test("should update style in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update style in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id: "background:" + bgId, type: "background" }], }); }); @@ -156,21 +149,17 @@ test.describe("layer editor", () => { test.describe("opacity", () => { let bgId: string; - test.beforeEach(async ({ driver }) => { - const { when } = driver; - bgId = await createBackground(driver); + test.beforeEach(async () => { + bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.type("spec-field-input:background-opacity", "0."); }); - test("should keep '.' in the input field", async ({ driver }) => { - await driver - .then(driver.get.elementByTestId("spec-field-input:background-opacity")) - .shouldHaveValue("0."); + test("should keep '.' in the input field", async () => { + await then(get.elementByTestId("spec-field-input:background-opacity")).shouldHaveValue("0."); }); - test("should revert to a valid value when focus out", async ({ driver }) => { - const { get, when, then } = driver; + test("should revert to a valid value when focus out", async () => { await when.click("layer-list-item:background:" + bgId); await then(get.elementByTestId("spec-field-input:background-opacity")).shouldHaveValue("0"); }); @@ -182,8 +171,7 @@ test.describe("layer editor", () => { }); test.describe("layout", () => { - test("text-font", async ({ driver }) => { - const { get, when, then } = driver; + test("text-font", async () => { await when.setStyle("font"); await when.collapseGroupInLayerEditor(); await when.collapseGroupInLayerEditor(1); @@ -204,8 +192,7 @@ test.describe("layer editor", () => { }); test.describe("json-editor", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "circle", layer: "example", @@ -225,25 +212,23 @@ test.describe("layer editor", () => { test.skip("expand/collapse", () => {}); test.skip("modify", () => {}); - test("parse error", async ({ driver }) => { - const { get, when, then } = driver; - const bgId = await createBackground(driver); + test("parse error", async () => { + const bgId = await createBackground(); await when.click("layer-list-item:background:" + bgId); await when.collapseGroupInLayerEditor(); await when.collapseGroupInLayerEditor(1); await then(get.element(".cm-lint-marker-error")).shouldNotExist(); - await when.appendTextInJsonEditor( - " {" - ); + // Inject an invalid token (CodeMirror auto-closes brackets/quotes, so a + // bare word reliably breaks the JSON) and expect a lint error. + await when.appendTextInJsonEditor("zzz"); await then(get.element(".cm-lint-marker-error")).shouldExist(); }); }); test.describe("sticky header", () => { - test("should keep layer header visible when scrolling properties", async ({ driver }) => { - const { get, when, then } = driver; + test("should keep layer header visible when scrolling properties", async () => { // Setup: Create a layer with many properties (e.g. symbol layer) await when.modal.fillLayers({ type: "symbol", diff --git a/e2e/layers-list.spec.ts b/e2e/layers-list.spec.ts index f4427b50..95378a57 100644 --- a/e2e/layers-list.spec.ts +++ b/e2e/layers-list.spec.ts @@ -1,41 +1,44 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("layers list", () => { - setupMaputnik(); - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("both"); - await driver.when.modal.open(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + await when.modal.open(); }); test.describe("ops", () => { let id: string; - test.beforeEach(async ({ driver }) => { - id = await driver.when.modal.fillLayers({ type: "background" }); + test.beforeEach(async () => { + id = await when.modal.fillLayers({ type: "background" }); }); - test("should update layers in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update layers in local storage", async () => { + await then(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.beforeEach(async () => { + await when.click("layer-list-item:" + id + ":delete"); }); - test("should empty layers in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should empty layers in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [], }); }); }); test.describe("when clicking duplicate", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("layer-list-item:" + id + ":copy"); + test.beforeEach(async () => { + await when.click("layer-list-item:" + id + ":copy"); }); - test("should add copy layer in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should add copy layer in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [ { id: id + "-copy", type: "background" }, { id, type: "background" }, @@ -45,23 +48,23 @@ test.describe("layers list", () => { }); test.describe("when clicking hide", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("layer-list-item:" + id + ":toggle-visibility"); + test.beforeEach(async () => { + await 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({ + test("should update visibility to none in local storage", async () => { + await then(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.beforeEach(async () => { + await 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({ + test("should update visibility to visible in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "background", layout: { visibility: "visible" } }], }); }); @@ -69,15 +72,14 @@ test.describe("layers list", () => { test.describe("when selecting a layer", () => { let secondId: string; - test.beforeEach(async ({ driver }) => { - await driver.when.modal.open(); - secondId = await driver.when.modal.fillLayers({ + test.beforeEach(async () => { + await when.modal.open(); + secondId = await when.modal.fillLayers({ id: "second-layer", type: "background", }); }); - test("should show the selected layer in the editor", async ({ driver }) => { - const { get, when, then } = driver; + test("should show the selected layer in the editor", async () => { 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); @@ -88,8 +90,7 @@ test.describe("layers list", () => { }); test.describe("background", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "background" }); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "background" }], @@ -100,8 +101,7 @@ test.describe("layers list", () => { }); test.describe("fill", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "fill", layer: "example" }); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "fill", source: "example" }], @@ -113,16 +113,14 @@ test.describe("layers list", () => { }); test.describe("line", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { 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; + test("groups", async () => { await when.modal.open(); const id1 = await when.modal.fillLayers({ id: "aa", type: "line", layer: "example" }); @@ -154,16 +152,14 @@ test.describe("layers list", () => { }); test.describe("symbol", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { 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; + test("should show spec info when hovering and clicking single line property", async () => { await when.modal.fillLayers({ type: "symbol", layer: "example" }); await when.hover("spec-field-container:text-rotate"); @@ -172,8 +168,7 @@ test.describe("layers list", () => { 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; + test("should show spec info when hovering and clicking multi line property", async () => { await when.modal.fillLayers({ type: "symbol", layer: "example" }); await when.hover("spec-field-container:text-offset"); @@ -182,8 +177,7 @@ test.describe("layers list", () => { 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; + test("should hide spec info when clicking a second time", async () => { await when.modal.fillLayers({ type: "symbol", layer: "example" }); await when.hover("spec-field-container:text-rotate"); @@ -196,8 +190,7 @@ test.describe("layers list", () => { }); test.describe("raster", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "raster", layer: "raster" }); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "raster", source: "raster" }], @@ -206,8 +199,7 @@ test.describe("layers list", () => { }); test.describe("circle", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "circle", layer: "example" }); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "circle", source: "example" }], @@ -216,8 +208,7 @@ test.describe("layers list", () => { }); test.describe("fill extrusion", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { const id = await when.modal.fillLayers({ type: "fill-extrusion", layer: "example" }); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [{ id, type: "fill-extrusion", source: "example" }], @@ -226,16 +217,14 @@ test.describe("layers list", () => { }); test.describe("hillshade", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { 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; + test("set hillshade illumination direction array", async () => { const id = await when.modal.fillLayers({ type: "hillshade", layer: "example" }); await when.collapseGroupInLayerEditor(); await when.collapseGroupInLayerEditor(1); @@ -256,8 +245,7 @@ test.describe("layers list", () => { }); }); - test("set hillshade highlight color array", async ({ driver }) => { - const { get, when, then } = driver; + test("set hillshade highlight color array", async () => { const id = await when.modal.fillLayers({ type: "hillshade", layer: "example" }); await when.collapseGroupInLayerEditor(); await when.setValueToPropertyArray("spec-field:hillshade-highlight-color", "blue"); @@ -280,16 +268,14 @@ test.describe("layers list", () => { }); test.describe("color-relief", () => { - test("add", async ({ driver }) => { - const { get, when, then } = driver; + test("add", async () => { 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; + test("adds elevation expression when clicking the elevation button", async () => { await when.modal.fillLayers({ type: "color-relief", layer: "example" }); await when.collapseGroupInLayerEditor(); await when.click("make-elevation-function"); @@ -300,8 +286,7 @@ test.describe("layers list", () => { }); test.describe("groups", () => { - test("simple", async ({ driver }) => { - const { get, when, then } = driver; + test("simple", async () => { await when.setStyle("geojson"); await when.modal.open(); @@ -324,8 +309,7 @@ test.describe("layers list", () => { }); test.describe("drag and drop", () => { - test("move layer should update local storage", async ({ driver }) => { - const { get, when, then } = driver; + test("move layer should update local storage", async () => { await when.modal.open(); const firstId = await when.modal.fillLayers({ id: "a", type: "background" }); await when.modal.open(); @@ -346,8 +330,7 @@ test.describe("layers list", () => { }); test.describe("sticky header", () => { - test("should keep header visible when scrolling layer list", async ({ driver }) => { - const { get, when, then } = driver; + test("should keep header visible when scrolling layer list", async () => { // Setup: Create multiple layers to enable scrolling for (let i = 0; i < 20; i++) { await when.modal.open(); diff --git a/e2e/map.spec.ts b/e2e/map.spec.ts index 415c73cd..48165a1a 100644 --- a/e2e/map.spec.ts +++ b/e2e/map.spec.ts @@ -1,19 +1,23 @@ -import { test, setupMaputnik } from "./fixtures"; +import { test } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; test.describe("map", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); + + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + await when.setStyle("both"); + }); test.describe("zoom level", () => { - test("via url", async ({ driver }) => { - const { get, when, then } = driver; + test("via url", async () => { const zoomLevel = 12.37; await when.setStyle("geojson", zoomLevel); await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText("Zoom: " + zoomLevel); }); - test("via map controls", async ({ driver }) => { - const { get, when, then } = driver; + test("via map controls", async () => { const zoomLevel = 12.37; await when.setStyle("geojson", zoomLevel); await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); @@ -21,8 +25,7 @@ test.describe("map", () => { await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText("Zoom: " + (zoomLevel + 1)); }); - test("via style file definition", async ({ driver }) => { - const { get, when, then } = driver; + test("via style file definition", async () => { await when.setStyle("zoom_7_center_0_51"); await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); await then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText("Zoom: " + 7); @@ -36,26 +39,23 @@ test.describe("map", () => { }); test.describe("search", () => { - test("should exist", async ({ driver }) => { - const { get, then } = driver; + test("should exist", async () => { await then(get.searchControl()).shouldBeVisible(); }); }); test.describe("popup", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("rectangles"); - await driver.then(driver.get.locationHash()).shouldExist(); + test.beforeEach(async () => { + await when.setStyle("rectangles"); + await then(get.locationHash()).shouldExist(); }); - test("should open on feature click", async ({ driver }) => { - const { get, when, then } = driver; + test("should open on feature click", async () => { await when.clickCenter("maplibre:map"); await then(get.elementByTestId("feature-layer-popup")).shouldBeVisible(); }); - test("should open a second feature after closing popup", async ({ driver }) => { - const { get, when, then } = driver; + test("should open a second feature after closing popup", async () => { await when.clickCenter("maplibre:map"); await then(get.elementByTestId("feature-layer-popup")).shouldBeVisible(); await when.closePopup(); diff --git a/e2e/maputnik-driver.ts b/e2e/maputnik-driver.ts index 02cf9376..0e5f5dc1 100644 --- a/e2e/maputnik-driver.ts +++ b/e2e/maputnik-driver.ts @@ -1,8 +1,9 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { expect, type Locator, type Page, type Request, type TestInfo } from "@playwright/test"; -import { readCoverage, writeCoverage } from "./coverage"; +import { expect, type Locator, type Page, type Request } from "@playwright/test"; +import { readCoverage } from "./coverage"; +import { currentPage, recordCoverageChunk } from "./fixtures"; import { ModalDriver } from "./modal-driver"; const baseUrl = "http://localhost:8888/"; @@ -205,28 +206,14 @@ async function centerOf(locator: Locator): Promise<{ x: number; y: number }> { export class MaputnikDriver { private scope: Locator | null = null; private readonly recordedRequests = new Map(); - private readonly coverageChunks: unknown[] = []; private readonly modalDriver = new ModalDriver(this); - constructor(private readonly page: Page) { - // Accept confirm dialogs (e.g. the "replace current style" prompt shown when - // a style is loaded via the URL). Playwright dismisses dialogs by default. - page.on("dialog", (dialog) => dialog.accept().catch(() => undefined)); - } - - // ---- Coverage collection ------------------------------------------------- - - /** Snapshots the current coverage before the page is unloaded. */ - private async captureCoverage(): Promise { - const coverage = await readCoverage(this.page); - if (coverage) this.coverageChunks.push(coverage); - } - - async flushCoverage(testInfo: TestInfo): Promise { - await this.captureCoverage(); - this.coverageChunks.forEach((chunk, index) => { - writeCoverage(chunk, `${testInfo.testId}-${index}`); - }); + /** + * The page for the currently running test. Resolved lazily so a single driver + * instance can be created once per `describe` and reused across its tests. + */ + private get page(): Page { + return currentPage(); } // ---- Element access ------------------------------------------------------ @@ -315,7 +302,8 @@ export class MaputnikDriver { modal: this.modalDriver.when, visit: async (url: string) => { - await this.captureCoverage(); + // Snapshot coverage before navigating, since a full page load resets it. + recordCoverageChunk(await readCoverage(this.page)); const target = url.startsWith("http") ? url : new URL(url, baseUrl).toString(); await this.page.goto(target); }, diff --git a/e2e/modals.spec.ts b/e2e/modals.spec.ts index 8fdfbdd4..e75880cd 100644 --- a/e2e/modals.spec.ts +++ b/e2e/modals.spec.ts @@ -1,54 +1,54 @@ -import { test, expect, setupMaputnik } from "./fixtures"; +import { test, expect } from "./fixtures"; +import { MaputnikDriver } from "./maputnik-driver"; import tokens from "../src/config/tokens.json" with { type: "json" }; test.describe("modals", () => { - setupMaputnik(); + const { given, get, when, then } = new MaputnikDriver(); - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle(""); + test.beforeEach(async () => { + await given.setupMockBackedResponses(); + // Load a style first so it is persisted to localStorage, then reset the URL + // to the root (no style param) — several tests read the stored style. + await when.setStyle("both"); + await when.setStyle(""); }); test.describe("open", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("nav:open"); + test.beforeEach(async () => { + await when.click("nav:open"); }); - test("close", async ({ driver }) => { - const { get, when, then } = driver; + test("close", async () => { await when.modal.close("modal:open"); await then(get.elementByTestId("modal:open")).shouldNotExist(); }); - test("upload", async ({ driver }) => { - const { get, when, then } = driver; + test("upload", async () => { await when.chooseExampleFile(); await then(get.fixture("example-style.json")).shouldEqualToStoredStyle(); }); - test("upload via drag and drop", async ({ driver }) => { - const { get, when, then } = driver; + test("upload via drag and drop", async () => { await when.dropExampleFile(); await then(get.fixture("example-style.json")).shouldEqualToStoredStyle(); }); test.describe("when click open url", () => { - test.beforeEach(async ({ driver }) => { - const { get, when } = driver; + test.beforeEach(async () => { const styleFileUrl = get.exampleFileUrl(); await when.setValue("modal:open.url.input", styleFileUrl); await when.click("modal:open.url.button"); await when.wait(200); }); - test("load from url", async ({ driver }) => { - await driver.then(driver.get.responseBody("example-style.json")).shouldEqualToStoredStyle(); + test("load from url", async () => { + await then(get.responseBody("example-style.json")).shouldEqualToStoredStyle(); }); }); }); test.describe("shortcuts", () => { - test("open/close", async ({ driver }) => { - const { get, when, then } = driver; + test("open/close", async () => { await when.setStyle(""); await when.typeKeys("?"); await when.modal.close("modal:shortcuts"); @@ -57,12 +57,11 @@ test.describe("modals", () => { }); test.describe("export", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("nav:export"); + test.beforeEach(async () => { + await when.click("nav:export"); }); - test("close", async ({ driver }) => { - const { get, when, then } = driver; + test("close", async () => { await when.modal.close("modal:export"); await then(get.elementByTestId("modal:export")).shouldNotExist(); }); @@ -72,16 +71,15 @@ test.describe("modals", () => { }); test.describe("sources", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("layer"); - await driver.when.click("nav:sources"); + test.beforeEach(async () => { + await when.setStyle("layer"); + await when.click("nav:sources"); }); test.skip("active sources", () => {}); test.skip("public source", () => {}); - test("add new source", async ({ driver }) => { - const { get, when, then } = driver; + test("add new source", async () => { const sourceId = "n1z2v3r"; await when.setValue("modal:sources.add.source_id", sourceId); await when.select("modal:sources.add.source_type", "tile_vector"); @@ -93,8 +91,7 @@ test.describe("modals", () => { }); }); - test("add new pmtiles source", async ({ driver }) => { - const { get, when, then } = driver; + test("add new pmtiles source", async () => { const sourceId = "pmtilestest"; await when.setValue("modal:sources.add.source_id", sourceId); await when.select("modal:sources.add.source_type", "pmtiles_vector"); @@ -115,8 +112,7 @@ test.describe("modals", () => { }); }); - test("add new raster source", async ({ driver }) => { - const { get, when, then } = driver; + test("add new raster source", async () => { const sourceId = "rastertest"; await when.setValue("modal:sources.add.source_id", sourceId); await when.select("modal:sources.add.source_type", "tile_raster"); @@ -131,59 +127,56 @@ test.describe("modals", () => { }); test.describe("inspect", () => { - test("toggle", async ({ driver }) => { + test("toggle", async () => { // There is no assertion in this test - await driver.when.setStyle("geojson"); - await driver.when.select("maputnik-select", "inspect"); + await when.setStyle("geojson"); + await when.select("maputnik-select", "inspect"); }); }); test.describe("style settings", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("nav:settings"); + test.beforeEach(async () => { + await when.click("nav:settings"); }); test.describe("when click name filed spec information", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("field-doc-button-Name"); + test.beforeEach(async () => { + await when.click("field-doc-button-Name"); }); - test("should show the spec information", async ({ driver }) => { - await driver.then(driver.get.elementsText("spec-field-doc")).shouldInclude("name for the style"); + test("should show the spec information", async () => { + await then(get.elementsText("spec-field-doc")).shouldInclude("name for the style"); }); }); test.describe("when set name and click owner", () => { - test.beforeEach(async ({ driver }) => { - const { when } = driver; + test.beforeEach(async () => { await when.setValue("modal:settings.name", "foobar"); await when.click("modal:settings.owner"); await when.wait(200); }); - test("show name specifications", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("show name specifications", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ name: "foobar", }); }); }); test.describe("when set owner and click name", () => { - test.beforeEach(async ({ driver }) => { - const { when } = driver; + test.beforeEach(async () => { await when.setValue("modal:settings.owner", "foobar"); await when.click("modal:settings.name"); await when.wait(200); }); - test("should update owner in local storage", async ({ driver }) => { - await driver.then(driver.get.styleFromLocalStorage()).shouldDeepNestedInclude({ + test("should update owner in local storage", async () => { + await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ owner: "foobar", }); }); }); - test("sprite url", async ({ driver }) => { - const { get, when, then } = driver; + test("sprite url", async () => { await when.setTextInJsonEditor('"http://example.com"'); await when.click("modal:settings.name"); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ @@ -191,8 +184,7 @@ test.describe("modals", () => { }); }); - test("sprite object", async ({ driver }) => { - const { get, when, then } = driver; + test("sprite object", async () => { await when.setTextInJsonEditor(JSON.stringify([{ id: "1", url: "2" }])); await when.click("modal:settings.name"); @@ -201,8 +193,7 @@ test.describe("modals", () => { }); }); - test("glyphs url", async ({ driver }) => { - const { get, when, then } = driver; + test("glyphs url", async () => { const glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"; await when.setValue("modal:settings.glyphs", glyphsUrl); await when.click("modal:settings.name"); @@ -211,8 +202,7 @@ test.describe("modals", () => { }); }); - test("maptiler access token", async ({ driver }) => { - const { get, when, then } = driver; + test("maptiler access token", async () => { const apiKey = "testing123"; await when.setValue("modal:settings.maputnik:openmaptiles_access_token", apiKey); await when.click("modal:settings.name"); @@ -221,8 +211,7 @@ test.describe("modals", () => { }); }); - test("thunderforest access token", async ({ driver }) => { - const { get, when, then } = driver; + test("thunderforest access token", async () => { const apiKey = "testing123"; await when.setValue("modal:settings.maputnik:thunderforest_access_token", apiKey); await when.click("modal:settings.name"); @@ -231,8 +220,7 @@ test.describe("modals", () => { }); }); - test("stadia access token", async ({ driver }) => { - const { get, when, then } = driver; + test("stadia access token", async () => { const apiKey = "testing123"; await when.setValue("modal:settings.maputnik:stadia_access_token", apiKey); await when.click("modal:settings.name"); @@ -241,8 +229,7 @@ test.describe("modals", () => { }); }); - test("locationiq access token", async ({ driver }) => { - const { get, when, then } = driver; + test("locationiq access token", async () => { const apiKey = "testing123"; await when.setValue("modal:settings.maputnik:locationiq_access_token", apiKey); await when.click("modal:settings.name"); @@ -251,32 +238,28 @@ test.describe("modals", () => { }); }); - test("style projection mercator", async ({ driver }) => { - const { get, when, then } = driver; + test("style projection mercator", async () => { await when.select("modal:settings.projection", "mercator"); await then(get.styleFromLocalStorage().then((style) => style.projection)).shouldInclude({ type: "mercator", }); }); - test("style projection globe", async ({ driver }) => { - const { get, when, then } = driver; + test("style projection globe", async () => { await when.select("modal:settings.projection", "globe"); await then(get.styleFromLocalStorage().then((style) => style.projection)).shouldInclude({ type: "globe", }); }); - test("style projection vertical-perspective", async ({ driver }) => { - const { get, when, then } = driver; + test("style projection vertical-perspective", async () => { await when.select("modal:settings.projection", "vertical-perspective"); await then(get.styleFromLocalStorage().then((style) => style.projection)).shouldInclude({ type: "vertical-perspective", }); }); - test("style renderer", async ({ driver }) => { - const { get, when, then } = driver; + test("style renderer", async () => { await when.select("modal:settings.maputnik:renderer", "ol"); await then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("ol"); @@ -286,9 +269,7 @@ test.describe("modals", () => { }); }); - test("include API key when change renderer", async ({ driver }) => { - const { get, when, given } = driver; - + test("include API key when change renderer", async () => { await when.click("modal:settings.close-modal"); await when.click("nav:open"); @@ -297,10 +278,10 @@ test.describe("modals", () => { await when.click("nav:settings"); await when.select("modal:settings.maputnik:renderer", "mlgljs"); - await driver.then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("mlgljs"); + await then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("mlgljs"); await when.select("modal:settings.maputnik:renderer", "ol"); - await driver.then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("ol"); + await then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("ol"); await given.intercept( "https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=*", @@ -309,7 +290,7 @@ test.describe("modals", () => { ); await when.select("modal:settings.maputnik:renderer", "mlgljs"); - await driver.then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("mlgljs"); + await then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual("mlgljs"); const request = await when.waitForResponse("tileRequest"); expect(request.url()).toContain( @@ -319,13 +300,12 @@ test.describe("modals", () => { }); test.describe("add layer", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.setStyle("layer"); - await driver.when.modal.open(); + test.beforeEach(async () => { + await when.setStyle("layer"); + await when.modal.open(); }); - test("shows duplicate id error", async ({ driver }) => { - const { get, when, then } = driver; + test("shows duplicate id error", async () => { await when.setValue("add-layer.layer-id.input", "background"); await when.click("add-layer"); await then(get.elementByTestId("modal:add-layer")).shouldExist(); @@ -338,12 +318,11 @@ test.describe("modals", () => { }); test.describe("global state", () => { - test.beforeEach(async ({ driver }) => { - await driver.when.click("nav:global-state"); + test.beforeEach(async () => { + await when.click("nav:global-state"); }); - test("add variable", async ({ driver }) => { - const { get, when, then } = driver; + test("add variable", async () => { await when.wait(100); await when.click("global-state-add-variable"); await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ @@ -351,8 +330,7 @@ test.describe("modals", () => { }); }); - test("add multiple variables", async ({ driver }) => { - const { get, when, then } = driver; + test("add multiple variables", async () => { await when.click("global-state-add-variable"); await when.click("global-state-add-variable"); await when.click("global-state-add-variable"); @@ -362,8 +340,7 @@ test.describe("modals", () => { }); }); - test("remove variable", async ({ driver }) => { - const { get, when, then } = driver; + test("remove variable", async () => { await when.click("global-state-add-variable"); await when.click("global-state-add-variable"); await when.click("global-state-add-variable"); @@ -374,8 +351,7 @@ test.describe("modals", () => { }); }); - test("edit variable key", async ({ driver }) => { - const { get, when, then } = driver; + test("edit variable key", async () => { await when.click("global-state-add-variable"); await when.wait(100); await when.setValue("global-state-variable-key:0", "mykey"); @@ -386,8 +362,7 @@ test.describe("modals", () => { }); }); - test("edit variable value", async ({ driver }) => { - const { get, when, then } = driver; + test("edit variable value", async () => { await when.click("global-state-add-variable"); await when.wait(100); await when.setValue("global-state-variable-value:0", "myvalue"); @@ -400,12 +375,11 @@ test.describe("modals", () => { }); test.describe("error panel", () => { - test("not visible when no errors", async ({ driver }) => { - await driver.then(driver.get.element("maputnik-message-panel-error")).shouldNotExist(); + test("not visible when no errors", async () => { + await then(get.element("maputnik-message-panel-error")).shouldNotExist(); }); - test("visible on style error", async ({ driver }) => { - const { get, when, then } = driver; + test("visible on style error", async () => { await when.modal.open(); await when.modal.fillLayers({ type: "circle", @@ -416,8 +390,7 @@ test.describe("modals", () => { }); test.describe("Handle localStorage QuotaExceededError", () => { - test("handles quota exceeded error when opening style from URL", async ({ driver, page }) => { - const { get, when, then } = driver; + test("handles quota exceeded error when opening style from URL", async ({ page }) => { // Clear localStorage to start fresh await when.clearLocalStorage();