diff --git a/umap/static/umap/test/DataLayer.js b/umap/static/umap/test/DataLayer.js deleted file mode 100644 index 79647c7f..00000000 --- a/umap/static/umap/test/DataLayer.js +++ /dev/null @@ -1,463 +0,0 @@ -describe('U.DataLayer', () => { - let path = '/map/99/datalayer/update/62/', - map, - datalayer - - before(async () => { - fetchMock.mock(/\/datalayer\/62\/\?.*/, JSON.stringify(RESPONSES.datalayer62_GET)) - fetchMock.sticky('/map/99/update/settings/', { id: 99 }) - this.options = { - umap_id: 99, - } - MAP = map = initMap({ umap_id: 99 }) - const datalayer_options = defaultDatalayerData() - await map.initDataLayers([datalayer_options]) - datalayer = map.getDataLayerByUmapId(62) - enableEdit() - }) - after(() => { - fetchMock.restore() - resetMap() - }) - - describe('#init()', () => { - it('should be added in datalayers index', () => { - assert.notEqual(map.datalayers_index.indexOf(datalayer), -1) - }) - }) - - describe('#edit()', () => { - var editButton, form, input, forceButton - - it('row in control should be active', () => { - assert.notOk( - qs('.leaflet-control-browse #browse_data_toggle_' + L.stamp(datalayer) + '.off') - ) - }) - - it('should have edit button', () => { - editButton = qs('#browse_data_toggle_' + L.stamp(datalayer) + ' .layer-edit') - assert.ok(editButton) - }) - - it('should have toggle visibility element', () => { - assert.ok(qs('.leaflet-control-browse i.layer-toggle')) - }) - - it('should exist only one datalayer', () => { - assert.equal(qsa('.leaflet-control-browse i.layer-toggle').length, 1) - }) - - it('should build a form on edit button click', () => { - happen.click(editButton) - form = qs('form.umap-form') - input = qs('form.umap-form input[name="name"]') - assert.ok(form) - assert.ok(input) - }) - - it('should update name on input change', () => { - var new_name = 'This is a new name' - input.value = new_name - happen.once(input, { type: 'input' }) - assert.equal(datalayer.options.name, new_name) - }) - - it('should have made datalayer dirty', () => { - assert.ok(datalayer.isDirty) - assert.notEqual(map.dirty_datalayers.indexOf(datalayer), -1) - }) - - it('should have made Map dirty', () => { - assert.ok(map.isDirty) - }) - - it('should call datalayer.save on save button click', (done) => { - const postDatalayer = fetchMock.post(path, () => { - return defaultDatalayerData() - }) - clickSave() - window.setTimeout(() => { - assert(fetchMock.called(path)) - done() - }, 500) - }) - - it('should show alert if server respond 412', (done) => { - cleanAlert() - fetchMock.restore() - fetchMock.post(path, 412) - happen.click(editButton) - input = qs('form.umap-form input[name="name"]') - input.value = 'a new name' - happen.once(input, { type: 'input' }) - clickSave() - window.setTimeout(() => { - assert(L.DomUtil.hasClass(map._container, 'umap-alert')) - assert.notEqual(map.dirty_datalayers.indexOf(datalayer), -1) - const forceButton = qs('#umap-alert-container .umap-action') - assert.ok(forceButton) - done() - }, 500) - }) - - it('should save anyway on force save button click', (done) => { - const forceButton = qs('#umap-alert-container .umap-action') - fetchMock.restore() - fetchMock.post(path, defaultDatalayerData) - happen.click(forceButton) - window.setTimeout(() => { - assert.notOk(qs('#umap-alert-container .umap-action')) - assert(fetchMock.called(path)) - assert.equal(map.dirty_datalayers.indexOf(datalayer), -1) - done() - }, 500) - }) - }) - - describe('#save() new', () => { - let newLayerButton, form, input, newDatalayer, editButton, manageButton - - it('should have a manage datalayers action', () => { - enableEdit() - manageButton = qs('.manage-datalayers') - assert.ok(manageButton) - happen.click(manageButton) - }) - - it('should have a new layer button', () => { - newLayerButton = qs('#umap-ui-container .add-datalayer') - assert.ok(newLayerButton) - }) - - it('should build a form on new layer button click', () => { - happen.click(newLayerButton) - form = qs('form.umap-form') - input = qs('form.umap-form input[name="name"]') - assert.ok(form) - assert.ok(input) - }) - - it('should have an empty name', () => { - assert.notOk(input.value) - }) - - it('should have created a new datalayer', () => { - assert.equal(map.datalayers_index.length, 2) - newDatalayer = map.datalayers_index[1] - }) - - it('should have made Map dirty', () => { - assert.ok(map.isDirty) - }) - - it('should update name on input change', () => { - var new_name = 'This is a new name' - input.value = new_name - happen.once(input, { type: 'input' }) - assert.equal(newDatalayer.options.name, new_name) - }) - - it('should set umap_id on save callback', async () => { - assert.notOk(newDatalayer.umap_id) - fetchMock.post('/map/99/datalayer/create/', defaultDatalayerData({ id: 63 })) - clickSave() - return new Promise((resolve) => { - window.setTimeout(() => { - assert.equal(newDatalayer.umap_id, 63) - resolve() - }, 1000) - }) - }) - - it('should have unset map dirty', () => { - assert.notOk(map.isDirty) - }) - - it('should have edit button', () => { - editButton = qs('#browse_data_toggle_' + L.stamp(newDatalayer) + ' .layer-edit') - assert.ok(editButton) - }) - - it('should call update if we edit again', async () => { - happen.click(editButton) - assert.notOk(map.isDirty) - input = qs('form.umap-form input[name="name"]') - input.value = "a new name again but we don't care which" - happen.once(input, { type: 'input' }) - assert.ok(map.isDirty) - var response = () => { - return defaultDatalayerData({ pk: 63 }) - } - var spy = sinon.spy(response) - fetchMock.post('/map/99/datalayer/update/63/', spy) - return new Promise((resolve) => { - clickSave() - window.setTimeout(() => { - assert.ok(spy.calledOnce) - resolve() - }, 1000) - }) - }) - }) - - describe('#iconClassChange()', () => { - it('should change icon class', () => { - happen.click(qs('[data-id="' + datalayer._leaflet_id + '"] .layer-edit')) - changeSelectValue( - qs('form#datalayer-advanced-properties select[name=iconClass]'), - 'Circle' - ) - assert.notOk(qs('div.umap-div-icon')) - assert.ok(qs('div.umap-circle-icon')) - happen.click( - qs('form#datalayer-advanced-properties .umap-field-iconClass .undefine') - ) - assert.notOk(qs('div.umap-circle-icon')) - assert.ok(qs('div.umap-div-icon')) - clickCancel() - }) - }) - - describe('#show/hide', () => { - it('should hide features on hide', () => { - assert.ok(qs('div.umap-div-icon')) - assert.ok(qs('path[fill="none"]')) - datalayer.hide() - assert.notOk(qs('div.umap-div-icon')) - assert.notOk(qs('path[fill="none"]')) - }) - - it('should show features on show', () => { - assert.notOk(qs('div.umap-div-icon')) - assert.notOk(qs('path[fill="none"]')) - datalayer.show() - assert.ok(qs('div.umap-div-icon')) - assert.ok(qs('path[fill="none"]')) - }) - }) - - describe('#clone()', () => { - it('should clone everything but the id and the name', () => { - enableEdit() - var clone = datalayer.clone() - assert.notOk(clone.umap_id) - assert.notEqual(clone.options.name, datalayer.name) - assert.ok(clone.options.name) - assert.equal(clone.options.color, datalayer.options.color) - assert.equal(clone.options.stroke, datalayer.options.stroke) - clone._delete() - clickSave() - }) - }) - - describe('#restore()', () => { - var oldConfirm, - newConfirm = () => { - return true - } - - before(() => { - oldConfirm = window.confirm - window.confirm = newConfirm - }) - after(() => { - window.confirm = oldConfirm - }) - - it('should restore everything', (done) => { - enableEdit() - var geojson = L.Util.CopyJSON(RESPONSES.datalayer62_GET) - geojson.features.push({ - geometry: { - type: 'Point', - coordinates: [-1.274658203125, 50.57634993749885], - }, - type: 'Feature', - id: 1807, - properties: { _umap_options: {}, name: 'new point from restore' }, - }) - geojson._umap_options.color = 'Chocolate' - fetchMock.get('/datalayer/62/olderversion.geojson', geojson) - sinon.spy(window, 'confirm') - datalayer.restore('olderversion.geojson') - window.setTimeout(() => { - assert(window.confirm.calledOnce) - window.confirm.restore() - assert.equal(datalayer.umap_id, 62) - assert.ok(datalayer.isDirty) - assert.equal(datalayer._index.length, 4) - assert.ok(qs('path[fill="Chocolate"]')) - done() - }, 1000) - }) - - it('should revert anything on cancel click', () => { - clickCancel() - assert.equal(datalayer._index.length, 3) - assert.notOk(qs('path[fill="Chocolate"]')) - }) - }) - - describe('#smart-options()', () => { - let poly, marker - before(() => { - datalayer.eachLayer(function (layer) { - if (!poly && layer instanceof L.Polygon) { - poly = layer - } - if (!marker && layer instanceof L.Marker) { - marker = layer - } - }) - }) - - it('should parse color variable', () => { - let icon = qs('div.umap-div-icon .icon_container') - poly.properties.mycolor = 'DarkGoldenRod' - marker.properties.mycolor = 'DarkRed' - marker.properties._umap_options.color = undefined - assert.notOk(qs('path[fill="DarkGoldenRod"]')) - assert.equal(icon.style.backgroundColor, 'olivedrab') - datalayer.options.color = '{mycolor}' - datalayer.options.fillColor = '{mycolor}' - datalayer.indexProperties(poly) - datalayer.indexProperties(marker) - datalayer.redraw() - icon = qs('div.umap-div-icon .icon_container') - assert.equal(icon.style.backgroundColor, 'darkred') - assert.ok(qs('path[fill="DarkGoldenRod"]')) - }) - }) - - describe('#facet-search()', () => { - before(async () => { - fetchMock.get(/\/datalayer\/63\/\?.*/, RESPONSES.datalayer63_GET) - map.options.facetKey = 'name' - await map.initDataLayers([RESPONSES.datalayer63_GET._umap_options]) - }) - it('should not impact non browsable layer', () => { - assert.ok(qs('path[fill="SteelBlue"]')) - }) - it('should allow advanced filter', () => { - map.openFacet() - assert.ok(qs('div.umap-facet-search')) - // This one if from the normal datalayer - // it's name is "test", so it should be hidden - // by the filter - assert.ok(qs('path[fill="none"]')) - happen.click(qs('input[data-value="name poly"]')) - assert.notOk(qs('path[fill="none"]')) - // This one comes from a non browsable layer - // so it should not be affected by the filter - assert.ok(qs('path[fill="SteelBlue"]')) - happen.click(qs('input[data-value="name poly"]')) // Undo - }) - it('should allow to control facet label', () => { - map.options.facetKey = 'name|Nom' - map.openFacet() - assert.ok(qs('div.umap-facet-search h5')) - assert.equal(qs('div.umap-facet-search h5').textContent, 'Nom') - }) - }) - describe('#zoomEnd', () => { - it('should honour the fromZoom option', () => { - map.setZoom(6, { animate: false }) - assert.ok(qs('path[fill="none"]')) - datalayer.options.fromZoom = 6 - map.setZoom(5, { animate: false }) - assert.notOk(qs('path[fill="none"]')) - map.setZoom(6, { animate: false }) - assert.ok(qs('path[fill="none"]')) - }) - - it('should honour the toZoom option', () => { - map.setZoom(6, { animate: false }) - assert.ok(qs('path[fill="none"]')) - datalayer.options.toZoom = 6 - map.setZoom(7, { animate: false }) - assert.notOk(qs('path[fill="none"]')) - map.setZoom(6, { animate: false }) - assert.ok(qs('path[fill="none"]')) - }) - }) - - describe('#displayOnLoad', () => { - before(() => { - fetchMock.get(/\/datalayer\/64\/\?.*/, RESPONSES.datalayer64_GET) - }) - - beforeEach(async () => { - await map.initDataLayers([RESPONSES.datalayer64_GET._umap_options]) - datalayer = map.getDataLayerByUmapId(64) - map.setZoom(10, { animate: false }) - }) - - afterEach(() => { - datalayer._delete() - }) - - it('should not display layer at load', () => { - assert.notOk(qs('path[fill="AliceBlue"]')) - }) - - it('should display on click', (done) => { - happen.click(qs(`[data-id='${L.stamp(datalayer)}'] .layer-toggle`)) - window.setTimeout(() => { - assert.ok(qs('path[fill="AliceBlue"]')) - done() - }, 500) - }) - - it('should not display on zoom', (done) => { - map.setZoom(9, { animate: false }) - window.setTimeout(() => { - assert.notOk(qs('path[fill="AliceBlue"]')) - done() - }, 500) - }) - }) - - describe('#delete()', () => { - let deleteLink, - deletePath = '/map/99/datalayer/delete/62/' - before(() => { - datalayer = map.getDataLayerByUmapId(62) - }) - - it('should have a delete link in update form', () => { - enableEdit() - happen.click(qs('#browse_data_toggle_' + L.stamp(datalayer) + ' .layer-edit')) - deleteLink = qs('button.delete_datalayer_button') - assert.ok(deleteLink) - }) - - it('should delete features on datalayer delete', () => { - happen.click(deleteLink) - assert.notOk(qs('div.icon_container')) - }) - - it('should have set map dirty', () => { - assert.ok(map.isDirty) - }) - - it('should delete layer control row on delete', () => { - assert.notOk( - qs('.leaflet-control-browse #browse_data_toggle_' + L.stamp(datalayer)) - ) - }) - - it('should be removed from map.datalayers_index', () => { - assert.equal(map.datalayers_index.indexOf(datalayer), -1) - }) - - it('should be removed from map.datalayers', () => { - assert.notOk(map.datalayers[L.stamp(datalayer)]) - }) - - it('should be visible again on edit cancel', () => { - clickCancel() - assert.ok(qs('div.icon_container')) - }) - }) -}) diff --git a/umap/static/umap/test/index.html b/umap/static/umap/test/index.html index 41d6b78c..e4d2c811 100644 --- a/umap/static/umap/test/index.html +++ b/umap/static/umap/test/index.html @@ -89,7 +89,6 @@ - diff --git a/umap/tests/integration/test_collaborative_editing.py b/umap/tests/integration/test_collaborative_editing.py index d6daf27d..35e655b2 100644 --- a/umap/tests/integration/test_collaborative_editing.py +++ b/umap/tests/integration/test_collaborative_editing.py @@ -1,9 +1,11 @@ +import json import re +from pathlib import Path from time import sleep from playwright.sync_api import expect -from umap.models import DataLayer +from umap.models import DataLayer, Map from ..base import DataLayerFactory, MapFactory @@ -265,3 +267,33 @@ def test_same_second_edit_doesnt_conflict(context, live_server, tilelayer): "id": str(datalayer.pk), "permissions": {"edit_status": 1}, } + + +def test_should_display_alert_on_conflict(context, live_server, datalayer, map): + map.edit_status = Map.ANONYMOUS + map.save() + + # Open the map on two pages. + page_one = context.new_page() + page_one.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + page_two = context.new_page() + page_two.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + + page_one.locator(".leaflet-marker-icon").click(modifiers=["Shift"]) + page_one.locator('input[name="name"]').fill("new name") + with page_one.expect_response(re.compile(r".*/datalayer/update/.*")): + page_one.get_by_role("button", name="Save").click() + + page_two.locator(".leaflet-marker-icon").click(modifiers=["Shift"]) + page_two.locator('input[name="name"]').fill("custom name") + with page_two.expect_response(re.compile(r".*/datalayer/update/.*")): + page_two.get_by_role("button", name="Save").click() + saved = DataLayer.objects.last() + data = json.loads(Path(saved.geojson.path).read_text()) + assert data["features"][0]["properties"]["name"] == "new name" + expect(page_two.get_by_text("Woops! Someone else seems to")).to_be_visible() + with page_two.expect_response(re.compile(r".*/datalayer/update/.*")): + page_two.get_by_role("button", name="Save anyway").click() + saved = DataLayer.objects.last() + data = json.loads(Path(saved.geojson.path).read_text()) + assert data["features"][0]["properties"]["name"] == "custom name" diff --git a/umap/tests/integration/test_datalayer.py b/umap/tests/integration/test_datalayer.py new file mode 100644 index 00000000..946bae9d --- /dev/null +++ b/umap/tests/integration/test_datalayer.py @@ -0,0 +1,108 @@ +import json + +import pytest +from django.core.files.base import ContentFile +from playwright.sync_api import expect + +from ..base import DataLayerFactory + +pytestmark = pytest.mark.django_db + + +def set_options(datalayer, **options): + # For now we need to change both the DB and the FS… + datalayer.settings.update(options) + data = json.load(datalayer.geojson.file) + data["_umap_options"].update(**options) + datalayer.geojson = ContentFile(json.dumps(data), "foo.json") + datalayer.save() + + +def test_honour_displayOnLoad_false(map, live_server, datalayer, page): + set_options(datalayer, displayOnLoad=False) + page.goto(f"{live_server.url}{map.get_absolute_url()}") + expect(page.locator(".leaflet-marker-icon")).to_be_hidden() + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + layers_off = page.locator(".umap-browse-datalayers li.off") + expect(layers).to_have_count(1) + expect(layers_off).to_have_count(1) + page.get_by_role("button", name="See data layers").click() + page.get_by_label("Zoom in").click() + expect(markers).to_be_hidden() + page.get_by_title("Show/hide layer").click() + expect(layers_off).to_have_count(0) + expect(markers).to_be_visible() + + +def test_should_honour_fromZoom(live_server, map, datalayer, page): + set_options(datalayer, displayOnLoad=True, fromZoom=6) + page.goto(f"{live_server.url}{map.get_absolute_url()}#5/48.55/14.68") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_be_hidden() + page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.55/14.68") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_be_visible() + page.get_by_label("Zoom out").click() + expect(markers).to_be_hidden() + page.get_by_label("Zoom in").click() + expect(markers).to_be_visible() + page.get_by_label("Zoom in").click() + expect(markers).to_be_visible() + + +def test_should_honour_toZoom(live_server, map, datalayer, page): + set_options(datalayer, displayOnLoad=True, toZoom=6) + page.goto(f"{live_server.url}{map.get_absolute_url()}#7/48.55/14.68") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_be_hidden() + page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.55/14.68") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_be_visible() + page.get_by_label("Zoom out").click() + expect(markers).to_be_visible() + page.get_by_label("Zoom in").click() + expect(markers).to_be_visible() + page.get_by_label("Zoom in").click() + # FIXME does not work (but works when using PWDEBUG=1), not sure why + # may be a race condition related to css transition + # expect(markers).to_be_hidden() + + +def test_should_honour_color_variable(live_server, map, page): + data = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"mycolor": "aliceblue", "name": "Point 4"}, + "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]}, + }, + { + "type": "Feature", + "properties": {"name": "a polygon", "mycolor": "tomato"}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [2.12, 49.57], + [1.08, 49.02], + [2.51, 47.55], + [3.19, 48.77], + [2.12, 49.57], + ] + ], + }, + }, + ], + "_umap_options": { + "name": "Calque 2", + "color": "{mycolor}", + "fillColor": "{mycolor}", + }, + } + DataLayerFactory(map=map, data=data) + page.goto(f"{live_server.url}{map.get_absolute_url()}") + expect(page.locator(".leaflet-overlay-pane path[fill='tomato']")) + markers = page.locator(".leaflet-marker-icon .icon_container") + expect(markers).to_have_css("background-color", "rgb(240, 248, 255)") diff --git a/umap/tests/integration/test_edit_datalayer.py b/umap/tests/integration/test_edit_datalayer.py index 0f379add..67a3de89 100644 --- a/umap/tests/integration/test_edit_datalayer.py +++ b/umap/tests/integration/test_edit_datalayer.py @@ -1,5 +1,11 @@ +import re + from playwright.sync_api import expect +from umap.models import DataLayer, Map + +from ..base import DataLayerFactory + def test_should_have_fieldset_for_layer_type_properties(page, live_server, tilelayer): page.goto(f"{live_server.url}/en/map/new/") @@ -44,3 +50,145 @@ def test_should_have_fieldset_for_layer_type_properties(page, live_server, tilel expect(choropleth_header).to_be_hidden() expect(heat_header).to_be_hidden() expect(cluster_header).to_be_hidden() + + +def test_cancel_deleting_datalayer_should_restore( + live_server, map, login, datalayer, page +): + # Faster than doing a login + map.edit_status = Map.ANONYMOUS + map.save() + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + page.get_by_role("link", name="Manage layers").click() + page.once("dialog", lambda dialog: dialog.accept()) + page.locator("#umap-ui-container").get_by_title("Delete layer").click() + expect(markers).to_have_count(0) + page.get_by_role("button", name="See data layers").click() + expect(page.get_by_text("test datalayer")).to_be_hidden() + page.once("dialog", lambda dialog: dialog.accept()) + page.get_by_role("button", name="Cancel edits").click() + expect(markers).to_have_count(1) + expect( + page.locator(".leaflet-control-browse").get_by_text("test datalayer") + ).to_be_visible() + + +def test_can_clone_datalayer(live_server, map, login, datalayer, page): + # Faster than doing a login + map.edit_status = Map.ANONYMOUS + map.save() + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + page.get_by_role("link", name="Manage layers").click() + page.locator("#umap-ui-container").get_by_title("Edit", exact=True).click() + page.get_by_role("heading", name="Advanced actions").click() + page.get_by_role("button", name="Clone").click() + expect(layers).to_have_count(2) + expect(markers).to_have_count(2) + + +def test_can_change_icon_class(live_server, map, page): + # Faster than doing a login + data = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"name": "Point 4"}, + "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]}, + }, + ], + } + DataLayerFactory(map=map, data=data) + map.edit_status = Map.ANONYMOUS + map.save() + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + expect(page.locator(".umap-div-icon")).to_be_visible() + page.get_by_role("link", name="Manage layers").click() + expect(page.locator(".umap-circle-icon")).to_be_hidden() + page.locator("#umap-ui-container").get_by_title("Edit", exact=True).click() + page.get_by_role("heading", name="Shape properties").click() + page.locator(".umap-field-iconClass a.define").click() + page.get_by_text("Circle").click() + expect(page.locator(".umap-circle-icon")).to_be_visible() + expect(page.locator(".umap-div-icon")).to_be_hidden() + + +def test_can_change_name(live_server, map, page, datalayer): + map.edit_status = Map.ANONYMOUS + map.save() + page.goto( + f"{live_server.url}{map.get_absolute_url()}?edit&datalayersControl=expanded" + ) + page.get_by_role("link", name="Manage layers").click() + page.locator("#umap-ui-container").get_by_title("Edit", exact=True).click() + expect(page.locator(".umap-is-dirty")).to_be_hidden() + page.locator('input[name="name"]').click() + page.locator('input[name="name"]').press("Control+a") + page.locator('input[name="name"]').fill("new name") + expect(page.locator(".leaflet-control-browse li span")).to_contain_text("new name") + expect(page.locator(".umap-is-dirty")).to_be_visible() + with page.expect_response(re.compile(".*/datalayer/update/.*")): + page.get_by_role("button", name="Save").click() + saved = DataLayer.objects.last() + assert saved.name == "new name" + expect(page.locator(".umap-is-dirty")).to_be_hidden() + + +def test_can_create_new_datalayer(live_server, map, page, datalayer): + map.edit_status = Map.ANONYMOUS + map.save() + page.goto( + f"{live_server.url}{map.get_absolute_url()}?edit&datalayersControl=expanded" + ) + page.get_by_role("link", name="Manage layers").click() + page.get_by_role("button", name="Add a layer").click() + page.locator('input[name="name"]').click() + page.locator('input[name="name"]').fill("my new layer") + expect(page.get_by_text("my new layer")).to_be_visible() + with page.expect_response(re.compile(".*/datalayer/create/.*")): + page.get_by_role("button", name="Save").click() + assert DataLayer.objects.count() == 2 + saved = DataLayer.objects.last() + assert saved.name == "my new layer" + expect(page.locator(".umap-is-dirty")).to_be_hidden() + # Edit again, it should not create a new datalayer + page.get_by_role("link", name="Manage layers").click() + page.locator("#umap-ui-container").get_by_title("Edit", exact=True).first.click() + page.locator('input[name="name"]').click() + page.locator('input[name="name"]').fill("my new layer with a new name") + expect(page.get_by_text("my new layer with a new name")).to_be_visible() + page.get_by_role("button", name="Save").click() + with page.expect_response(re.compile(".*/datalayer/update/.*")): + page.get_by_role("button", name="Save").click() + assert DataLayer.objects.count() == 2 + saved = DataLayer.objects.last() + assert saved.name == "my new layer with a new name" + expect(page.locator(".umap-is-dirty")).to_be_hidden() + + +def test_can_restore_version(live_server, map, page, datalayer): + map.edit_status = Map.ANONYMOUS + map.save() + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + marker = page.locator(".leaflet-marker-icon") + expect(marker).to_have_class(re.compile(".*umap-ball-icon.*")) + marker.click(modifiers=["Shift"]) + page.get_by_role("heading", name="Shape properties").click() + page.locator("#umap-feature-shape-properties").get_by_text("Default").click() + with page.expect_response(re.compile(".*/datalayer/update/.*")): + page.get_by_role("button", name="Save").click() + expect(marker).to_have_class(re.compile(".*umap-div-icon.*")) + page.get_by_role("link", name="Manage layers").click() + page.locator("#umap-ui-container").get_by_title("Edit", exact=True).click() + page.get_by_role("heading", name="Versions").click() + page.once("dialog", lambda dialog: dialog.accept()) + page.get_by_role("button", name="Restore this version").last.click() + expect(marker).to_have_class(re.compile(".*umap-ball-icon.*")) diff --git a/umap/tests/integration/test_facets_browser.py b/umap/tests/integration/test_facets_browser.py index 6a09b6ff..68e64ee3 100644 --- a/umap/tests/integration/test_facets_browser.py +++ b/umap/tests/integration/test_facets_browser.py @@ -46,6 +46,30 @@ DATALAYER_DATA2 = { } +DATALAYER_DATA3 = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"name": "a polygon"}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [2.12, 49.57], + [1.08, 49.02], + [2.51, 47.55], + [3.19, 48.77], + [2.12, 49.57], + ] + ], + }, + }, + ], + "_umap_options": {"name": "Calque 2", "browsable": False}, +} + + @pytest.fixture def bootstrap(map, live_server): map.settings["properties"]["onLoadPanel"] = "facet" @@ -54,11 +78,15 @@ def bootstrap(map, live_server): map.save() DataLayerFactory(map=map, data=DATALAYER_DATA1) DataLayerFactory(map=map, data=DATALAYER_DATA2) + DataLayerFactory(map=map, data=DATALAYER_DATA3) def test_simple_facet_search(live_server, page, bootstrap, map): page.goto(f"{live_server.url}{map.get_absolute_url()}") panel = page.locator(".umap-facet-search") + # From a non browsable datalayer, should not be impacted + paths = page.locator(".leaflet-overlay-pane path") + expect(paths).to_be_visible expect(panel).to_be_visible() # Facet name expect(page.get_by_text("My type")).to_be_visible() @@ -67,6 +95,7 @@ def test_simple_facet_search(live_server, page, bootstrap, map): odd = page.get_by_text("odd") expect(oven).to_be_visible() expect(odd).to_be_visible() + expect(paths).to_be_visible markers = page.locator(".leaflet-marker-icon") expect(markers).to_have_count(4) # Tooltips @@ -81,6 +110,8 @@ def test_simple_facet_search(live_server, page, bootstrap, map): expect(page.get_by_text("Point 4")).to_be_hidden() expect(page.get_by_text("Point 1")).to_be_visible() expect(page.get_by_text("Point 3")).to_be_visible() + expect(paths).to_be_visible # Now let's filter odd.click() expect(markers).to_have_count(4) + expect(paths).to_be_visible diff --git a/umap/tests/integration/test_owned_map.py b/umap/tests/integration/test_owned_map.py index 78daff60..f753baba 100644 --- a/umap/tests/integration/test_owned_map.py +++ b/umap/tests/integration/test_owned_map.py @@ -231,3 +231,21 @@ def test_can_change_owner(map, live_server, login, user): save.click() modified = Map.objects.get(pk=map.pk) assert modified.owner == user + + +def test_can_delete_datalayer(live_server, map, login, datalayer): + page = login(map.owner) + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + page.get_by_role("link", name="Manage layers").click() + page.once("dialog", lambda dialog: dialog.accept()) + page.locator("#umap-ui-container").get_by_title("Delete layer").click() + with page.expect_response(re.compile(r".*/datalayer/delete/.*")): + page.get_by_role("button", name="Save").click() + expect(markers).to_have_count(0) + # FIXME does not work, resolve to 1 element, even if this command is empty: + # document.querySelectorAll(".umap-browse-datalayers li") + # expect(layers).to_have_count(0)