diff --git a/cypress/e2e/accessibility.cy.ts b/cypress/e2e/accessibility.cy.ts
index 17c71b62..5a47f1ad 100644
--- a/cypress/e2e/accessibility.cy.ts
+++ b/cypress/e2e/accessibility.cy.ts
@@ -1,41 +1,39 @@
-import driver from "./driver";
+import MaputnikDriver from "./driver";
describe("accessibility", () => {
- // skipped due to the following issue with cypress: https://github.com/cypress-io/cypress/issues/299
- describe.skip("skip links", () => {
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
+
+ describe("skip links", () => {
beforeEach(() => {
- driver.beforeEach();
- driver.setStyle("layer");
+ when.setStyle("layer");
});
-
+
it("skip link to layer list", () => {
- const selector = driver.getDataAttribute("root:skip:layer-list");
- driver.isExists(selector);
- driver.typeKeys('{tab}');
- driver.isFocused(selector);
- driver.click(selector);
-
- driver.isFocused("#skip-target-layer-list");
+ const selector = "root:skip:layer-list";
+ should.isExists(selector);
+ when.tab();
+ should.beFocused(selector);
+ when.click(selector);
+ should.beFocused("skip-target-layer-list");
});
-
+
it("skip link to layer editor", () => {
- const selector = driver.getDataAttribute("root:skip:layer-editor");
- driver.isExists(selector);
- driver.typeKeys('{tab}{tab}');
- driver.isFocused(selector);
- driver.click(selector);
-
- driver.isFocused("#skip-target-layer-editor");
+ const selector = "root:skip:layer-editor";
+ should.isExists(selector);
+ when.tab().tab();
+ should.beFocused(selector);
+ when.click(selector);
+ should.beFocused("skip-target-layer-editor");
});
-
+
it("skip link to map view", () => {
- const selector = driver.getDataAttribute("root:skip:map-view");
- driver.isExists(selector);
- driver.typeKeys('{tab}{tab}{tab}');
- driver.isFocused(selector);
- driver.click(selector);
-
- driver.isFocused(".maplibregl-canvas");
+ const selector = "root:skip:map-view";
+ should.isExists(selector);
+ when.tab().tab().tab();
+ should.beFocused(selector);
+ when.click(selector);
+ should.canvasBeFocused();
});
- });
-})
+ });
+});
diff --git a/cypress/e2e/driver.ts b/cypress/e2e/driver.ts
index fcadcd66..16cd60e4 100644
--- a/cypress/e2e/driver.ts
+++ b/cypress/e2e/driver.ts
@@ -1,170 +1,198 @@
-import {v1 as uuid} from "uuid";
+import { CypressHelper } from "@shellygo/cypress-test-utils";
+import { v1 as uuid } from "uuid";
+export default class MaputnikDriver {
+ private helper = new CypressHelper({ defaultDataAttribute: "data-wd-key" });
+ public beforeAndAfter = () => {
+ beforeEach(() => {
+ this.given.setupInterception();
+ this.when.setStyle("both");
+ });
+ };
-export default {
- isMac() {
- return Cypress.platform === "darwin";
+ 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/*" }, []);
},
+ };
- beforeEach() {
- this.setupInterception();
- this.setStyle('both');
+ public when = {
+ within: (selector: string, fn: () => void) => {
+ this.helper.when.within(fn, selector);
},
-
- 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/*' }, []);
+ tab: () => cy.get("body").tab(),
+ waitForExampleFileRequset: () => {
+ this.helper.when.waitForResponse("example-style.json");
},
-
- setStyle(styleProperties: 'geojson' | 'raster' | 'both' | 'layer' | '', zoom? : number) {
- 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;
- }
- if (zoom) {
- url += "#" + zoom + "/41.3805/2.1635";
- }
- cy.visit("http://localhost:8888/" + url);
- if (styleProperties) {
- cy.on('window:confirm', () => true)
- }
- cy.get(".maputnik-toolbar-link").should("be.visible");
- },
-
- getDataAttribute(key: string, selector?: string) {
- return `*[data-wd-key='${key}'] ${selector || ''}`;
- },
-
- closeModal(key: string) {
- const selector = this.getDataAttribute(key);
-
- this.isDisplayedInViewport(selector);
-
- this.click(this.getDataAttribute(key + ".close-modal"));
-
- this.doesNotExists(selector);
- },
-
- openLayersModal() {
- cy.get(this.getDataAttribute('layer-list:add-layer')).click();
-
- cy.get(this.getDataAttribute('modal:add-layer')).should('exist');
- cy.get(this.getDataAttribute('modal:add-layer')).should('be.visible');
- },
-
- getStyleFromWindow(win: Window) {
- const styleId = win.localStorage.getItem("maputnik:latest_style");
- const styleItem = win.localStorage.getItem(`maputnik:style:${styleId}`)
- const obj = JSON.parse(styleItem || "");
- return obj;
- },
-
- isStyleStoreEqual(getter: (obj:any) => any, styleObj: any) {
- cy.window().then((win: any) => {
- const obj = this.getStyleFromWindow(win);
- assert.deepEqual(getter(obj), styleObj);
- });
- },
-
- isStyleStoreEqualToExampleFileData() {
- cy.window().then((win: any) => {
- const obj = this.getStyleFromWindow(win);
- cy.fixture('example-style.json').should('deep.equal', obj);
- });
- },
-
- fillLayersModal(opts: any) {
- var type = opts.type;
- var layer = opts.layer;
- var id;
- if(opts.id) {
- id = opts.id
- }
- else {
- id = `${type}:${uuid()}`;
- }
-
- cy.get(this.getDataAttribute('add-layer.layer-type', "select")).select(type);
- cy.get(this.getDataAttribute("add-layer.layer-id", "input")).type(id);
- if(layer) {
- cy.get(this.getDataAttribute("add-layer.layer-source-block", "input")).type(layer);
- }
- cy.get(this.getDataAttribute("add-layer")).click();
-
- return id;
- },
-
- typeKeys(keys: string) {
- cy.get('body').type(keys);
- },
-
- click(selector: string) {
- cy.get(selector).click();
- },
-
- select(selector: string, value: string) {
- cy.get(selector).select(value);
- },
-
- isSelected(selector: string, value: string) {
- cy.get(selector).find(`option[value="${value}"]`).should("be.selected");
- },
-
-
- focus(selector: string) {
- cy.get(selector).focus();
- },
-
- isFocused(selector: string) {
- cy.get(selector).should('have.focus');
- },
-
- isDisplayedInViewport(selector: string) {
- cy.get(selector).should('be.visible');
- },
-
- isNotDisplayedInViewport(selector: string) {
- cy.get(selector).should('not.be.visible');
- },
-
- setValue(selector: string, text: string) {
- cy.get(selector).clear().type(text, {parseSpecialCharSequences: false});
- },
-
- isExists(selector: string) {
- cy.get(selector).should('exist');
- },
-
- doesNotExists(selector: string) {
- cy.get(selector).should('not.exist');
- },
-
- chooseExampleFile() {
- cy.get("input[type='file']").selectFile('cypress/fixtures/example-style.json', {force: true});
- },
-
- getExampleFileUrl() {
- return "http://localhost:8888/example-style.json";
- },
-
- waitForExampleFileRequset() {
- cy.wait('@example-style.json');
+ chooseExampleFile: () => {
+ cy.get("input[type='file']").selectFile(
+ "cypress/fixtures/example-style.json",
+ { force: true }
+ );
+ },
+ setStyle: (
+ styleProperties: "geojson" | "raster" | "both" | "layer" | "",
+ zoom?: number
+ ) => {
+ 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;
+ }
+ if (zoom) {
+ url += "#" + zoom + "/41.3805/2.1635";
+ }
+ cy.visit("http://localhost:8888/" + url);
+ if (styleProperties) {
+ cy.on("window:confirm", () => true);
+ }
+ cy.get(".maputnik-toolbar-link").should("be.visible");
+ },
+ fillLayersModal: (opts: any) => {
+ var type = opts.type;
+ var layer = opts.layer;
+ var id;
+ if (opts.id) {
+ id = opts.id;
+ } else {
+ id = `${type}:${uuid()}`;
}
+ cy.get(
+ this.get.getDataAttribute("add-layer.layer-type", "select")
+ ).select(type);
+ cy.get(this.get.getDataAttribute("add-layer.layer-id", "input")).type(id);
+ if (layer) {
+ cy.get(
+ this.get.getDataAttribute("add-layer.layer-source-block", "input")
+ ).type(layer);
+ }
+ this.when.click("add-layer");
+ return id;
+ },
+
+ typeKeys: (keys: string) => {
+ cy.get("body").type(keys);
+ },
+
+ click: (selector: string) => {
+ this.helper.when.click(selector);
+ // cy.get(selector).click({ force: true });
+ },
+
+ select: (selector: string, value: string) => {
+ cy.get(selector).select(value);
+ },
+
+ focus: (selector: string) => {
+ this.helper.when.focus(selector);
+ },
+
+ setValue: (selector: string, text: string) => {
+ cy.get(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");
+
+ cy.get(this.get.getDataAttribute("modal:add-layer")).should("exist");
+ cy.get(this.get.getDataAttribute("modal:add-layer")).should("be.visible");
+ },
+ };
+
+ public get = {
+ isMac: () => {
+ return Cypress.platform === "darwin";
+ },
+ getStyleFromWindow: (win: Window) => {
+ const styleId = win.localStorage.getItem("maputnik:latest_style");
+ const styleItem = win.localStorage.getItem(`maputnik:style:${styleId}`);
+ const obj = JSON.parse(styleItem || "");
+ return obj;
+ },
+ getExampleFileUrl: () => {
+ return "http://localhost:8888/example-style.json";
+ },
+ getDataAttribute: (key: string, selector?: string): string => {
+ return `*[data-wd-key='${key}'] ${selector || ""}`;
+ },
+ };
+
+ public should = {
+ canvasBeFocused: () => {
+ this.when.within("maplibre:map", () => {
+ cy.get("canvas").should("be.focused");
+ });
+ },
+ notExist: (selector: string) => {
+ cy.get(selector).should("not.exist");
+ },
+ beFocused: (selector: string) => {
+ this.helper.get.element(selector).should("have.focus");
+ },
+
+ notBeFocused: (selector: string) => {
+ this.helper.get.element(selector).should("not.have.focus");
+ },
+
+ beVisible: (selector: string) => {
+ this.helper.get.element(selector).should("be.visible");
+ },
+
+ notBeVisible: (selector: string) => {
+ this.helper.get.element(selector).should("not.be.visible");
+ },
+
+ equalStyleStore: (getter: (obj: any) => any, styleObj: any) => {
+ cy.window().then((win: any) => {
+ const obj = this.get.getStyleFromWindow(win);
+ assert.deepEqual(getter(obj), styleObj);
+ });
+ },
+
+ isStyleStoreEqualToExampleFileData: () => {
+ cy.window().then((win: any) => {
+ const obj = this.get.getStyleFromWindow(win);
+ cy.fixture("example-style.json").should("deep.equal", obj);
+ });
+ },
+
+ isExists: (selector: string) => {
+ this.helper.get.element(selector).should("exist");
+ },
+ isSelected: (selector: string, value: string) => {
+ cy.get(selector).find(`option[value="${value}"]`).should("be.selected");
+ },
+ };
}
diff --git a/cypress/e2e/history.cy.ts b/cypress/e2e/history.cy.ts
index a404985a..59b6001e 100644
--- a/cypress/e2e/history.cy.ts
+++ b/cypress/e2e/history.cy.ts
@@ -1,80 +1,97 @@
-import driver from "./driver";
+import MaputnikDriver from "./driver";
describe("history", () => {
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
+
let undoKeyCombo: string;
let redoKeyCombo: string;
before(() => {
- const isMac = driver.isMac();
- undoKeyCombo = isMac ? '{meta}z' : '{ctrl}z';
- redoKeyCombo = isMac ? '{meta}{shift}z' : '{ctrl}y';
- driver.beforeEach();
+ const isMac = get.isMac();
+ undoKeyCombo = isMac ? "{meta}z" : "{ctrl}z";
+ redoKeyCombo = isMac ? "{meta}{shift}z" : "{ctrl}y";
});
it("undo/redo", () => {
- driver.setStyle('geojson');
- driver.openLayersModal();
+ when.setStyle("geojson");
+ when.openLayersModal();
- driver.isStyleStoreEqual((a: any) => a.layers, []);
+ should.equalStyleStore((a: any) => a.layers, []);
- driver.fillLayersModal({
+ when.fillLayersModal({
id: "step 1",
- type: "background"
- })
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": "step 1",
- "type": 'background'
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "step 1",
+ type: "background",
+ },
+ ]
+ );
- driver.openLayersModal();
- driver.fillLayersModal({
+ when.openLayersModal();
+ when.fillLayersModal({
id: "step 2",
- type: "background"
- })
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": "step 1",
- "type": 'background'
- },
- {
- "id": "step 2",
- "type": 'background'
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "step 1",
+ type: "background",
+ },
+ {
+ id: "step 2",
+ type: "background",
+ },
+ ]
+ );
- driver.typeKeys(undoKeyCombo);
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": "step 1",
- "type": 'background'
- }
- ]);
+ when.typeKeys(undoKeyCombo);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "step 1",
+ type: "background",
+ },
+ ]
+ );
- driver.typeKeys(undoKeyCombo)
- driver.isStyleStoreEqual((a: any) => a.layers, []);
+ when.typeKeys(undoKeyCombo);
+ should.equalStyleStore((a: any) => a.layers, []);
- driver.typeKeys(redoKeyCombo)
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": "step 1",
- "type": 'background'
- }
- ]);
+ when.typeKeys(redoKeyCombo);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "step 1",
+ type: "background",
+ },
+ ]
+ );
- driver.typeKeys(redoKeyCombo)
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": "step 1",
- "type": 'background'
- },
- {
- "id": "step 2",
- "type": 'background'
- }
- ]);
+ when.typeKeys(redoKeyCombo);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "step 1",
+ type: "background",
+ },
+ {
+ id: "step 2",
+ type: "background",
+ },
+ ]
+ );
});
-})
+});
diff --git a/cypress/e2e/keyboard.cy.ts b/cypress/e2e/keyboard.cy.ts
index 48e660ab..3d7a24bb 100644
--- a/cypress/e2e/keyboard.cy.ts
+++ b/cypress/e2e/keyboard.cy.ts
@@ -1,60 +1,61 @@
-import driver from "./driver";
+import { default as MaputnikDriver } from "./driver";
describe("keyboard", () => {
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
describe("shortcuts", () => {
beforeEach(() => {
- driver.setupInterception();
- driver.setStyle('');
- })
+ given.setupInterception();
+ when.setStyle("");
+ });
it("ESC should unfocus", () => {
- const targetSelector = driver.getDataAttribute("nav:inspect") + " select";
- driver.focus(targetSelector);
- driver.isFocused(targetSelector);
+ const targetSelector = "maputnik-select";
+ when.focus(targetSelector);
+ should.beFocused(targetSelector);
- //driver.typeKeys("{esc}");
- //driver.isFocused('body');
+ when.typeKeys("{esc}");
+ expect(should.notBeFocused(targetSelector));
});
it("'?' should show shortcuts modal", () => {
- driver.typeKeys("?");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:shortcuts"));
+ when.typeKeys("?");
+ should.beVisible("modal:shortcuts");
});
it("'o' should show open modal", () => {
- driver.typeKeys("o");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:open"));
+ when.typeKeys("o");
+ should.beVisible("modal:open");
});
it("'e' should show export modal", () => {
- driver.typeKeys("e");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:export"));
+ when.typeKeys("e");
+ should.beVisible("modal:export");
});
it("'d' should show sources modal", () => {
- driver.typeKeys("d");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:sources"));
+ when.typeKeys("d");
+ should.beVisible("modal:sources");
});
it("'s' should show settings modal", () => {
- driver.typeKeys("s");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:settings"));
+ when.typeKeys("s");
+ should.beVisible("modal:settings");
});
it("'i' should change map to inspect mode", () => {
- driver.typeKeys("i");
- driver.isSelected(driver.getDataAttribute("nav:inspect"), "inspect");
+ when.typeKeys("i");
+ should.isSelected(get.getDataAttribute("nav:inspect"), "inspect");
});
it("'m' should focus map", () => {
- driver.typeKeys("m");
- driver.isFocused(".maplibregl-canvas");
+ when.typeKeys("m");
+ should.beFocused(".maplibregl-canvas");
});
it("'!' should show debug modal", () => {
- driver.typeKeys("!");
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:debug"));
+ when.typeKeys("!");
+ should.beVisible("modal:debug");
});
});
-
});
diff --git a/cypress/e2e/layers.cy.ts b/cypress/e2e/layers.cy.ts
index 1ea995a6..736a578e 100644
--- a/cypress/e2e/layers.cy.ts
+++ b/cypress/e2e/layers.cy.ts
@@ -1,112 +1,132 @@
var assert = require("assert");
-import driver from "./driver";
-import { v1 as uuid } from 'uuid';
+import { v1 as uuid } from "uuid";
+import MaputnikDriver from "./driver";
describe("layers", () => {
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
beforeEach(() => {
- driver.beforeEach();
- driver.setStyle('both');
- driver.openLayersModal();
+ when.setStyle("both");
+ when.openLayersModal();
});
describe("ops", () => {
it("delete", () => {
- var id = driver.fillLayersModal({
- type: "background"
- })
+ var id = when.fillLayersModal({
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'background'
- },
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ },
+ ]
+ );
- driver.click(driver.getDataAttribute("layer-list-item:"+id+":delete", ""))
+ when.click("layer-list-item:" + id + ":delete");
- driver.isStyleStoreEqual((a: any) => a.layers, []);
+ should.equalStyleStore((a: any) => a.layers, []);
});
it("duplicate", () => {
var styleObj;
- var id = driver.fillLayersModal({
- type: "background"
- })
+ var id = when.fillLayersModal({
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'background'
- },
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ },
+ ]
+ );
- driver.click(driver.getDataAttribute("layer-list-item:"+id+":copy", ""));
+ when.click("layer-list-item:" + id + ":copy");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id+"-copy",
- "type": "background"
- },
- {
- "id": id,
- "type": "background"
- },
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id + "-copy",
+ type: "background",
+ },
+ {
+ id: id,
+ type: "background",
+ },
+ ]
+ );
});
it("hide", () => {
var styleObj;
- var id = driver.fillLayersModal({
- type: "background"
- })
+ var id = when.fillLayersModal({
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'background'
- },
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ },
+ ]
+ );
- driver.click(driver.getDataAttribute("layer-list-item:"+id+":toggle-visibility", ""));
+ when.click("layer-list-item:" + id + ":toggle-visibility");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "background",
- "layout": {
- "visibility": "none"
- }
- },
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ layout: {
+ visibility: "none",
+ },
+ },
+ ]
+ );
- driver.click(driver.getDataAttribute("layer-list-item:"+id+":toggle-visibility", ""));
+ when.click("layer-list-item:" + id + ":toggle-visibility");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "background",
- "layout": {
- "visibility": "visible"
- }
- },
- ]);
- })
- })
-
-
- describe('background', () => {
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ layout: {
+ visibility: "visible",
+ },
+ },
+ ]
+ );
+ });
+ });
+ describe("background", () => {
it("add", () => {
- var id = driver.fillLayersModal({
- type: "background"
- })
+ var id = when.fillLayersModal({
+ type: "background",
+ });
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'background'
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "background",
+ },
+ ]
+ );
});
describe("modify", () => {
@@ -114,17 +134,26 @@ describe("layers", () => {
// Setup
var id = uuid();
- driver.select(driver.getDataAttribute("add-layer.layer-type", "select"), "background");
- driver.setValue(driver.getDataAttribute("add-layer.layer-id", "input"), "background:"+id);
+ when.select(
+ get.getDataAttribute("add-layer.layer-type", "select"),
+ "background"
+ );
+ when.setValue(
+ get.getDataAttribute("add-layer.layer-id", "input"),
+ "background:" + id
+ );
- driver.click(driver.getDataAttribute("add-layer"));
+ when.click("add-layer");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'background:'+id,
- "type": 'background'
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "background:" + id,
+ type: "background",
+ },
+ ]
+ );
return id;
}
@@ -134,35 +163,47 @@ describe("layers", () => {
it("id", () => {
var bgId = createBackground();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
+ when.click("layer-list-item:background:" + bgId);
var id = uuid();
- driver.setValue(driver.getDataAttribute("layer-editor.layer-id", "input"), "foobar:"+id)
- driver.click(driver.getDataAttribute("min-zoom"));
+ when.setValue(
+ get.getDataAttribute("layer-editor.layer-id", "input"),
+ "foobar:" + id
+ );
+ when.click("min-zoom");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'foobar:'+id,
- "type": 'background'
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "foobar:" + id,
+ type: "background",
+ },
+ ]
+ );
});
it("min-zoom", () => {
var bgId = createBackground();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
- driver.setValue(driver.getDataAttribute("min-zoom", 'input[type="text"]'), "1");
+ when.click("layer-list-item:background:" + bgId);
+ when.setValue(
+ get.getDataAttribute("min-zoom", 'input[type="text"]'),
+ "1"
+ );
- driver.click(driver.getDataAttribute("layer-editor.layer-id", "input"));
+ when.click("layer-editor.layer-id");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'background:'+bgId,
- "type": 'background',
- "minzoom": 1
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "background:" + bgId,
+ type: "background",
+ minzoom: 1,
+ },
+ ]
+ );
// AND RESET!
// driver.setValue(driver.getDataAttribute("min-zoom", "input"), "")
@@ -179,38 +220,47 @@ describe("layers", () => {
it("max-zoom", () => {
var bgId = createBackground();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
- driver.setValue(driver.getDataAttribute("max-zoom", 'input[type="text"]'), "1")
+ when.click("layer-list-item:background:" + bgId);
+ when.setValue(
+ get.getDataAttribute("max-zoom", 'input[type="text"]'),
+ "1"
+ );
- driver.click(driver.getDataAttribute("layer-editor.layer-id", "input"));
+ when.click("layer-editor.layer-id");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'background:'+bgId,
- "type": 'background',
- "maxzoom": 1
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "background:" + bgId,
+ type: "background",
+ maxzoom: 1,
+ },
+ ]
+ );
});
it("comments", () => {
var bgId = createBackground();
var id = uuid();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
- driver.setValue(driver.getDataAttribute("layer-comment", "textarea"), id);
+ when.click("layer-list-item:background:" + bgId);
+ when.setValue(get.getDataAttribute("layer-comment", "textarea"), id);
- driver.click(driver.getDataAttribute("layer-editor.layer-id", "input"));
+ when.click("layer-editor.layer-id");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'background:'+bgId,
- "type": 'background',
- metadata: {
- 'maputnik:comment': id
- }
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "background:" + bgId,
+ type: "background",
+ metadata: {
+ "maputnik:comment": id,
+ },
+ },
+ ]
+ );
// Unset it again.
// TODO: This fails
@@ -228,31 +278,33 @@ describe("layers", () => {
it("color", () => {
var bgId = createBackground();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
+ when.click("layer-list-item:background:" + bgId);
- driver.click(driver.getDataAttribute("spec-field:background-color", "input"));
+ when.click("spec-field:background-color");
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": 'background:'+bgId,
- "type": 'background'
- }
- ]);
-
- })
- })
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: "background:" + bgId,
+ type: "background",
+ },
+ ]
+ );
+ });
+ });
describe("filter", () => {
it("expand/collapse");
it("compound filter");
- })
+ });
describe("paint", () => {
it("expand/collapse");
it("color");
it("pattern");
it("opacity");
- })
+ });
// <=====
describe("json-editor", () => {
@@ -263,165 +315,183 @@ describe("layers", () => {
it.skip("parse error", () => {
var bgId = createBackground();
- driver.click(driver.getDataAttribute("layer-list-item:background:"+bgId));
+ when.click("layer-list-item:background:" + bgId);
var errorSelector = ".CodeMirror-lint-marker-error";
- driver.doesNotExists(errorSelector);
+ should.notExist(errorSelector);
- driver.click(".CodeMirror");
- driver.typeKeys("\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013 {");
- driver.isExists(errorSelector);
+ when.click(".CodeMirror");
+ when.typeKeys(
+ "\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013\uE013 {"
+ );
+ should.isExists(errorSelector);
- driver.click(driver.getDataAttribute("layer-editor.layer-id"));
+ when.click("layer-editor.layer-id");
});
});
- })
+ });
});
- describe('fill', () => {
+ describe("fill", () => {
it("add", () => {
-
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "fill",
- layer: "example"
+ layer: "example",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'fill',
- "source": "example"
- }
- ]);
- })
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "fill",
+ source: "example",
+ },
+ ]
+ );
+ });
// TODO: Change source
- it("change source")
+ it("change source");
});
- describe('line', () => {
+ describe("line", () => {
it("add", () => {
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "line",
- layer: "example"
+ layer: "example",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "line",
- "source": "example",
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "line",
+ source: "example",
+ },
+ ]
+ );
});
it("groups", () => {
// TODO
// Click each of the layer groups.
- })
+ });
});
- describe('symbol', () => {
+ describe("symbol", () => {
it("add", () => {
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "symbol",
- layer: "example"
+ layer: "example",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "symbol",
- "source": "example",
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "symbol",
+ source: "example",
+ },
+ ]
+ );
});
});
- describe('raster', () => {
+ describe("raster", () => {
it("add", () => {
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "raster",
- layer: "raster"
+ layer: "raster",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "raster",
- "source": "raster",
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "raster",
+ source: "raster",
+ },
+ ]
+ );
});
});
- describe('circle', () => {
+ describe("circle", () => {
it("add", () => {
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "circle",
- layer: "example"
+ layer: "example",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": "circle",
- "source": "example",
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "circle",
+ source: "example",
+ },
+ ]
+ );
});
-
});
- describe('fill extrusion', () => {
+ describe("fill extrusion", () => {
it("add", () => {
- var id = driver.fillLayersModal({
+ var id = when.fillLayersModal({
type: "fill-extrusion",
- layer: "example"
+ layer: "example",
});
- driver.isStyleStoreEqual((a: any) => a.layers, [
- {
- "id": id,
- "type": 'fill-extrusion',
- "source": "example"
- }
- ]);
+ should.equalStyleStore(
+ (a: any) => a.layers,
+ [
+ {
+ id: id,
+ type: "fill-extrusion",
+ source: "example",
+ },
+ ]
+ );
});
});
-
describe("groups", () => {
it("simple", () => {
- driver.setStyle("geojson");
+ when.setStyle("geojson");
- driver.openLayersModal();
- driver.fillLayersModal({
+ when.openLayersModal();
+ when.fillLayersModal({
id: "foo",
- type: "background"
- })
+ type: "background",
+ });
- driver.openLayersModal();
- driver.fillLayersModal({
+ when.openLayersModal();
+ when.fillLayersModal({
id: "foo_bar",
- type: "background"
- })
+ type: "background",
+ });
- driver.openLayersModal();
- driver.fillLayersModal({
+ when.openLayersModal();
+ when.fillLayersModal({
id: "foo_bar_baz",
- type: "background"
- })
+ type: "background",
+ });
- driver.isDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo"));
- driver.isNotDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo_bar"));
- driver.isNotDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo_bar_baz"));
+ should.beVisible("layer-list-item:foo");
- driver.click(driver.getDataAttribute("layer-list-group:foo-0"));
+ should.notBeVisible("layer-list-item:foo_bar");
+ should.notBeVisible("layer-list-item:foo_bar_baz");
- driver.isDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo"));
- driver.isDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo_bar"));
- driver.isDisplayedInViewport(driver.getDataAttribute("layer-list-item:foo_bar_baz"));
- })
- })
+ when.click("layer-list-group:foo-0");
+
+ should.beVisible("layer-list-item:foo");
+ should.beVisible("layer-list-item:foo_bar");
+ should.beVisible("layer-list-item:foo_bar_baz");
+ });
+ });
});
diff --git a/cypress/e2e/map.cy.ts b/cypress/e2e/map.cy.ts
index 102e3055..ea0b695b 100644
--- a/cypress/e2e/map.cy.ts
+++ b/cypress/e2e/map.cy.ts
@@ -1,25 +1,25 @@
-import driver from "./driver";
+import MaputnikDriver from "./driver";
describe("map", () => {
- describe("zoom level", () => {
- beforeEach(() => {
- driver.beforeEach();
- });
- it("via url", () => {
- var zoomLevel = 12.37;
- driver.setStyle("geojson", zoomLevel);
- driver.isDisplayedInViewport(".maplibregl-ctrl-zoom");
- // HM TODO
- //driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel);
- })
- it("via map controls", () => {
- var zoomLevel = 12.37;
- driver.setStyle("geojson", zoomLevel);
-
- driver.click(".maplibregl-ctrl-zoom-in");
- driver.isDisplayedInViewport(".maplibregl-ctrl-zoom");
- // HM TODO
- //driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel + 1);
- })
- })
- })
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
+ describe("zoom level", () => {
+ it("via url", () => {
+ var zoomLevel = 12.37;
+ when.setStyle("geojson", zoomLevel);
+ should.beVisible("maplibre:ctrl-zoom");
+ // HM TODO
+ //driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel);
+ });
+
+ it("via map controls", () => {
+ var zoomLevel = 12.37;
+ when.setStyle("geojson", zoomLevel);
+
+ when.click("maplibre:ctrl-zoom");
+ should.beVisible("maplibre:ctrl-zoom");
+ // HM TODO
+ //driver.getText(".maplibregl-ctrl-zoom") === "Zoom "+(zoomLevel + 1);
+ });
+ });
+});
diff --git a/cypress/e2e/modals.cy.ts b/cypress/e2e/modals.cy.ts
index f8a044a1..a910d2c4 100644
--- a/cypress/e2e/modals.cy.ts
+++ b/cypress/e2e/modals.cy.ts
@@ -1,137 +1,160 @@
-import driver from "./driver";
+import MaputnikDriver from "./driver";
describe("modals", () => {
+ let { beforeAndAfter, given, when, get, should } = new MaputnikDriver();
+ beforeAndAfter();
beforeEach(() => {
- driver.beforeEach();
- driver.setStyle('');
+ when.setStyle("");
});
describe("open", () => {
beforeEach(() => {
- driver.click(driver.getDataAttribute("nav:open"));
+ when.click("nav:open");
});
it("close", () => {
- driver.closeModal("modal:open");
+ when.closeModal("modal:open");
+ should.notExist("modal:open");
});
it.skip("upload", () => {
// HM: I was not able to make the following choose file actually to select a file and close the modal...
- driver.chooseExampleFile();
+ when.chooseExampleFile();
- driver.isStyleStoreEqualToExampleFileData();
+ should.isStyleStoreEqualToExampleFileData();
});
it("load from url", () => {
- var styleFileUrl = driver.getExampleFileUrl();
+ var styleFileUrl = get.getExampleFileUrl();
- driver.setValue(driver.getDataAttribute("modal:open.url.input"), styleFileUrl);
- driver.click(driver.getDataAttribute("modal:open.url.button"))
- driver.waitForExampleFileRequset();
+ when.setValue(get.getDataAttribute("modal:open.url.input"), styleFileUrl);
+ when.click("modal:open.url.button");
+ when.waitForExampleFileRequset();
- driver.isStyleStoreEqualToExampleFileData();
+ should.isStyleStoreEqualToExampleFileData();
});
- })
+ });
describe("shortcuts", () => {
it("open/close", () => {
- driver.setStyle('');
-
- driver.typeKeys("?");
-
- driver.isDisplayedInViewport(driver.getDataAttribute("modal:shortcuts"));
-
- driver.closeModal("modal:shortcuts");
+ when.setStyle("");
+ when.typeKeys("?");
+ when.closeModal("modal:shortcuts");
+ should.notExist("modal:shortcuts");
});
-
});
describe("export", () => {
beforeEach(() => {
- driver.click(driver.getDataAttribute("nav:export"));
+ when.click("nav:export");
});
it("close", () => {
- driver.closeModal("modal:export");
+ when.closeModal("modal:export");
+ should.notExist("modal:export");
});
// TODO: Work out how to download a file and check the contents
- it("download")
-
- })
+ it("download");
+ });
describe("sources", () => {
- it("active sources")
- it("public source")
- it("add new source")
- })
+ it("active sources");
+ it("public source");
+ it("add new source");
+ });
describe("inspect", () => {
it("toggle", () => {
- driver.setStyle('geojson');
+ when.setStyle("geojson");
- driver.select(driver.getDataAttribute("nav:inspect", "select"), "inspect");
- })
- })
+ when.select(get.getDataAttribute("nav:inspect", "select"), "inspect");
+ });
+ });
describe("style settings", () => {
beforeEach(() => {
- driver.click(driver.getDataAttribute("nav:settings"));
+ when.click("nav:settings");
});
it("name", () => {
- driver.setValue(driver.getDataAttribute("modal:settings.name"), "foobar");
- driver.click(driver.getDataAttribute("modal:settings.owner"));
+ when.setValue(get.getDataAttribute("modal:settings.name"), "foobar");
+ when.click("modal:settings.owner");
- driver.isStyleStoreEqual((obj) => obj.name, "foobar");
- })
+ should.equalStyleStore((obj) => obj.name, "foobar");
+ });
it("owner", () => {
- driver.setValue(driver.getDataAttribute("modal:settings.owner"), "foobar")
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ when.setValue(get.getDataAttribute("modal:settings.owner"), "foobar");
+ when.click("modal:settings.name");
- driver.isStyleStoreEqual((obj) => obj.owner, "foobar");
- })
+ should.equalStyleStore((obj) => obj.owner, "foobar");
+ });
it("sprite url", () => {
- driver.setValue(driver.getDataAttribute("modal:settings.sprite"), "http://example.com")
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ when.setValue(
+ get.getDataAttribute("modal:settings.sprite"),
+ "http://example.com"
+ );
+ when.click("modal:settings.name");
- driver.isStyleStoreEqual((obj) => obj.sprite, "http://example.com");
- })
+ should.equalStyleStore((obj) => obj.sprite, "http://example.com");
+ });
it("glyphs url", () => {
- var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"
- driver.setValue(driver.getDataAttribute("modal:settings.glyphs"), glyphsUrl);
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
+ when.setValue(get.getDataAttribute("modal:settings.glyphs"), glyphsUrl);
+ when.click("modal:settings.name");
- driver.isStyleStoreEqual((obj) => obj.glyphs, glyphsUrl);
- })
+ should.equalStyleStore((obj) => obj.glyphs, glyphsUrl);
+ });
it("maptiler access token", () => {
var apiKey = "testing123";
- driver.setValue(driver.getDataAttribute("modal:settings.maputnik:openmaptiles_access_token"), apiKey);
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ when.setValue(
+ get.getDataAttribute(
+ "modal:settings.maputnik:openmaptiles_access_token"
+ ),
+ apiKey
+ );
+ when.click("modal:settings.name");
- driver.isStyleStoreEqual((obj) => obj.metadata["maputnik:openmaptiles_access_token"], apiKey);
- })
+ should.equalStyleStore(
+ (obj) => obj.metadata["maputnik:openmaptiles_access_token"],
+ apiKey
+ );
+ });
it("thunderforest access token", () => {
var apiKey = "testing123";
- driver.setValue(driver.getDataAttribute("modal:settings.maputnik:thunderforest_access_token"), apiKey);
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ when.setValue(
+ get.getDataAttribute(
+ "modal:settings.maputnik:thunderforest_access_token"
+ ),
+ apiKey
+ );
+ when.click("modal:settings.name");
- driver.isStyleStoreEqual((obj) => obj.metadata["maputnik:thunderforest_access_token"], apiKey);
- })
+ should.equalStyleStore(
+ (obj) => obj.metadata["maputnik:thunderforest_access_token"],
+ apiKey
+ );
+ });
it("style renderer", () => {
- cy.on('uncaught:exception', () => false); // this is due to the fact that this is an invalid style for openlayers
- driver.select(driver.getDataAttribute("modal:settings.maputnik:renderer"), "ol");
- driver.isSelected(driver.getDataAttribute("modal:settings.maputnik:renderer"), "ol");
-
- driver.click(driver.getDataAttribute("modal:settings.name"));
+ cy.on("uncaught:exception", () => false); // this is due to the fact that this is an invalid style for openlayers
+ when.select(
+ get.getDataAttribute("modal:settings.maputnik:renderer"),
+ "ol"
+ );
+ should.isSelected(
+ get.getDataAttribute("modal:settings.maputnik:renderer"),
+ "ol"
+ );
- driver.isStyleStoreEqual((obj) => obj.metadata["maputnik:renderer"], "ol");
- })
- })
+ when.click("modal:settings.name");
+
+ should.equalStyleStore((obj) => obj.metadata["maputnik:renderer"], "ol");
+ });
+ });
describe("sources", () => {
- it("toggle")
- })
-})
+ it("toggle");
+ });
+});
diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts
index f80f74f8..c3760faa 100644
--- a/cypress/support/e2e.ts
+++ b/cypress/support/e2e.ts
@@ -14,7 +14,8 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
-import './commands'
+import "cypress-plugin-tab";
+import "./commands";
// Alternatively you can use CommonJS syntax:
-// require('./commands')
\ No newline at end of file
+// require('./commands')
diff --git a/src/components/App.jsx b/src/components/App.jsx
index 6714f42e..5ef42a0d 100644
--- a/src/components/App.jsx
+++ b/src/components/App.jsx
@@ -1,43 +1,50 @@
-import autoBind from 'react-autobind';
-import React from 'react'
-import cloneDeep from 'lodash.clonedeep'
-import clamp from 'lodash.clamp'
-import buffer from 'buffer'
-import get from 'lodash.get'
-import {unset} from 'lodash'
-import {arrayMoveMutable} from 'array-move'
-import url from 'url'
+import { arrayMoveMutable } from "array-move";
+import buffer from "buffer";
+import { unset } from "lodash";
+import clamp from "lodash.clamp";
+import cloneDeep from "lodash.clonedeep";
+import get from "lodash.get";
+import React from "react";
+import autoBind from "react-autobind";
import hash from "string-hash";
+import url from "url";
-import MapMaplibreGl from './MapMaplibreGl'
-import MapOpenLayers from './MapOpenLayers'
-import LayerList from './LayerList'
-import LayerEditor from './LayerEditor'
-import AppToolbar from './AppToolbar'
-import AppLayout from './AppLayout'
-import MessagePanel from './AppMessagePanel'
+import AppLayout from "./AppLayout";
+import MessagePanel from "./AppMessagePanel";
+import AppToolbar from "./AppToolbar";
+import LayerEditor from "./LayerEditor";
+import LayerList from "./LayerList";
+import MapMaplibreGl from "./MapMaplibreGl";
+import MapOpenLayers from "./MapOpenLayers";
-import ModalSettings from './ModalSettings'
-import ModalExport from './ModalExport'
-import ModalSources from './ModalSources'
-import ModalOpen from './ModalOpen'
-import ModalShortcuts from './ModalShortcuts'
-import ModalSurvey from './ModalSurvey'
-import ModalDebug from './ModalDebug'
+import ModalDebug from "./ModalDebug";
+import ModalExport from "./ModalExport";
+import ModalOpen from "./ModalOpen";
+import ModalSettings from "./ModalSettings";
+import ModalShortcuts from "./ModalShortcuts";
+import ModalSources from "./ModalSources";
+import ModalSurvey from "./ModalSurvey";
-import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
-import {latest, validate} from '@maplibre/maplibre-gl-style-spec'
-import style from '../libs/style'
-import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
-import { undoMessages, redoMessages } from '../libs/diffmessage'
-import { StyleStore } from '../libs/stylestore'
-import { ApiStyleStore } from '../libs/apistore'
-import { RevisionStore } from '../libs/revisions'
-import LayerWatcher from '../libs/layerwatcher'
-import tokens from '../config/tokens.json'
-import isEqual from 'lodash.isequal'
-import Debug from '../libs/debug'
-import {formatLayerId} from '../util/format';
+import { latest, validate } from "@maplibre/maplibre-gl-style-spec";
+import isEqual from "lodash.isequal";
+import tokens from "../config/tokens.json";
+import { ApiStyleStore } from "../libs/apistore";
+import Debug from "../libs/debug";
+import { redoMessages, undoMessages } from "../libs/diffmessage";
+import LayerWatcher from "../libs/layerwatcher";
+import {
+ downloadGlyphsMetadata,
+ downloadSpriteMetadata,
+} from "../libs/metadata";
+import { RevisionStore } from "../libs/revisions";
+import style from "../libs/style";
+import { StyleStore } from "../libs/stylestore";
+import {
+ initialStyleUrl,
+ loadStyleUrl,
+ removeStyleQuerystring,
+} from "../libs/urlopen";
+import { formatLayerId } from "../util/format";
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
window.Buffer = buffer.Buffer;
@@ -47,18 +54,20 @@ function setFetchAccessToken(url, mapStyle) {
const matchesMaptiler = url.match(/\.maptiler\.com/);
const matchesThunderforest = url.match(/\.thunderforest\.com/);
if (matchesTilehosting || matchesMaptiler) {
- const accessToken = style.getAccessToken("openmaptiles", mapStyle, {allowFallback: true})
+ const accessToken = style.getAccessToken("openmaptiles", mapStyle, {
+ allowFallback: true,
+ });
if (accessToken) {
- return url.replace('{key}', accessToken)
+ return url.replace("{key}", accessToken);
}
- }
- else if (matchesThunderforest) {
- const accessToken = style.getAccessToken("thunderforest", mapStyle, {allowFallback: true})
+ } else if (matchesThunderforest) {
+ const accessToken = style.getAccessToken("thunderforest", mapStyle, {
+ allowFallback: true,
+ });
if (accessToken) {
- return url.replace('{key}', accessToken)
+ return url.replace("{key}", accessToken);
}
- }
- else {
+ } else {
return url;
}
}
@@ -70,124 +79,135 @@ function updateRootSpec(spec, fieldName, newValues) {
...spec.$root,
[fieldName]: {
...spec.$root[fieldName],
- values: newValues
- }
- }
- }
+ values: newValues,
+ },
+ },
+ };
}
export default class App extends React.Component {
constructor(props) {
- super(props)
+ super(props);
autoBind(this);
- this.revisionStore = new RevisionStore()
- const params = new URLSearchParams(window.location.search.substring(1))
- let port = params.get("localport")
- if (port == null && (window.location.port != 80 && window.location.port != 443)) {
- port = window.location.port
+ this.revisionStore = new RevisionStore();
+ const params = new URLSearchParams(window.location.search.substring(1));
+ let port = params.get("localport");
+ if (
+ port == null &&
+ window.location.port != 80 &&
+ window.location.port != 443
+ ) {
+ port = window.location.port;
}
this.styleStore = new ApiStyleStore({
- onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, {save: false}),
+ onLocalStyleChange: (mapStyle) =>
+ this.onStyleChanged(mapStyle, { save: false }),
port: port,
- host: params.get("localhost")
- })
-
+ host: params.get("localhost"),
+ });
const shortcuts = [
{
key: "?",
handler: () => {
this.toggleModal("shortcuts");
- }
+ },
},
{
key: "o",
handler: () => {
this.toggleModal("open");
- }
+ },
},
{
key: "e",
handler: () => {
this.toggleModal("export");
- }
+ },
},
{
key: "d",
handler: () => {
this.toggleModal("sources");
- }
+ },
},
{
key: "s",
handler: () => {
this.toggleModal("settings");
- }
+ },
},
{
key: "i",
handler: () => {
- this.setMapState(
- this.state.mapState === "map" ? "inspect" : "map"
- );
- }
+ this.setMapState(this.state.mapState === "map" ? "inspect" : "map");
+ },
},
{
key: "m",
handler: () => {
document.querySelector(".maplibregl-canvas").focus();
- }
+ },
},
{
key: "!",
handler: () => {
this.toggleModal("debug");
- }
+ },
},
- ]
+ ];
document.body.addEventListener("keyup", (e) => {
- if(e.key === "Escape") {
+ if (e.key === "Escape") {
e.target.blur();
document.body.focus();
- }
- else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
+ } else if (
+ this.state.isOpen.shortcuts ||
+ document.activeElement === document.body
+ ) {
const shortcut = shortcuts.find((shortcut) => {
- return (shortcut.key === e.key)
- })
+ return shortcut.key === e.key;
+ });
- if(shortcut) {
+ if (shortcut) {
this.setModal("shortcuts", false);
shortcut.handler(e);
}
}
- })
+ });
- const styleUrl = initialStyleUrl()
- if(styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?")) {
- this.styleStore = new StyleStore()
- loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle))
- removeStyleQuerystring()
+ const styleUrl = initialStyleUrl();
+ if (
+ styleUrl &&
+ window.confirm(
+ "Load style from URL: " + styleUrl + " and discard current changes?"
+ )
+ ) {
+ this.styleStore = new StyleStore();
+ loadStyleUrl(styleUrl, (mapStyle) => this.onStyleChanged(mapStyle));
+ removeStyleQuerystring();
} else {
- if(styleUrl) {
- removeStyleQuerystring()
+ if (styleUrl) {
+ removeStyleQuerystring();
}
- this.styleStore.init(err => {
- if(err) {
- console.log('Falling back to local storage for storing styles')
- this.styleStore = new StyleStore()
+ this.styleStore.init((err) => {
+ if (err) {
+ console.log("Falling back to local storage for storing styles");
+ this.styleStore = new StyleStore();
}
- this.styleStore.latestStyle(mapStyle => this.onStyleChanged(mapStyle, {initialLoad: true}))
+ this.styleStore.latestStyle((mapStyle) =>
+ this.onStyleChanged(mapStyle, { initialLoad: true })
+ );
- if(Debug.enabled()) {
+ if (Debug.enabled()) {
Debug.set("maputnik", "styleStore", this.styleStore);
Debug.set("maputnik", "revisionStore", this.revisionStore);
}
- })
+ });
}
- if(Debug.enabled()) {
+ if (Debug.enabled()) {
Debug.set("maputnik", "revisionStore", this.revisionStore);
Debug.set("maputnik", "styleStore", this.styleStore);
}
@@ -228,35 +248,32 @@ export default class App extends React.Component {
openlayersDebugOptions: {
debugToolbox: false,
},
- }
+ };
this.layerWatcher = new LayerWatcher({
- onVectorLayersChange: v => this.setState({ vectorLayers: v })
- })
+ onVectorLayersChange: (v) => this.setState({ vectorLayers: v }),
+ });
}
handleKeyPress = (e) => {
- if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
- if(e.metaKey && e.shiftKey && e.keyCode === 90) {
+ if (navigator.platform.toUpperCase().indexOf("MAC") >= 0) {
+ if (e.metaKey && e.shiftKey && e.keyCode === 90) {
e.preventDefault();
this.onRedo(e);
- }
- else if(e.metaKey && e.keyCode === 90) {
+ } else if (e.metaKey && e.keyCode === 90) {
e.preventDefault();
this.onUndo(e);
}
- }
- else {
- if(e.ctrlKey && e.keyCode === 90) {
+ } else {
+ if (e.ctrlKey && e.keyCode === 90) {
e.preventDefault();
this.onUndo(e);
- }
- else if(e.ctrlKey && e.keyCode === 89) {
+ } else if (e.ctrlKey && e.keyCode === 89) {
e.preventDefault();
this.onRedo(e);
}
}
- }
+ };
componentDidMount() {
window.addEventListener("keydown", this.handleKeyPress);
@@ -267,33 +284,38 @@ export default class App extends React.Component {
}
saveStyle(snapshotStyle) {
- this.styleStore.save(snapshotStyle)
+ this.styleStore.save(snapshotStyle);
}
updateFonts(urlTemplate) {
- const metadata = this.state.mapStyle.metadata || {}
- const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles
+ const metadata = this.state.mapStyle.metadata || {};
+ const accessToken =
+ metadata["maputnik:openmaptiles_access_token"] || tokens.openmaptiles;
- let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
- downloadGlyphsMetadata(glyphUrl, fonts => {
- this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
- })
+ let glyphUrl =
+ typeof urlTemplate === "string"
+ ? urlTemplate.replace("{key}", accessToken)
+ : urlTemplate;
+ downloadGlyphsMetadata(glyphUrl, (fonts) => {
+ this.setState({ spec: updateRootSpec(this.state.spec, "glyphs", fonts) });
+ });
}
updateIcons(baseUrl) {
- downloadSpriteMetadata(baseUrl, icons => {
- this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
- })
+ downloadSpriteMetadata(baseUrl, (icons) => {
+ this.setState({ spec: updateRootSpec(this.state.spec, "sprite", icons) });
+ });
}
onChangeMetadataProperty = (property, value) => {
// If we're changing renderer reset the map state.
if (
- property === 'maputnik:renderer' &&
- value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mlgljs')
+ property === "maputnik:renderer" &&
+ value !==
+ get(this.state.mapStyle, ["metadata", "maputnik:renderer"], "mlgljs")
) {
this.setState({
- mapState: 'map'
+ mapState: "map",
});
}
@@ -301,13 +323,13 @@ export default class App extends React.Component {
...this.state.mapStyle,
metadata: {
...this.state.mapStyle.metadata,
- [property]: value
- }
- }
- this.onStyleChanged(changedStyle)
- }
+ [property]: value,
+ },
+ };
+ this.onStyleChanged(changedStyle);
+ };
- onStyleChanged = (newStyle, opts={}) => {
+ onStyleChanged = (newStyle, opts = {}) => {
opts = {
save: true,
addRevision: true,
@@ -338,9 +360,11 @@ export default class App extends React.Component {
});
}
- const mappedErrors = layerErrors.concat(errors).map(error => {
+ const mappedErrors = layerErrors.concat(errors).map((error) => {
// Special case: Duplicate layer id
- const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/);
+ const dupMatch = error.message.match(
+ /layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/
+ );
if (dupMatch) {
const [matchStr, index, message] = dupMatch;
return {
@@ -351,13 +375,15 @@ export default class App extends React.Component {
index: parseInt(index, 10),
key: "id",
message,
- }
- }
- }
+ },
+ },
+ };
}
// Special case: Invalid source
- const invalidSourceMatch = error.message.match(/layers\[(\d+)\]: (source "(?:.*)" not found)/);
+ const invalidSourceMatch = error.message.match(
+ /layers\[(\d+)\]: (source "(?:.*)" not found)/
+ );
if (invalidSourceMatch) {
const [matchStr, index, message] = invalidSourceMatch;
return {
@@ -368,15 +394,17 @@ export default class App extends React.Component {
index: parseInt(index, 10),
key: "source",
message,
- }
- }
- }
+ },
+ },
+ };
}
- const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/);
+ const layerMatch = error.message.match(
+ /layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/
+ );
if (layerMatch) {
const [matchStr, index, group, property, message] = layerMatch;
- const key = (group && property) ? [group, property].join(".") : property;
+ const key = group && property ? [group, property].join(".") : property;
return {
message: error.message,
parsed: {
@@ -384,12 +412,11 @@ export default class App extends React.Component {
data: {
index: parseInt(index, 10),
key,
- message
- }
- }
- }
- }
- else {
+ message,
+ },
+ },
+ };
+ } else {
return {
message: error.message,
};
@@ -400,27 +427,26 @@ export default class App extends React.Component {
if (errors.length > 0) {
dirtyMapStyle = cloneDeep(newStyle);
- errors.forEach(error => {
- const {message} = error;
+ errors.forEach((error) => {
+ const { message } = error;
if (message) {
try {
const objPath = message.split(":")[0];
// Errors can be deply nested for example 'layers[0].filter[1][1][0]' we only care upto the property 'layers[0].filter'
const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^\[]+/)[0];
unset(dirtyMapStyle, unsetPath);
- }
- catch (err) {
+ } catch (err) {
console.warn(err);
}
}
});
}
- if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
- this.updateFonts(newStyle.glyphs)
+ if (newStyle.glyphs !== this.state.mapStyle.glyphs) {
+ this.updateFonts(newStyle.glyphs);
}
- if(newStyle.sprite !== this.state.mapStyle.sprite) {
- this.updateIcons(newStyle.sprite)
+ if (newStyle.sprite !== this.state.mapStyle.sprite) {
+ this.updateIcons(newStyle.sprite);
}
if (opts.addRevision) {
@@ -430,251 +456,266 @@ export default class App extends React.Component {
this.saveStyle(newStyle);
}
- this.setState({
- mapStyle: newStyle,
- dirtyMapStyle: dirtyMapStyle,
- errors: mappedErrors,
- }, () => {
- this.fetchSources();
- this.setStateInUrl();
- })
-
- }
+ this.setState(
+ {
+ mapStyle: newStyle,
+ dirtyMapStyle: dirtyMapStyle,
+ errors: mappedErrors,
+ },
+ () => {
+ this.fetchSources();
+ this.setStateInUrl();
+ }
+ );
+ };
onUndo = () => {
- const activeStyle = this.revisionStore.undo()
+ const activeStyle = this.revisionStore.undo();
- const messages = undoMessages(this.state.mapStyle, activeStyle)
- this.onStyleChanged(activeStyle, {addRevision: false});
+ const messages = undoMessages(this.state.mapStyle, activeStyle);
+ this.onStyleChanged(activeStyle, { addRevision: false });
this.setState({
infos: messages,
- })
- }
+ });
+ };
onRedo = () => {
- const activeStyle = this.revisionStore.redo()
- const messages = redoMessages(this.state.mapStyle, activeStyle)
- this.onStyleChanged(activeStyle, {addRevision: false});
+ const activeStyle = this.revisionStore.redo();
+ const messages = redoMessages(this.state.mapStyle, activeStyle);
+ this.onStyleChanged(activeStyle, { addRevision: false });
this.setState({
infos: messages,
- })
- }
+ });
+ };
onMoveLayer = (move) => {
let { oldIndex, newIndex } = move;
let layers = this.state.mapStyle.layers;
- oldIndex = clamp(oldIndex, 0, layers.length-1);
- newIndex = clamp(newIndex, 0, layers.length-1);
- if(oldIndex === newIndex) return;
+ oldIndex = clamp(oldIndex, 0, layers.length - 1);
+ newIndex = clamp(newIndex, 0, layers.length - 1);
+ if (oldIndex === newIndex) return;
if (oldIndex === this.state.selectedLayerIndex) {
this.setState({
- selectedLayerIndex: newIndex
+ selectedLayerIndex: newIndex,
});
}
layers = layers.slice(0);
arrayMoveMutable(layers, oldIndex, newIndex);
this.onLayersChange(layers);
- }
+ };
onLayersChange = (changedLayers) => {
const changedStyle = {
...this.state.mapStyle,
- layers: changedLayers
- }
- this.onStyleChanged(changedStyle)
- }
+ layers: changedLayers,
+ };
+ this.onStyleChanged(changedStyle);
+ };
onLayerDestroy = (index) => {
let layers = this.state.mapStyle.layers;
const remainingLayers = layers.slice(0);
remainingLayers.splice(index, 1);
this.onLayersChange(remainingLayers);
- }
+ };
onLayerCopy = (index) => {
let layers = this.state.mapStyle.layers;
- const changedLayers = layers.slice(0)
+ const changedLayers = layers.slice(0);
- const clonedLayer = cloneDeep(changedLayers[index])
- clonedLayer.id = clonedLayer.id + "-copy"
- changedLayers.splice(index, 0, clonedLayer)
- this.onLayersChange(changedLayers)
- }
+ const clonedLayer = cloneDeep(changedLayers[index]);
+ clonedLayer.id = clonedLayer.id + "-copy";
+ changedLayers.splice(index, 0, clonedLayer);
+ this.onLayersChange(changedLayers);
+ };
onLayerVisibilityToggle = (index) => {
let layers = this.state.mapStyle.layers;
- const changedLayers = layers.slice(0)
+ const changedLayers = layers.slice(0);
- const layer = { ...changedLayers[index] }
- const changedLayout = 'layout' in layer ? {...layer.layout} : {}
- changedLayout.visibility = changedLayout.visibility === 'none' ? 'visible' : 'none'
-
- layer.layout = changedLayout
- changedLayers[index] = layer
- this.onLayersChange(changedLayers)
- }
+ const layer = { ...changedLayers[index] };
+ const changedLayout = "layout" in layer ? { ...layer.layout } : {};
+ changedLayout.visibility =
+ changedLayout.visibility === "none" ? "visible" : "none";
+ layer.layout = changedLayout;
+ changedLayers[index] = layer;
+ this.onLayersChange(changedLayers);
+ };
onLayerIdChange = (index, oldId, newId) => {
- const changedLayers = this.state.mapStyle.layers.slice(0)
+ const changedLayers = this.state.mapStyle.layers.slice(0);
changedLayers[index] = {
...changedLayers[index],
- id: newId
- }
+ id: newId,
+ };
- this.onLayersChange(changedLayers)
- }
+ this.onLayersChange(changedLayers);
+ };
onLayerChanged = (index, layer) => {
- const changedLayers = this.state.mapStyle.layers.slice(0)
- changedLayers[index] = layer
+ const changedLayers = this.state.mapStyle.layers.slice(0);
+ changedLayers[index] = layer;
- this.onLayersChange(changedLayers)
- }
+ this.onLayersChange(changedLayers);
+ };
setMapState = (newState) => {
- this.setState({
- mapState: newState
- }, this.setStateInUrl);
- }
+ this.setState(
+ {
+ mapState: newState,
+ },
+ this.setStateInUrl
+ );
+ };
setDefaultValues = (styleObj) => {
- const metadata = styleObj.metadata || {}
- if(metadata['maputnik:renderer'] === undefined) {
+ const metadata = styleObj.metadata || {};
+ if (metadata["maputnik:renderer"] === undefined) {
const changedStyle = {
...styleObj,
metadata: {
...styleObj.metadata,
- 'maputnik:renderer': 'mlgljs'
- }
- }
- return changedStyle
+ "maputnik:renderer": "mlgljs",
+ },
+ };
+ return changedStyle;
} else {
- return styleObj
+ return styleObj;
}
- }
+ };
openStyle = (styleObj) => {
- styleObj = this.setDefaultValues(styleObj)
- this.onStyleChanged(styleObj)
- }
+ styleObj = this.setDefaultValues(styleObj);
+ this.onStyleChanged(styleObj);
+ };
fetchSources() {
const sourceList = {};
- for(let [key, val] of Object.entries(this.state.mapStyle.sources)) {
- if(
+ for (let [key, val] of Object.entries(this.state.mapStyle.sources)) {
+ if (
!this.state.sources.hasOwnProperty(key) &&
val.type === "vector" &&
val.hasOwnProperty("url")
) {
sourceList[key] = {
type: val.type,
- layers: []
+ layers: [],
};
let url = val.url;
try {
- url = setFetchAccessToken(url, this.state.mapStyle)
- } catch(err) {
+ url = setFetchAccessToken(url, this.state.mapStyle);
+ } catch (err) {
console.warn("Failed to setFetchAccessToken: ", err);
}
fetch(url, {
- mode: 'cors',
+ mode: "cors",
})
- .then(response => response.json())
- .then(json => {
+ .then((response) => response.json())
+ .then((json) => {
+ if (!json.hasOwnProperty("vector_layers")) {
+ return;
+ }
- if(!json.hasOwnProperty("vector_layers")) {
- return;
- }
+ // Create new objects before setState
+ const sources = Object.assign(
+ {},
+ {
+ [key]: this.state.sources[key],
+ }
+ );
- // Create new objects before setState
- const sources = Object.assign({}, {
- [key]: this.state.sources[key],
+ for (let layer of json.vector_layers) {
+ sources[key].layers.push(layer.id);
+ }
+
+ console.debug("Updating source: " + key);
+ this.setState({
+ sources: sources,
+ });
+ })
+ .catch((err) => {
+ console.error("Failed to process sources for '%s'", url, err);
});
-
- for(let layer of json.vector_layers) {
- sources[key].layers.push(layer.id)
- }
-
- console.debug("Updating source: "+key);
- this.setState({
- sources: sources
- });
- })
- .catch(err => {
- console.error("Failed to process sources for '%s'", url, err);
- });
- }
- else {
- sourceList[key] = this.state.sources[key] || this.state.mapStyle.sources[key];
+ } else {
+ sourceList[key] =
+ this.state.sources[key] || this.state.mapStyle.sources[key];
}
}
- if(!isEqual(this.state.sources, sourceList)) {
+ if (!isEqual(this.state.sources, sourceList)) {
console.debug("Setting sources");
this.setState({
- sources: sourceList
- })
+ sources: sourceList,
+ });
}
}
- _getRenderer () {
+ _getRenderer() {
const metadata = this.state.mapStyle.metadata || {};
- return metadata['maputnik:renderer'] || 'mlgljs';
+ return metadata["maputnik:renderer"] || "mlgljs";
}
onMapChange = (mapView) => {
this.setState({
mapView,
});
- }
+ };
mapRenderer() {
- const {mapStyle, dirtyMapStyle} = this.state;
+ const { mapStyle, dirtyMapStyle } = this.state;
const metadata = this.state.mapStyle.metadata || {};
const mapProps = {
- mapStyle: (dirtyMapStyle || mapStyle),
+ mapStyle: dirtyMapStyle || mapStyle,
replaceAccessTokens: (mapStyle) => {
return style.replaceAccessTokens(mapStyle, {
- allowFallback: true
+ allowFallback: true,
});
},
onDataChange: (e) => {
- this.layerWatcher.analyzeMap(e.map)
+ this.layerWatcher.analyzeMap(e.map);
this.fetchSources();
},
- }
+ };
const renderer = this._getRenderer();
let mapElement;
// Check if OL code has been loaded?
- if(renderer === 'ol') {
- mapElement =