mirror of
https://github.com/maputnik/editor.git
synced 2026-07-27 16:27:26 +00:00
Add more end to end tests to increase coverage
This commit is contained in:
@@ -212,6 +212,39 @@ describe("layer editor", () => {
|
|||||||
layers: [{ id, type: "fill", source: "example", filter: ["all", ["!=", "name", ""], ["==", "name", ""]] }],
|
layers: [{ id, type: "fill", source: "example", filter: ["all", ["!=", "name", ""], ["==", "name", ""]] }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("change the combining operator and delete a filter item", async () => {
|
||||||
|
const id = await when.modal.fillLayers({ type: "fill", layer: "example" });
|
||||||
|
await when.addFilter();
|
||||||
|
await when.addFilter();
|
||||||
|
|
||||||
|
await when.selectFilterCombiningOperator("any");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, filter: ["any", ["==", "name", ""], ["==", "name", ""]] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await when.deleteFilterItem();
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, filter: ["any", ["==", "name", ""]] }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("convert a filter to an expression and delete it", async () => {
|
||||||
|
const id = await when.modal.fillLayers({ type: "fill", layer: "example" });
|
||||||
|
await when.addFilter();
|
||||||
|
|
||||||
|
// A single-item "all" collapses to the bare comparison when migrated.
|
||||||
|
await when.convertFilterToExpression();
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, filter: ["==", ["get", "name"], ""] }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deleting the expression restores the default (empty) filter.
|
||||||
|
await when.deleteFilterExpression();
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ id, filter: ["all"] }],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("functions", () => {
|
describe("functions", () => {
|
||||||
@@ -292,6 +325,119 @@ describe("layer editor", () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("edit the base and the stops of a zoom function", async () => {
|
||||||
|
await when.modal.fillLayers({ type: "circle", layer: "example" });
|
||||||
|
await when.makeZoomFunction("circle-radius");
|
||||||
|
|
||||||
|
await when.setFunctionBase("circle-radius", "2");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-radius": { base: 2, stops: [[6, 5], [10, 5]] } } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Editing a stop's zoom re-sorts the stops by zoom.
|
||||||
|
await when.setFunctionStopValue("circle-radius", "Zoom", 0, "3");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-radius": { base: 2, stops: [[3, 5], [10, 5]] } } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await when.setFunctionStopValue("circle-radius", "Output value", 0, "9");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-radius": { base: 2, stops: [[3, 9], [10, 5]] } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("convert a zoom function to a data function and back", async () => {
|
||||||
|
await when.modal.fillLayers({ type: "circle", layer: "example" });
|
||||||
|
await when.makeZoomFunction("circle-radius");
|
||||||
|
|
||||||
|
// Picking a non-interpolate scale turns the zoom function into a data one,
|
||||||
|
// carrying the existing stops over as zoom/value pairs.
|
||||||
|
await when.selectFunctionType("circle-radius", "categorical");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
paint: {
|
||||||
|
"circle-radius": {
|
||||||
|
property: "",
|
||||||
|
type: "exponential",
|
||||||
|
stops: [[{ zoom: 6, value: 0 }, 5], [{ zoom: 10, value: 0 }, 5]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Picking "interpolate" converts it back to a plain zoom function.
|
||||||
|
await when.selectFunctionType("circle-radius", "interpolate");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-radius": { stops: [[6, 5], [10, 5]] } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("edit the property, default and stops of a data function", async () => {
|
||||||
|
await when.modal.fillLayers({ type: "circle", layer: "example" });
|
||||||
|
await when.setValue("spec-field-input:circle-blur", "1");
|
||||||
|
await when.makeDataFunction("circle-blur");
|
||||||
|
|
||||||
|
await when.setFunctionProperty("circle-blur", "myprop");
|
||||||
|
await when.setFunctionDefault("circle-blur", "0.5");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-blur": { property: "myprop", default: 0.5 } } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await when.setFunctionStopValue("circle-blur", "Input value", 0, "7");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
paint: {
|
||||||
|
"circle-blur": {
|
||||||
|
property: "myprop",
|
||||||
|
stops: [[{ zoom: 6, value: 7 }, 1], [{ zoom: 10, value: 0 }, 1]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await when.selectFunctionType("circle-blur", "categorical");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-blur": { property: "myprop", type: "categorical" } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("convert a property to an expression, revert it and delete it", async () => {
|
||||||
|
await when.modal.fillLayers({ type: "circle", layer: "example" });
|
||||||
|
await when.setValue("spec-field-input:circle-blur", "1");
|
||||||
|
|
||||||
|
await when.makeExpression("circle-blur");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-blur": ["literal", 1] } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reverting an expression restores the plain value it wrapped.
|
||||||
|
await when.undoExpression("circle-blur");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-blur": 1 } }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deleting an expression falls back to the property's spec default.
|
||||||
|
await when.makeExpression("circle-blur");
|
||||||
|
await when.deleteExpression("circle-blur");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-blur": 0 } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("convert a zoom function to an expression", async () => {
|
||||||
|
await when.modal.fillLayers({ type: "circle", layer: "example" });
|
||||||
|
await when.makeZoomFunction("circle-radius");
|
||||||
|
|
||||||
|
await when.makeExpression("circle-radius");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [{ paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("layout", () => {
|
describe("layout", () => {
|
||||||
|
|||||||
+79
-16
@@ -133,11 +133,6 @@ export class MaputnikDriver {
|
|||||||
await this.helper.when.click("layer-editor-group:" + title);
|
await this.helper.when.click("layer-editor-group:" + title);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Adds one of the predefined public sources listed in the sources modal. */
|
|
||||||
addPublicSource: async (index = 0) => {
|
|
||||||
await this.helper.get.element(".maputnik-public-source-select").nth(index).click();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Picks a source for the selected layer from the source autocomplete.
|
* Picks a source for the selected layer from the source autocomplete.
|
||||||
* The autocomplete is a controlled (downshift) input, so the value has to be
|
* The autocomplete is a controlled (downshift) input, so the value has to be
|
||||||
@@ -201,6 +196,62 @@ export class MaputnikDriver {
|
|||||||
await container.locator(".maputnik-delete-stop").first().click({ force: true });
|
await container.locator(".maputnik-delete-stop").first().click({ force: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Turns the property into a raw style expression. */
|
||||||
|
makeExpression: async (fieldName: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.scrollIntoViewIfNeeded();
|
||||||
|
// In the plain spec field the expression button shares the zoom-function
|
||||||
|
// class and comes first; inside a function editor it has its own test id.
|
||||||
|
const inFunctionEditor = container.locator("[data-wd-key='convert-to-expression']");
|
||||||
|
const button =
|
||||||
|
(await inFunctionEditor.count()) > 0
|
||||||
|
? inFunctionEditor
|
||||||
|
: container.locator(".maputnik-make-zoom-function").first();
|
||||||
|
await button.click({ force: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Reverts an expression back to a plain value. */
|
||||||
|
undoExpression: async (fieldName: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='undo-expression']").click({ force: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Removes an expression, restoring the property's spec default. */
|
||||||
|
deleteExpression: async (fieldName: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='delete-expression']").click({ force: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Picks the function scale (categorical/interval/exponential/identity/interpolate). */
|
||||||
|
selectFunctionType: async (fieldName: string, type: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='function-type'] select").selectOption(type);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sets the "Base" input of a zoom/data function. */
|
||||||
|
setFunctionBase: async (fieldName: string, value: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='function-base'] input").fill(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sets the data property a data function keys off of. */
|
||||||
|
setFunctionProperty: async (fieldName: string, value: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='function-property'] input").fill(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Sets the fallback value used when a feature has no matching stop. */
|
||||||
|
setFunctionDefault: async (fieldName: string, value: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator("[data-wd-key='function-default'] input").fill(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Edits one cell of a function's stop table ("Zoom", "Input value" or "Output value"). */
|
||||||
|
setFunctionStopValue: async (fieldName: string, column: string, index: number, value: string) => {
|
||||||
|
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
|
||||||
|
await container.locator(`[aria-label="${column}"]`).nth(index).fill(value);
|
||||||
|
},
|
||||||
|
|
||||||
addFilter: async () => {
|
addFilter: async () => {
|
||||||
const button = this.helper.get.elementByTestId("layer-filter-button");
|
const button = this.helper.get.elementByTestId("layer-filter-button");
|
||||||
await button.scrollIntoViewIfNeeded();
|
await button.scrollIntoViewIfNeeded();
|
||||||
@@ -211,8 +262,29 @@ export class MaputnikDriver {
|
|||||||
await this.helper.get.element(".maputnik-filter-editor-operator select").first().selectOption(value);
|
await this.helper.get.element(".maputnik-filter-editor-operator select").first().selectOption(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteFirstActiveSource: async () => {
|
/** Chooses how the filter items combine: all / none / any. */
|
||||||
await this.helper.get.element(".maputnik-active-source-type-editor-header-delete").first().click();
|
selectFilterCombiningOperator: async (value: string) => {
|
||||||
|
await this.helper.when.selectWithin("filter-combining-operator", value);
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFilterItem: async (index = 0) => {
|
||||||
|
await this.helper.get
|
||||||
|
.element(".maputnik-filter-editor-block-action .maputnik-icon-button")
|
||||||
|
.nth(index)
|
||||||
|
.click();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Converts the simple filter editor into a raw expression editor. */
|
||||||
|
convertFilterToExpression: async () => {
|
||||||
|
await this.helper.when.click("filter-convert-to-expression");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the filter expression, restoring the simple filter editor.
|
||||||
|
* The filter group precedes the paint group, so its button comes first.
|
||||||
|
*/
|
||||||
|
deleteFilterExpression: async () => {
|
||||||
|
await this.helper.get.element("[data-wd-key='delete-expression']").first().click();
|
||||||
},
|
},
|
||||||
|
|
||||||
setColorValue: async (fieldName: string, value: string) => {
|
setColorValue: async (fieldName: string, value: string) => {
|
||||||
@@ -238,15 +310,6 @@ export class MaputnikDriver {
|
|||||||
await this.helper.when.typeText(text);
|
await this.helper.when.typeText(text);
|
||||||
},
|
},
|
||||||
|
|
||||||
exportCreateHtml: async () => {
|
|
||||||
await this.helper.get.element(".maputnik-modal-export-buttons button").last().click();
|
|
||||||
},
|
|
||||||
|
|
||||||
exportSaveStyle: async () => {
|
|
||||||
await this.helper.stubSaveFilePicker();
|
|
||||||
await this.helper.get.element(".maputnik-modal-export-buttons button").first().click();
|
|
||||||
},
|
|
||||||
|
|
||||||
waitForExampleFileResponse: () => this.helper.when.waitForResponse("example-style.json"),
|
waitForExampleFileResponse: () => this.helper.when.waitForResponse("example-style.json"),
|
||||||
|
|
||||||
/** Fill localStorage until we get a QuotaExceededError. */
|
/** Fill localStorage until we get a QuotaExceededError. */
|
||||||
|
|||||||
@@ -34,5 +34,45 @@ export class ModalDriver {
|
|||||||
close: async (key: string) => {
|
close: async (key: string) => {
|
||||||
await this.helper.when.click(key + ".close-modal");
|
await this.helper.when.click(key + ".close-modal");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a source of the given type from the sources modal, keeping whatever
|
||||||
|
* defaults that type's editor prefills.
|
||||||
|
*/
|
||||||
|
addSource: async (sourceId: string, sourceType: string) => {
|
||||||
|
const { when } = this.helper;
|
||||||
|
await when.setValue("modal:sources.add.source_id", sourceId);
|
||||||
|
await when.select("modal:sources.add.source_type", sourceType);
|
||||||
|
await when.click("modal:sources.add.add_source");
|
||||||
|
await when.wait(200);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Adds one of the predefined public sources listed in the sources modal. */
|
||||||
|
addPublicSource: async (index = 0) => {
|
||||||
|
await this.helper.get.element(".maputnik-public-source-select").nth(index).click();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFirstActiveSource: async () => {
|
||||||
|
await this.helper.get.element(".maputnik-active-source-type-editor-header-delete").first().click();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Fills one number box of a coordinate pair in the image/video source editor. */
|
||||||
|
setCoordinateValue: async (index: number, value: string) => {
|
||||||
|
const input = this.helper.get
|
||||||
|
.elementByTestId("modal:sources")
|
||||||
|
.locator(".maputnik-array input")
|
||||||
|
.nth(index);
|
||||||
|
await input.fill(value);
|
||||||
|
await input.blur();
|
||||||
|
},
|
||||||
|
|
||||||
|
exportCreateHtml: async () => {
|
||||||
|
await this.helper.get.element(".maputnik-modal-export-buttons button").last().click();
|
||||||
|
},
|
||||||
|
|
||||||
|
exportSaveStyle: async () => {
|
||||||
|
await this.helper.stubSaveFilePicker();
|
||||||
|
await this.helper.get.element(".maputnik-modal-export-buttons button").first().click();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+144
-4
@@ -66,11 +66,11 @@ describe("modals", () => {
|
|||||||
|
|
||||||
test("download HTML and save the style", async () => {
|
test("download HTML and save the style", async () => {
|
||||||
// Generate the standalone HTML export (triggers a file download).
|
// Generate the standalone HTML export (triggers a file download).
|
||||||
await when.exportCreateHtml();
|
await when.modal.exportCreateHtml();
|
||||||
await then(get.elementByTestId("modal:export")).shouldExist();
|
await then(get.elementByTestId("modal:export")).shouldExist();
|
||||||
|
|
||||||
// Saving the style closes the export modal.
|
// Saving the style closes the export modal.
|
||||||
await when.exportSaveStyle();
|
await when.modal.exportSaveStyle();
|
||||||
await then(get.elementByTestId("modal:export")).shouldNotExist();
|
await then(get.elementByTestId("modal:export")).shouldNotExist();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -85,14 +85,14 @@ describe("modals", () => {
|
|||||||
await when.setStyle("both");
|
await when.setStyle("both");
|
||||||
await when.click("nav:sources");
|
await when.click("nav:sources");
|
||||||
const before = Object.keys(get.fixture("geojson-raster-style.json").sources).length;
|
const before = Object.keys(get.fixture("geojson-raster-style.json").sources).length;
|
||||||
await when.deleteFirstActiveSource();
|
await when.modal.deleteFirstActiveSource();
|
||||||
await then(
|
await then(
|
||||||
get.styleFromLocalStorage().then((style) => Object.keys(style.sources).length)
|
get.styleFromLocalStorage().then((style) => Object.keys(style.sources).length)
|
||||||
).shouldEqual(before - 1);
|
).shouldEqual(before - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("public source", async () => {
|
test("public source", async () => {
|
||||||
await when.addPublicSource();
|
await when.modal.addPublicSource();
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
sources: {
|
sources: {
|
||||||
openmaptiles: {
|
openmaptiles: {
|
||||||
@@ -148,6 +148,108 @@ describe("modals", () => {
|
|||||||
sources: { [sourceId]: { tileSize: 128 } },
|
sources: { [sourceId]: { tileSize: 128 } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("add new geojson url source", async () => {
|
||||||
|
await when.modal.addSource("geojsonurl", "geojson_url");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
geojsonurl: { type: "geojson", data: "http://localhost:3000/geojson.json" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new geojson json source", async () => {
|
||||||
|
await when.modal.addSource("geojsonjson", "geojson_json");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
geojsonjson: { type: "geojson", cluster: false, data: "" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new tilejson vector source", async () => {
|
||||||
|
await when.modal.addSource("tilejsonvector", "tilejson_vector");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
tilejsonvector: { type: "vector", url: "http://localhost:3000/tilejson.json" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new tilejson raster source", async () => {
|
||||||
|
await when.modal.addSource("tilejsonraster", "tilejson_raster");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
tilejsonraster: { type: "raster", url: "http://localhost:3000/tilejson.json" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new tilejson raster-dem source", async () => {
|
||||||
|
await when.modal.addSource("tilejsonrasterdem", "tilejson_raster-dem");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
tilejsonrasterdem: { type: "raster-dem", url: "http://localhost:3000/tilejson.json" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new tile xyz raster-dem source", async () => {
|
||||||
|
await when.modal.addSource("tilexyzrasterdem", "tilexyz_raster-dem");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
tilexyzrasterdem: {
|
||||||
|
type: "raster-dem",
|
||||||
|
tiles: ["http://localhost:3000/{x}/{y}/{z}.png"],
|
||||||
|
minzoom: 0,
|
||||||
|
maxzoom: 14,
|
||||||
|
tileSize: 512,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new image source", async () => {
|
||||||
|
await when.modal.addSource("imagesource", "image");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
imagesource: {
|
||||||
|
type: "image",
|
||||||
|
url: "http://localhost:3000/image.png",
|
||||||
|
coordinates: [[0, 0], [0, 0], [0, 0], [0, 0]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add new video source", async () => {
|
||||||
|
await when.modal.addSource("videosource", "video");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
videosource: {
|
||||||
|
type: "video",
|
||||||
|
urls: ["http://localhost:3000/movie.mp4"],
|
||||||
|
coordinates: [[0, 0], [0, 0], [0, 0], [0, 0]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("edit the corner coordinates of an image source", async () => {
|
||||||
|
const sourceId = "imagecoords";
|
||||||
|
await when.setValue("modal:sources.add.source_id", sourceId);
|
||||||
|
await when.select("modal:sources.add.source_type", "image");
|
||||||
|
// The first corner is the first two number boxes of the coordinate arrays.
|
||||||
|
await when.modal.setCoordinateValue(0, "1");
|
||||||
|
await when.modal.setCoordinateValue(1, "2");
|
||||||
|
await when.click("modal:sources.add.add_source");
|
||||||
|
await when.wait(200);
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
sources: {
|
||||||
|
[sourceId]: { type: "image", coordinates: [[1, 2], [0, 0], [0, 0], [0, 0]] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("inspect", () => {
|
describe("inspect", () => {
|
||||||
@@ -262,6 +364,44 @@ describe("modals", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("map view defaults", async () => {
|
||||||
|
await when.setValue("modal:settings.zoom", "4");
|
||||||
|
await when.setValue("modal:settings.bearing", "12");
|
||||||
|
await when.setValue("modal:settings.pitch", "30");
|
||||||
|
await when.click("modal:settings.name");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
zoom: 4,
|
||||||
|
bearing: 12,
|
||||||
|
pitch: 30,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("light intensity", async () => {
|
||||||
|
await when.setValue("modal:settings.light-intensity", "0.7");
|
||||||
|
await when.click("modal:settings.name");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
light: { intensity: 0.7 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("terrain source and exaggeration", async () => {
|
||||||
|
await when.setValue("modal:settings.maputnik:terrain_source", "terrain");
|
||||||
|
await when.setValue("modal:settings.terrain-exaggeration", "1.5");
|
||||||
|
await when.click("modal:settings.name");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
terrain: { source: "terrain", exaggeration: 1.5 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("transition delay and duration", async () => {
|
||||||
|
await when.setValue("modal:settings.transition-delay", "100");
|
||||||
|
await when.setValue("modal:settings.transition-duration", "500");
|
||||||
|
await when.click("modal:settings.name");
|
||||||
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
transition: { delay: 100, duration: 500 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("style projection mercator", async () => {
|
test("style projection mercator", async () => {
|
||||||
await when.select("modal:settings.projection", "mercator");
|
await when.select("modal:settings.projection", "mercator");
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
|
|||||||
onClick={this.makeExpression}
|
onClick={this.makeExpression}
|
||||||
title={t("Convert to expression")}
|
title={t("Convert to expression")}
|
||||||
className="maputnik-make-zoom-function"
|
className="maputnik-make-zoom-function"
|
||||||
|
data-wd-key="filter-convert-to-expression"
|
||||||
>
|
>
|
||||||
<TbMathFunction />
|
<TbMathFunction />
|
||||||
</InputButton>
|
</InputButton>
|
||||||
@@ -248,6 +249,7 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
|
|||||||
fieldSpec={fieldSpec}
|
fieldSpec={fieldSpec}
|
||||||
label={t("Filter")}
|
label={t("Filter")}
|
||||||
action={actions}
|
action={actions}
|
||||||
|
data-wd-key="filter-combining-operator"
|
||||||
>
|
>
|
||||||
<InputSelect
|
<InputSelect
|
||||||
value={combiningOp}
|
value={combiningOp}
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
<Block
|
<Block
|
||||||
label={t("Function")}
|
label={t("Function")}
|
||||||
key="function"
|
key="function"
|
||||||
|
data-wd-key="function-type"
|
||||||
>
|
>
|
||||||
<div className="maputnik-data-spec-property-input">
|
<div className="maputnik-data-spec-property-input">
|
||||||
<InputSelect
|
<InputSelect
|
||||||
@@ -303,6 +304,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
<Block
|
<Block
|
||||||
label={t("Base")}
|
label={t("Base")}
|
||||||
key="base"
|
key="base"
|
||||||
|
data-wd-key="function-base"
|
||||||
>
|
>
|
||||||
<div className="maputnik-data-spec-property-input">
|
<div className="maputnik-data-spec-property-input">
|
||||||
<InputSpec
|
<InputSpec
|
||||||
@@ -317,6 +319,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
<Block
|
<Block
|
||||||
label={"Property"}
|
label={"Property"}
|
||||||
key="property"
|
key="property"
|
||||||
|
data-wd-key="function-property"
|
||||||
>
|
>
|
||||||
<div className="maputnik-data-spec-property-input">
|
<div className="maputnik-data-spec-property-input">
|
||||||
<InputString
|
<InputString
|
||||||
@@ -330,6 +333,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
<Block
|
<Block
|
||||||
label={t("Default")}
|
label={t("Default")}
|
||||||
key="default"
|
key="default"
|
||||||
|
data-wd-key="function-default"
|
||||||
>
|
>
|
||||||
<InputSpec
|
<InputSpec
|
||||||
fieldName={this.props.fieldName}
|
fieldName={this.props.fieldName}
|
||||||
@@ -368,6 +372,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
}
|
}
|
||||||
<InputButton
|
<InputButton
|
||||||
className="maputnik-add-stop"
|
className="maputnik-add-stop"
|
||||||
|
data-wd-key="convert-to-expression"
|
||||||
onClick={this.props.onExpressionClick?.bind(this)}
|
onClick={this.props.onExpressionClick?.bind(this)}
|
||||||
>
|
>
|
||||||
<TbMathFunction style={{ verticalAlign: "text-bottom" }} />
|
<TbMathFunction style={{ verticalAlign: "text-bottom" }} />
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
|
|||||||
onClick={this.props.onUndo}
|
onClick={this.props.onUndo}
|
||||||
disabled={undoDisabled}
|
disabled={undoDisabled}
|
||||||
className="maputnik-delete-stop"
|
className="maputnik-delete-stop"
|
||||||
|
data-wd-key="undo-expression"
|
||||||
title={t("Revert from expression")}
|
title={t("Revert from expression")}
|
||||||
>
|
>
|
||||||
<MdUndo />
|
<MdUndo />
|
||||||
@@ -59,6 +60,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
|
|||||||
key="delete_action"
|
key="delete_action"
|
||||||
onClick={this.props.onDelete}
|
onClick={this.props.onDelete}
|
||||||
className="maputnik-delete-stop"
|
className="maputnik-delete-stop"
|
||||||
|
data-wd-key="delete-expression"
|
||||||
title={t("Delete expression")}
|
title={t("Delete expression")}
|
||||||
>
|
>
|
||||||
<MdDelete />
|
<MdDelete />
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
|
|||||||
<div className="maputnik-data-fieldset-inner">
|
<div className="maputnik-data-fieldset-inner">
|
||||||
<Block
|
<Block
|
||||||
label={t("Function")}
|
label={t("Function")}
|
||||||
|
data-wd-key="function-type"
|
||||||
>
|
>
|
||||||
<div className="maputnik-data-spec-property-input">
|
<div className="maputnik-data-spec-property-input">
|
||||||
<InputSelect
|
<InputSelect
|
||||||
@@ -206,6 +207,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
|
|||||||
</Block>
|
</Block>
|
||||||
<Block
|
<Block
|
||||||
label={t("Base")}
|
label={t("Base")}
|
||||||
|
data-wd-key="function-base"
|
||||||
>
|
>
|
||||||
<div className="maputnik-data-spec-property-input">
|
<div className="maputnik-data-spec-property-input">
|
||||||
<InputSpec
|
<InputSpec
|
||||||
@@ -240,6 +242,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
|
|||||||
</InputButton>
|
</InputButton>
|
||||||
<InputButton
|
<InputButton
|
||||||
className="maputnik-add-stop"
|
className="maputnik-add-stop"
|
||||||
|
data-wd-key="convert-to-expression"
|
||||||
onClick={this.props.onExpressionClick?.bind(this)}
|
onClick={this.props.onExpressionClick?.bind(this)}
|
||||||
>
|
>
|
||||||
<TbMathFunction style={{ verticalAlign: "text-bottom" }} />
|
<TbMathFunction style={{ verticalAlign: "text-bottom" }} />
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Zoom")}
|
label={t("Zoom")}
|
||||||
|
data-wd-key="modal:settings.zoom"
|
||||||
fieldSpec={latest.$root.zoom}
|
fieldSpec={latest.$root.zoom}
|
||||||
value={mapStyle.zoom}
|
value={mapStyle.zoom}
|
||||||
default={0}
|
default={0}
|
||||||
@@ -214,6 +215,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Bearing")}
|
label={t("Bearing")}
|
||||||
|
data-wd-key="modal:settings.bearing"
|
||||||
fieldSpec={latest.$root.bearing}
|
fieldSpec={latest.$root.bearing}
|
||||||
value={mapStyle.bearing}
|
value={mapStyle.bearing}
|
||||||
default={latest.$root.bearing.default}
|
default={latest.$root.bearing.default}
|
||||||
@@ -222,6 +224,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Pitch")}
|
label={t("Pitch")}
|
||||||
|
data-wd-key="modal:settings.pitch"
|
||||||
fieldSpec={latest.$root.pitch}
|
fieldSpec={latest.$root.pitch}
|
||||||
value={mapStyle.pitch}
|
value={mapStyle.pitch}
|
||||||
default={latest.$root.pitch.default}
|
default={latest.$root.pitch.default}
|
||||||
@@ -248,6 +251,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Light intensity")}
|
label={t("Light intensity")}
|
||||||
|
data-wd-key="modal:settings.light-intensity"
|
||||||
fieldSpec={latest.light.intensity}
|
fieldSpec={latest.light.intensity}
|
||||||
value={light.intensity as number}
|
value={light.intensity as number}
|
||||||
default={latest.light.intensity.default}
|
default={latest.light.intensity.default}
|
||||||
@@ -274,6 +278,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Terrain exaggeration")}
|
label={t("Terrain exaggeration")}
|
||||||
|
data-wd-key="modal:settings.terrain-exaggeration"
|
||||||
fieldSpec={latest.terrain.exaggeration}
|
fieldSpec={latest.terrain.exaggeration}
|
||||||
value={terrain.exaggeration}
|
value={terrain.exaggeration}
|
||||||
default={latest.terrain.exaggeration.default}
|
default={latest.terrain.exaggeration.default}
|
||||||
@@ -282,6 +287,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Transition delay")}
|
label={t("Transition delay")}
|
||||||
|
data-wd-key="modal:settings.transition-delay"
|
||||||
fieldSpec={latest.transition.delay}
|
fieldSpec={latest.transition.delay}
|
||||||
value={transition.delay}
|
value={transition.delay}
|
||||||
default={latest.transition.delay.default}
|
default={latest.transition.delay.default}
|
||||||
@@ -290,6 +296,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
|
|
||||||
<FieldNumber
|
<FieldNumber
|
||||||
label={t("Transition duration")}
|
label={t("Transition duration")}
|
||||||
|
data-wd-key="modal:settings.transition-duration"
|
||||||
fieldSpec={latest.transition.duration}
|
fieldSpec={latest.transition.duration}
|
||||||
value={transition.duration}
|
value={transition.duration}
|
||||||
default={latest.transition.duration.default}
|
default={latest.transition.duration.default}
|
||||||
|
|||||||
Reference in New Issue
Block a user