diff --git a/.eslintrc b/.eslintrc index 996d01b4..d85160a0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -37,7 +37,8 @@ "react/prop-types": ["off"], // Disable no-undef. It's covered by @typescript-eslint "no-undef": "off", - "indent": ["error", 2] + "indent": ["error", 2], + "no-var": ["error"] }, "globals": { "global": "readonly" diff --git a/cypress/e2e/accessibility.cy.ts b/cypress/e2e/accessibility.cy.ts index 30231ac9..b435ae14 100644 --- a/cypress/e2e/accessibility.cy.ts +++ b/cypress/e2e/accessibility.cy.ts @@ -1,7 +1,7 @@ -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("accessibility", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, when, should } = new MaputnikDriver(); beforeAndAfter(); describe("skip links", () => { diff --git a/cypress/e2e/cypress-wrapper-driver.ts b/cypress/e2e/cypress-wrapper-driver.ts new file mode 100644 index 00000000..c092ee28 --- /dev/null +++ b/cypress/e2e/cypress-wrapper-driver.ts @@ -0,0 +1,41 @@ +import { CypressHelper } from "@shellygo/cypress-test-utils"; + +export default class CypressWrapperDriver { + private helper = new CypressHelper({ defaultDataAttribute: "data-wd-key" }); + + public given = { + ...this.helper.given, + /** + * + * @param url a url to a file, this assumes the file name is the last part of the url + * @param alias + */ + interceptGetToFile(url: string) { + let fileNameAndAlias = url.split('/').pop(); + cy.intercept('GET', url, { fixture: fileNameAndAlias }).as(fileNameAndAlias!); + }, + + interceptAndIgnore(url: string) { + cy.intercept({ method: "GET", url }, []); + } + } + + public get = { + ...this.helper.get, + elementByClassOrType(slector: string) { + return cy.get(slector); + } + } + + public when = { + ...this.helper.when, + visit(address: string) { + cy.visit(address); + }, + confirmAlert() { + cy.on("window:confirm", () => true); + } + } + + public beforeAndAfter = this.helper.beforeAndAfter; +} \ No newline at end of file diff --git a/cypress/e2e/history.cy.ts b/cypress/e2e/history.cy.ts index 59b6001e..0d91174d 100644 --- a/cypress/e2e/history.cy.ts +++ b/cypress/e2e/history.cy.ts @@ -1,7 +1,7 @@ -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("history", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, when, get, should } = new MaputnikDriver(); beforeAndAfter(); let undoKeyCombo: string; @@ -15,11 +15,11 @@ describe("history", () => { it("undo/redo", () => { when.setStyle("geojson"); - when.openLayersModal(); + when.modal.open(); should.equalStyleStore((a: any) => a.layers, []); - when.fillLayersModal({ + when.modal.fillLayers({ id: "step 1", type: "background", }); @@ -34,8 +34,8 @@ describe("history", () => { ] ); - when.openLayersModal(); - when.fillLayersModal({ + when.modal.open(); + when.modal.fillLayers({ id: "step 2", type: "background", }); diff --git a/cypress/e2e/keyboard.cy.ts b/cypress/e2e/keyboard.cy.ts index 8cb5b1ce..46afbedd 100644 --- a/cypress/e2e/keyboard.cy.ts +++ b/cypress/e2e/keyboard.cy.ts @@ -1,7 +1,7 @@ -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("keyboard", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, given, when, should } = new MaputnikDriver(); beforeAndAfter(); describe("shortcuts", () => { beforeEach(() => { diff --git a/cypress/e2e/layers.cy.ts b/cypress/e2e/layers.cy.ts index ad685c42..4156eaba 100644 --- a/cypress/e2e/layers.cy.ts +++ b/cypress/e2e/layers.cy.ts @@ -1,17 +1,17 @@ import { v1 as uuid } from "uuid"; -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("layers", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, when, should } = new MaputnikDriver(); beforeAndAfter(); beforeEach(() => { when.setStyle("both"); - when.openLayersModal(); + when.modal.open(); }); describe("ops", () => { it("delete", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "background", }); @@ -31,8 +31,7 @@ describe("layers", () => { }); it("duplicate", () => { - var styleObj; - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "background", }); @@ -64,8 +63,7 @@ describe("layers", () => { }); it("hide", () => { - var styleObj; - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "background", }); @@ -113,7 +111,7 @@ describe("layers", () => { describe("background", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "background", }); @@ -131,7 +129,7 @@ describe("layers", () => { describe("modify", () => { function createBackground() { // Setup - var id = uuid(); + let id = uuid(); when.selectWithin("add-layer.layer-type", "background"); when.setValue("add-layer.layer-id.input", "background:" + id); @@ -154,11 +152,11 @@ describe("layers", () => { describe("layer", () => { it("expand/collapse"); it("id", () => { - var bgId = createBackground(); + let bgId = createBackground(); when.click("layer-list-item:background:" + bgId); - var id = uuid(); + let id = uuid(); when.setValue("layer-editor.layer-id.input", "foobar:" + id); when.click("min-zoom"); @@ -174,7 +172,7 @@ describe("layers", () => { }); it("min-zoom", () => { - var bgId = createBackground(); + let bgId = createBackground(); when.click("layer-list-item:background:" + bgId); when.setValue("min-zoom.input-text", "1"); @@ -203,7 +201,7 @@ describe("layers", () => { }); it("max-zoom", () => { - var bgId = createBackground(); + let bgId = createBackground(); when.click("layer-list-item:background:" + bgId); when.setValue("max-zoom.input-text", "1"); @@ -223,8 +221,8 @@ describe("layers", () => { }); it("comments", () => { - var bgId = createBackground(); - var comment = "42"; + let bgId = createBackground(); + let comment = "42"; when.click("layer-list-item:background:" + bgId); when.setValue("layer-comment.input", comment); @@ -255,7 +253,7 @@ describe("layers", () => { }); it("color", () => { - var bgId = createBackground(); + let bgId = createBackground(); when.click("layer-list-item:background:" + bgId); @@ -292,11 +290,11 @@ describe("layers", () => { // TODO it.skip("parse error", () => { - var bgId = createBackground(); + let bgId = createBackground(); when.click("layer-list-item:background:" + bgId); - var errorSelector = ".CodeMirror-lint-marker-error"; + let errorSelector = ".CodeMirror-lint-marker-error"; should.notExist(errorSelector); when.click(".CodeMirror"); @@ -311,7 +309,7 @@ describe("layers", () => { describe("fill", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "fill", layer: "example", }); @@ -334,7 +332,7 @@ describe("layers", () => { describe("line", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "line", layer: "example", }); @@ -359,7 +357,7 @@ describe("layers", () => { describe("symbol", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "symbol", layer: "example", }); @@ -379,7 +377,7 @@ describe("layers", () => { describe("raster", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "raster", layer: "raster", }); @@ -399,7 +397,7 @@ describe("layers", () => { describe("circle", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "circle", layer: "example", }); @@ -419,7 +417,7 @@ describe("layers", () => { describe("fill extrusion", () => { it("add", () => { - var id = when.fillLayersModal({ + let id = when.modal.fillLayers({ type: "fill-extrusion", layer: "example", }); @@ -441,20 +439,20 @@ describe("layers", () => { it("simple", () => { when.setStyle("geojson"); - when.openLayersModal(); - when.fillLayersModal({ + when.modal.open(); + when.modal.fillLayers({ id: "foo", type: "background", }); - when.openLayersModal(); - when.fillLayersModal({ + when.modal.open(); + when.modal.fillLayers({ id: "foo_bar", type: "background", }); - when.openLayersModal(); - when.fillLayersModal({ + when.modal.open(); + when.modal.fillLayers({ id: "foo_bar_baz", type: "background", }); diff --git a/cypress/e2e/map.cy.ts b/cypress/e2e/map.cy.ts index afccd755..6ed22553 100644 --- a/cypress/e2e/map.cy.ts +++ b/cypress/e2e/map.cy.ts @@ -1,18 +1,18 @@ -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("map", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, when, should } = new MaputnikDriver(); beforeAndAfter(); describe("zoom level", () => { it("via url", () => { - var zoomLevel = 12.37; + let zoomLevel = 12.37; when.setStyle("geojson", zoomLevel); should.beVisible("maplibre:ctrl-zoom"); should.containText("maplibre:ctrl-zoom", "Zoom: " + zoomLevel); }); it("via map controls", () => { - var zoomLevel = 12.37; + let zoomLevel = 12.37; when.setStyle("geojson", zoomLevel); should.beVisible("maplibre:ctrl-zoom"); diff --git a/cypress/e2e/driver.ts b/cypress/e2e/maputnik-driver.ts similarity index 53% rename from cypress/e2e/driver.ts rename to cypress/e2e/maputnik-driver.ts index d1c142fb..965b1083 100644 --- a/cypress/e2e/driver.ts +++ b/cypress/e2e/maputnik-driver.ts @@ -1,7 +1,12 @@ -import { CypressHelper } from "@shellygo/cypress-test-utils"; -import { v1 as uuid } from "uuid"; +import CypressWrapperDriver from "./cypress-wrapper-driver"; +import ModalDriver from "./modal-driver"; + +const SERVER_ADDRESS = "http://localhost:8888/"; + export default class MaputnikDriver { - private helper = new CypressHelper({ defaultDataAttribute: "data-wd-key" }); + private helper = new CypressWrapperDriver(); + private modalDriver = new ModalDriver(); + public beforeAndAfter = () => { beforeEach(() => { this.given.setupInterception(); @@ -11,36 +16,28 @@ export default class MaputnikDriver { public given = { setupInterception: () => { - cy.intercept("GET", "http://localhost:8888/example-style.json", { - fixture: "example-style.json", - }).as("example-style.json"); - cy.intercept("GET", "http://localhost:8888/example-layer-style.json", { - fixture: "example-layer-style.json", - }); - cy.intercept("GET", "http://localhost:8888/geojson-style.json", { - fixture: "geojson-style.json", - }); - cy.intercept("GET", "http://localhost:8888/raster-style.json", { - fixture: "raster-style.json", - }); - cy.intercept("GET", "http://localhost:8888/geojson-raster-style.json", { - fixture: "geojson-raster-style.json", - }); - cy.intercept({ method: "GET", url: "*example.local/*" }, []); - cy.intercept({ method: "GET", url: "*example.com/*" }, []); + this.helper.given.interceptGetToFile(SERVER_ADDRESS + "example-style.json"); + this.helper.given.interceptGetToFile(SERVER_ADDRESS + "example-layer-style.json"); + this.helper.given.interceptGetToFile(SERVER_ADDRESS + "geojson-style.json"); + this.helper.given.interceptGetToFile(SERVER_ADDRESS + "raster-style.json"); + this.helper.given.interceptGetToFile(SERVER_ADDRESS + "geojson-raster-style.json"); + + this.helper.given.interceptAndIgnore("*example.local/*"); + this.helper.given.interceptAndIgnore("*example.com/*"); }, }; public when = { + modal: this.modalDriver.when, within: (selector: string, fn: () => void) => { this.helper.when.within(fn, selector); }, - tab: () => cy.get("body").tab(), + tab: () => this.helper.get.elementByClassOrType("body").tab(), waitForExampleFileRequset: () => { this.helper.when.waitForResponse("example-style.json"); }, chooseExampleFile: () => { - cy.get("input[type='file']").selectFile( + this.helper.get.elementByClassOrType("input[type='file']").selectFile( "cypress/fixtures/example-style.json", { force: true } ); @@ -51,55 +48,34 @@ export default class MaputnikDriver { ) => { let url = "?debug"; switch (styleProperties) { - case "geojson": - url += "&style=http://localhost:8888/geojson-style.json"; - break; - case "raster": - url += "&style=http://localhost:8888/raster-style.json"; - break; - case "both": - url += "&style=http://localhost:8888/geojson-raster-style.json"; - break; - case "layer": - url += "&style=http://localhost:8888/example-layer-style.json"; - break; + case "geojson": + url += `&style=${SERVER_ADDRESS}geojson-style.json`; + break; + case "raster": + url += `&style=${SERVER_ADDRESS}raster-style.json`; + break; + case "both": + url += `&style=${SERVER_ADDRESS}geojson-raster-style.json`; + break; + case "layer": + url += `&style=${SERVER_ADDRESS}/example-layer-style.json`; + break; } if (zoom) { - url += "#" + zoom + "/41.3805/2.1635"; + url += `#${zoom}/41.3805/2.1635`; } - cy.visit("http://localhost:8888/" + url); + this.helper.when.visit(SERVER_ADDRESS + url); if (styleProperties) { - cy.on("window:confirm", () => true); + this.helper.when.confirmAlert(); } this.helper.get.element("toolbar:link").should("be.visible"); }, - fillLayersModal: (opts: {type: string, layer?: string, id?: string}) => { - var type = opts.type; - var layer = opts.layer; - var id; - if (opts.id) { - id = opts.id; - } else { - id = `${type}:${uuid()}`; - } - - this.helper.get.element("add-layer.layer-type.select").select(type); - this.helper.get.element("add-layer.layer-id.input").type(id); - if (layer) { - this.when.within("add-layer.layer-source-block", () => { - cy.get("input").type(layer!); - }) - } - this.when.click("add-layer"); - - return id; - }, typeKeys: (keys: string, selector?: string) => { if (selector) { this.helper.get.element(selector).type(keys); } else { - cy.get("body").type(keys); + this.helper.get.elementByClassOrType("body").type(keys); } }, @@ -108,12 +84,12 @@ export default class MaputnikDriver { }, clickZoomin: () => { - cy.get(".maplibregl-ctrl-zoom-in").click(); + this.helper.get.elementByClassOrType(".maplibregl-ctrl-zoom-in").click(); }, selectWithin: (selector: string, value: string) => { this.when.within(selector, () => { - cy.get("select").select(value); + this.helper.get.elementByClassOrType("select").select(value); }); }, @@ -128,18 +104,6 @@ export default class MaputnikDriver { setValue: (selector: string, text: string) => { this.helper.get.element(selector).clear().type(text, { parseSpecialCharSequences: false }); }, - - closeModal: (key: string) => { - this.helper.when.waitUntil(() => this.helper.get.element(key)); - this.when.click(key + ".close-modal"); - }, - - openLayersModal: () => { - this.helper.when.click("layer-list:add-layer"); - - this.helper.get.element("modal:add-layer").should("exist"); - this.helper.get.element("modal:add-layer").should("be.visible"); - }, }; public get = { @@ -153,18 +117,18 @@ export default class MaputnikDriver { return obj; }, exampleFileUrl: () => { - return "http://localhost:8888/example-style.json"; + return SERVER_ADDRESS + "example-style.json"; }, }; public should = { canvasBeFocused: () => { this.when.within("maplibre:map", () => { - cy.get("canvas").should("be.focused"); + this.helper.get.elementByClassOrType("canvas").should("be.focused"); }); }, notExist: (selector: string) => { - cy.get(selector).should("not.exist"); + this.helper.get.elementByClassOrType(selector).should("not.exist"); }, beFocused: (selector: string) => { this.helper.get.element(selector).should("have.focus"); @@ -192,7 +156,7 @@ export default class MaputnikDriver { styleStoreEqualToExampleFileData: () => { cy.window().then((win: any) => { const obj = this.get.styleFromWindow(win); - cy.fixture("example-style.json").should("deep.equal", obj); + this.helper.given.fixture("example-style.json", "file:example-style.json").should("deep.equal", obj); }); }, diff --git a/cypress/e2e/modal-driver.ts b/cypress/e2e/modal-driver.ts new file mode 100644 index 00000000..aff69e7b --- /dev/null +++ b/cypress/e2e/modal-driver.ts @@ -0,0 +1,42 @@ +import { v1 as uuid } from "uuid"; +import CypressWrapperDriver from "./cypress-wrapper-driver"; + +export default class ModalDriver { + private helper = new CypressWrapperDriver(); + + public when = { + fillLayers: (opts: {type: string, layer?: string, id?: string}) => { + let type = opts.type; + let layer = opts.layer; + let id; + if (opts.id) { + id = opts.id; + } else { + id = `${type}:${uuid()}`; + } + + this.helper.get.element("add-layer.layer-type.select").select(type); + this.helper.get.element("add-layer.layer-id.input").type(id); + if (layer) { + this.helper.when.within(() => { + this.helper.get.elementByClassOrType("input").type(layer!); + }, "add-layer.layer-source-block") + } + this.helper.when.click("add-layer"); + + return id; + }, + + open: () => { + this.helper.when.click("layer-list:add-layer"); + + this.helper.get.element("modal:add-layer").should("exist"); + this.helper.get.element("modal:add-layer").should("be.visible"); + }, + + close: (key: string) => { + this.helper.when.waitUntil(() => this.helper.get.element(key)); + this.helper.when.click(key + ".close-modal"); + }, + } +} \ No newline at end of file diff --git a/cypress/e2e/modals.cy.ts b/cypress/e2e/modals.cy.ts index 117a159a..b0713fb0 100644 --- a/cypress/e2e/modals.cy.ts +++ b/cypress/e2e/modals.cy.ts @@ -1,7 +1,7 @@ -import MaputnikDriver from "./driver"; +import MaputnikDriver from "./maputnik-driver"; describe("modals", () => { - let { beforeAndAfter, given, when, get, should } = new MaputnikDriver(); + let { beforeAndAfter, when, get, should } = new MaputnikDriver(); beforeAndAfter(); beforeEach(() => { when.setStyle(""); @@ -12,7 +12,7 @@ describe("modals", () => { }); it("close", () => { - when.closeModal("modal:open"); + when.modal.close("modal:open"); should.notExist("modal:open"); }); @@ -24,7 +24,7 @@ describe("modals", () => { }); it("load from url", () => { - var styleFileUrl = get.exampleFileUrl(); + let styleFileUrl = get.exampleFileUrl(); when.setValue("modal:open.url.input", styleFileUrl); when.click("modal:open.url.button"); @@ -38,7 +38,7 @@ describe("modals", () => { it("open/close", () => { when.setStyle(""); when.typeKeys("?"); - when.closeModal("modal:shortcuts"); + when.modal.close("modal:shortcuts"); should.notExist("modal:shortcuts"); }); }); @@ -49,7 +49,7 @@ describe("modals", () => { }); it("close", () => { - when.closeModal("modal:export"); + when.modal.close("modal:export"); should.notExist("modal:export"); }); @@ -102,7 +102,7 @@ describe("modals", () => { should.equalStyleStore((obj) => obj.sprite, "http://example.com"); }); it("glyphs url", () => { - var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"; + let glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"; when.setValue("modal:settings.glyphs", glyphsUrl); when.click("modal:settings.name"); @@ -110,7 +110,7 @@ describe("modals", () => { }); it("maptiler access token", () => { - var apiKey = "testing123"; + let apiKey = "testing123"; when.setValue( "modal:settings.maputnik:openmaptiles_access_token", apiKey @@ -124,7 +124,7 @@ describe("modals", () => { }); it("thunderforest access token", () => { - var apiKey = "testing123"; + let apiKey = "testing123"; when.setValue("modal:settings.maputnik:thunderforest_access_token", apiKey); when.click("modal:settings.name"); diff --git a/package.json b/package.json index 2678fa3d..223c6748 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "start": "vite", "build": "tsc && vite build", - "lint": "eslint ./src --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint ./src ./cypress --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0", "test": "cypress run", "cy:open": "cypress open", "lint-css": "stylelint \"src/styles/*.scss\"", diff --git a/tsconfig.json b/tsconfig.json index 4c098757..24f33888 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,7 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"], + "include": ["src", "cypress/e2e"], "references": [{ "path": "./tsconfig.node.json" }], // TODO: Remove when issue is resolved https://github.com/cypress-io/cypress/issues/27448 "ts-node": {