diff --git a/umap/static/umap/js/umap.importer.js b/umap/static/umap/js/umap.importer.js index 46193200..5da209da 100644 --- a/umap/static/umap/js/umap.importer.js +++ b/umap/static/umap/js/umap.importer.js @@ -39,11 +39,10 @@ U.Importer = L.Class.extend({ this.container, L._('Choose the layer to import in') ) - this.clearLabel = L.DomUtil.add( + this.clearLabel = L.DomUtil.element( 'label', - '', - this.container, - L._('Replace layer content') + { textContent: L._('Replace layer content'), for: 'datalayer-clear-check' }, + this.container ) this.submitInput = L.DomUtil.element( 'input', @@ -59,7 +58,7 @@ U.Importer = L.Class.extend({ ) this.clearFlag = L.DomUtil.element( 'input', - { type: 'checkbox', name: 'clear' }, + { type: 'checkbox', name: 'clear', id: 'datalayer-clear-check' }, this.clearLabel ) let option diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index 7383f74c..77bda086 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -1540,7 +1540,7 @@ U.Map = L.Map.extend({ metadataFields = ['options.name', 'options.description'], title = L.DomUtil.create('h3', '', container) title.textContent = L._('Edit map properties') - const builder = new U.FormBuilder(this, metadataFields) + const builder = new U.FormBuilder(this, metadataFields, {className: 'map-metadata'}) const form = builder.build() container.appendChild(form) this._editControls(container) 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/Map.js b/umap/static/umap/test/Map.js index e6350721..c6e6fc53 100644 --- a/umap/static/umap/test/Map.js +++ b/umap/static/umap/test/Map.js @@ -19,310 +19,6 @@ describe('U.Map', () => { resetMap() }) - describe('#init()', () => { - it('should be initialized', function () { - assert.equal(map.options.umap_id, 99) - }) - - it('should have created the edit button', function () { - assert.ok(qs('div.leaflet-control-edit-enable')) - }) - - it('should have datalayer control div', function () { - assert.ok(qs('div.leaflet-control-browse')) - }) - - it('should have datalayer actions div', function () { - assert.ok(qs('div.umap-browse-actions')) - }) - - it('should have icon container div', function () { - assert.ok(qs('div.icon_container')) - }) - - it('should hide icon container div when hiding datalayer', function () { - var el = qs( - '.leaflet-control-browse #browse_data_toggle_' + - L.stamp(datalayer) + - ' .layer-toggle' - ) - happen.click(el) - assert.notOk(qs('div.icon_container')) - }) - - it('enable edit on click on toggle button', function () { - var el = qs('div.leaflet-control-edit-enable button') - happen.click(el) - assert.isTrue(L.DomUtil.hasClass(document.body, 'umap-edit-enabled')) - }) - - it('should have only one datalayer in its index', function () { - assert.equal(map.datalayers_index.length, 1) - }) - }) - - describe('#editMetadata()', function () { - var form, input - - it('should build a form on editMetadata control click', function (done) { - var button = qs('a.update-map-settings') - assert.ok(button) - happen.click(button) - form = qs('form.umap-form') - input = qs('form[class="umap-form"] input[name="name"]') - assert.ok(form) - assert.ok(input) - done() - }) - - it('should update map name on input change', function () { - var new_name = 'This is a new name' - input.value = new_name - happen.once(input, { type: 'input' }) - assert.equal(map.options.name, new_name) - }) - - it('should have made Map dirty', function () { - assert.ok(map.isDirty) - }) - - it('should have added dirty class on map container', function () { - assert.ok(L.DomUtil.hasClass(map._container, 'umap-is-dirty')) - }) - }) - - describe('#delete()', function () { - let path = '/map/99/update/delete/', - oldConfirm, - newConfirm = function () { - return true - } - - before(function () { - oldConfirm = window.confirm - window.confirm = newConfirm - }) - after(function () { - window.confirm = oldConfirm - }) - - it('should ask for confirmation on delete link click', async function () { - let button = qs('a.update-map-settings') - assert.ok(button, 'update map info button exists') - happen.click(button) - let deleteLink = qs('button.umap-delete') - assert.ok(deleteLink, 'delete map button exists') - sinon.spy(window, 'confirm') - await fetchMock.post(path, { redirect: '#' }) - happen.click(deleteLink) - assert(window.confirm.calledOnce) - window.confirm.restore() - }) - }) - - describe('#importData()', function () { - let fileInput, textarea, submit, formatSelect, layerSelect, clearFlag - - it('should build a form on click', function () { - happen.click(qs('a.upload-data')) - fileInput = qs('.umap-upload input[type="file"]') - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - layerSelect = qs('.umap-upload select[name="datalayer"]') - assert.ok(fileInput) - assert.ok(submit) - assert.ok(textarea) - assert.ok(formatSelect) - assert.ok(layerSelect) - }) - - it('should import geojson from textarea', function () { - datalayer.empty() - assert.equal(datalayer._index.length, 0) - textarea.value = - '{"type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates": [6.922931671142578, 47.481161607175736]}, "type": "Feature", "properties": {"color": "", "name": "Chez R\u00e9my", "description": ""}}, {"geometry": {"type": "LineString", "coordinates": [[2.4609375, 48.88639177703194], [2.48291015625, 48.76343113791796], [2.164306640625, 48.719961222646276]]}, "type": "Feature", "properties": {"color": "", "name": "P\u00e9rif", "description": ""}}]}' - changeSelectValue(formatSelect, 'geojson') - happen.click(submit) - assert.equal(datalayer._index.length, 2) - }) - - it('should remove dot in property name', function () { - datalayer.empty() - assert.equal(datalayer._index.length, 0) - textarea.value = - '{"type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates": [6.922931671142578, 47.481161607175736]}, "type": "Feature", "properties": {"color": "", "name": "Chez R\u00e9my", "A . in the name": ""}}, {"geometry": {"type": "LineString", "coordinates": [[2.4609375, 48.88639177703194], [2.48291015625, 48.76343113791796], [2.164306640625, 48.719961222646276]]}, "type": "Feature", "properties": {"color": "", "name": "P\u00e9rif", "with a dot.": ""}}]}' - changeSelectValue(formatSelect, 'geojson') - happen.click(submit) - assert.equal(datalayer._index.length, 2) - assert.ok(datalayer._propertiesIndex.includes('A _ in the name')) - assert.ok(datalayer._propertiesIndex.includes('with a dot_')) - }) - - it('should import osm from textarea', function () { - datalayer.empty() - happen.click(qs('a.upload-data')) - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - assert.equal(datalayer._index.length, 0) - textarea.value = - '{"version": 0.6,"generator": "Overpass API 0.7.55.4 3079d8ea","osm3s": {"timestamp_osm_base": "2018-09-22T05:26:02Z","copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."},"elements": [{"type": "node","id": 3619112991,"lat": 48.9352995,"lon": 2.3570684,"tags": {"information": "map","map_size": "city","map_type": "scheme","tourism": "information"}},{"type": "node","id": 3682500756,"lat": 48.9804426,"lon": 2.2719725,"tags": {"information": "map","level": "0","tourism": "information"}}]}' - changeSelectValue(formatSelect, 'osm') - happen.click(submit) - assert.equal(datalayer._index.length, 2) - assert.equal( - datalayer._layers[datalayer._index[0]].properties.tourism, - 'information' - ) - }) - - it('should import kml from textarea', function () { - datalayer.empty() - happen.click(qs('a.upload-data')) - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - assert.equal(datalayer._index.length, 0) - textarea.value = kml_example - changeSelectValue(formatSelect, 'kml') - happen.click(submit) - assert.equal(datalayer._index.length, 3) - }) - - it('should import gpx from textarea', function () { - datalayer.empty() - happen.click(qs('a.upload-data')) - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - assert.equal(datalayer._index.length, 0) - textarea.value = gpx_example - changeSelectValue(formatSelect, 'gpx') - happen.click(submit) - assert.equal(datalayer._index.length, 2) - }) - - it('should import csv from textarea', function () { - datalayer.empty() - happen.click(qs('a.upload-data')) - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - assert.equal(datalayer._index.length, 0) - textarea.value = csv_example - changeSelectValue(formatSelect, 'csv') - happen.click(submit) - assert.equal(datalayer._index.length, 1) - }) - - it('should replace content if asked so', function () { - happen.click(qs('a.upload-data')) - textarea = qs('.umap-upload textarea') - submit = qs('.umap-upload input[type="button"]') - formatSelect = qs('.umap-upload select[name="format"]') - clearFlag = qs('.umap-upload input[name="clear"]') - clearFlag.checked = true - assert.equal(datalayer._index.length, 1) - textarea.value = csv_example - changeSelectValue(formatSelect, 'csv') - happen.click(submit) - assert.equal(datalayer._index.length, 1) - }) - - it('should import GeometryCollection from textarea', function () { - datalayer.empty() - textarea.value = - '{"type": "GeometryCollection","geometries": [{"type": "Point","coordinates": [-80.66080570220947,35.04939206472683]},{"type": "Polygon","coordinates": [[[-80.66458225250244,35.04496519190309],[-80.66344499588013,35.04603679820616],[-80.66258668899536,35.045580049697556],[-80.66387414932251,35.044280059194946],[-80.66458225250244,35.04496519190309]]]},{"type": "LineString","coordinates": [[-80.66237211227417,35.05950973022538],[-80.66269397735596,35.0592638296087],[-80.66284418106079,35.05893010615862],[-80.66308021545409,35.05833291342246],[-80.66359519958496,35.057753281001425],[-80.66387414932251,35.05740198662245],[-80.66441059112549,35.05703312589789],[-80.66486120223999,35.056787217822475],[-80.66541910171509,35.05650617911516],[-80.66563367843628,35.05631296444281],[-80.66601991653441,35.055891403570705],[-80.66619157791138,35.05545227534804],[-80.66619157791138,35.05517123204622],[-80.66625595092773,35.05489018777713],[-80.6662130355835,35.054222703761525],[-80.6662130355835,35.05392409072499],[-80.66595554351807,35.05290528508858],[-80.66569805145262,35.052044560077285],[-80.66550493240356,35.0514824490509],[-80.665762424469,35.05048117920187],[-80.66617012023926,35.04972582715769],[-80.66651344299316,35.049286665781096],[-80.66692113876343,35.0485313026898],[-80.66700696945189,35.048215102112344],[-80.66707134246826,35.04777593261294],[-80.66704988479614,35.04738946150025],[-80.66696405410767,35.04698542156371],[-80.66681385040283,35.046353007216055],[-80.66659927368164,35.04596652937105],[-80.66640615463257,35.04561518428889],[-80.6659984588623,35.045193568195565],[-80.66552639007568,35.044877354697526],[-80.6649899482727,35.04454357245502],[-80.66449642181396,35.04417465365292],[-80.66385269165039,35.04387600387859],[-80.66303730010986,35.043717894732545]]}]}' - formatSelect = qs('.umap-upload select[name="format"]') - changeSelectValue(formatSelect, 'geojson') - happen.click(submit) - assert.equal(datalayer._index.length, 3) - }) - - it('should import multipolygon', function () { - datalayer.empty() - textarea.value = - '{"type": "Feature", "properties": { "name": "Some states" }, "geometry": { "type": "MultiPolygon", "coordinates": [[[[-109, 36], [-109, 40], [-102, 37], [-109, 36]], [[-108, 39], [-107, 37], [-104, 37], [-108, 39]]], [[[-119, 42], [-120, 39], [-114, 41], [-119, 42]]]] }}' - changeSelectValue(formatSelect, 'geojson') - happen.click(submit) - assert.equal(datalayer._index.length, 1) - var layer = datalayer.getFeatureByIndex(0) - assert.equal(layer._latlngs.length, 2) // Two shapes. - assert.equal(layer._latlngs[0].length, 2) // Hole. - }) - - it('should import multipolyline', function () { - datalayer.empty() - textarea.value = - '{"type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": { "type": "MultiLineString", "coordinates": [[[-108, 46], [-113, 43]], [[-112, 45], [-115, 44]]] } }]}' - changeSelectValue(formatSelect, 'geojson') - happen.click(submit) - assert.equal(datalayer._index.length, 1) - var layer = datalayer.getFeatureByIndex(0) - assert.equal(layer._latlngs.length, 2) // Two shapes. - }) - - it('should import raw umap data from textarea', function () { - //Right now, the import function will try to save and reload. Stop this from happening. - var disabledSaveFunction = map.save - map.save = function () {} - happen.click(qs('a.upload-data')) - var initialLayerCount = Object.keys(map.datalayers).length - formatSelect = qs('.umap-upload select[name="format"]') - textarea = qs('.umap-upload textarea') - textarea.value = - '{ "type": "umap", "geometry": { "type": "Point", "coordinates": [3.0528, 50.6269] }, "properties": { "umap_id": 666, "longCredit": "the illustrious mapmaker", "shortCredit": "the mapmaker", "slideshow": {}, "captionBar": true, "dashArray": "5,5", "fillOpacity": "0.5", "fillColor": "Crimson", "fill": true, "weight": "2", "opacity": "0.9", "smoothFactor": "1", "iconClass": "Drop", "color": "Red", "limitBounds": {}, "tilelayer": { "maxZoom": 18, "url_template": "http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg", "minZoom": 0, "attribution": "Map tiles by [[http://stamen.com|Stamen Design]], under [[http://creativecommons.org/licenses/by/3.0|CC BY 3.0]]. Data by [[http://openstreetmap.org|OpenStreetMap]], under [[http://creativecommons.org/licenses/by-sa/3.0|CC BY SA]].", "name": "Watercolor" }, "licence": { "url": "", "name": "No licence set" }, "description": "Map description", "name": "Imported map", "tilelayersControl": true, "onLoadPanel": "caption", "displayPopupFooter": true, "miniMap": true, "moreControl": true, "scaleControl": true, "zoomControl": true, "scrollWheelZoom": true, "datalayersControl": true, "zoom": 6 }, "layers": [{ "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [4.2939, 50.8893], [4.2441, 50.8196], [4.3869, 50.7642], [4.4813, 50.7929], [4.413, 50.9119], [4.2939, 50.8893] ] ] }, "properties": { "name": "Bruxelles", "description": "polygon" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [3.0528, 50.6269] }, "properties": { "_umap_options": { "color": "Orange" }, "name": "Lille", "description": "une ville" } }], "_umap_options": { "displayOnLoad": true, "name": "Cities", "id": 108, "remoteData": {}, "description": "A layer with some cities", "color": "Navy", "iconClass": "Drop", "smoothFactor": "1", "dashArray": "5,1", "fillOpacity": "0.5", "fillColor": "Blue", "fill": true } }, { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [1.7715, 50.9255], [1.6589, 50.9696], [1.4941, 51.0128], [1.4199, 51.0638], [1.2881, 51.1104] ] }, "properties": { "_umap_options": { "weight": "4" }, "name": "tunnel sous la Manche" } }], "_umap_options": { "displayOnLoad": true, "name": "Tunnels", "id": 109, "remoteData": {} } }]}' - formatSelect.value = 'umap' - submit = qs('.umap-upload input[type="button"]') - happen.click(submit) - assert.equal(Object.keys(map.datalayers).length, initialLayerCount + 2) - assert.equal(map.options.name, 'Imported map') - var foundFirstLayer = false - var foundSecondLayer = false - for (var idx in map.datalayers) { - var datalayer = map.datalayers[idx] - if (datalayer.options.name === 'Cities') { - foundFirstLayer = true - assert.equal(datalayer._index.length, 2) - } - if (datalayer.options.name === 'Tunnels') { - foundSecondLayer = true - assert.equal(datalayer._index.length, 1) - } - } - assert.equal(foundFirstLayer, true) - assert.equal(foundSecondLayer, true) - }) - - it('should only import options on the whitelist (umap format import)', function () { - assert.equal(map.options.umap_id, 99) - }) - - it('should update title bar (umap format import)', function () { - var title = qs('#map div.umap-main-edit-toolbox button.map-name') - assert.equal(title.innerHTML, 'Imported map') - }) - - it('should reinitialize controls (umap format import)', function () { - var minimap = qs('#map div.leaflet-control-container div.leaflet-control-minimap') - assert.ok(minimap) - }) - - it('should update the tilelayer switcher control (umap format import)', function () { - //The tilelayer in the imported data isn't in the tilelayer list (set in _pre.js), there should be no selection on the tilelayer switcher - var selectedLayer = qs('.umap-tilelayer-switcher-container li.selected') - assert.equal(selectedLayer, null) - }) - - it('should set the tilelayer (umap format import)', function () { - assert.equal( - map.selected_tilelayer._url, - 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg' - ) - }) - }) - describe('#localizeUrl()', function () { it('should replace known variables', function () { assert.equal( diff --git a/umap/static/umap/test/TableEditor.js b/umap/static/umap/test/TableEditor.js deleted file mode 100644 index aa0578af..00000000 --- a/umap/static/umap/test/TableEditor.js +++ /dev/null @@ -1,104 +0,0 @@ -describe('L.TableEditor', () => { - let path = '/map/99/datalayer/edit/62/', - datalayer - - before(async () => { - await fetchMock.mock( - /\/datalayer\/62\/\?.*/, - JSON.stringify(RESPONSES.datalayer62_GET) - ) - this.options = { - umap_id: 99, - } - map = initMap({ umap_id: 99 }) - const datalayer_options = defaultDatalayerData() - await map.initDataLayers([datalayer_options]) - datalayer = map.getDataLayerByUmapId(62) - enableEdit() - }) - after(() => { - fetchMock.restore() - clickCancel() - resetMap() - }) - - describe('#open()', () => { - var button - - it('should exist table click on edit mode', () => { - button = qs( - '#browse_data_toggle_' + L.stamp(datalayer) + ' .layer-table-edit' - ) - expect(button).to.be.ok - }) - - it('should open table button click', () => { - happen.click(button) - expect(qs('#umap-ui-container div.table')).to.be.ok - expect(qsa('#umap-ui-container div.table form').length).to.eql(3) // One per feature. - expect(qsa('#umap-ui-container div.table input').length).to.eql(3) // One per feature and per property. - }) - }) - describe('#properties()', () => { - var feature - - before(() => { - var firstIndex = datalayer._index[0] - feature = datalayer._layers[firstIndex] - }) - - it('should create new property column', () => { - var newPrompt = () => { - return 'newprop' - } - var oldPrompt = window.prompt - window.prompt = newPrompt - var button = qs('#umap-ui-container .add-property') - expect(button).to.be.ok - happen.click(button) - expect(qsa('#umap-ui-container div.table input').length).to.eql(6) // One per feature and per property. - window.prompt = oldPrompt - }) - - it('should populate feature property on fill', () => { - var input = qs( - 'form#umap-feature-properties_' + L.stamp(feature) + ' input[name=newprop]' - ) - changeInputValue(input, 'the value') - expect(feature.properties.newprop).to.eql('the value') - }) - - it('should update property name on update click', () => { - var newPrompt = () => { - return 'newname' - } - var oldPrompt = window.prompt - window.prompt = newPrompt - var button = qs('#umap-ui-container div.thead div.tcell:last-of-type .umap-edit') - expect(button).to.be.ok - happen.click(button) - expect(qsa('#umap-ui-container div.table input').length).to.eql(6) - expect(feature.properties.newprop).to.be.undefined - expect(feature.properties.newname).to.eql('the value') - window.prompt = oldPrompt - }) - - it('should update property on delete click', () => { - var oldConfirm, - newConfirm = () => { - return true - } - oldConfirm = window.confirm - window.confirm = newConfirm - var button = qs( - '#umap-ui-container div.thead div.tcell:last-of-type .umap-delete' - ) - expect(button).to.be.ok - happen.click(button) - FEATURE = feature - expect(qsa('#umap-ui-container div.table input').length).to.eql(3) - expect(feature.properties.newname).to.be.undefined - window.confirm = oldConfirm - }) - }) -}) diff --git a/umap/static/umap/test/index.html b/umap/static/umap/test/index.html index 8ab2c567..9fee3dba 100644 --- a/umap/static/umap/test/index.html +++ b/umap/static/umap/test/index.html @@ -3,6 +3,7 @@ Umap front Tests + @@ -88,8 +89,6 @@ - - diff --git a/umap/tests/conftest.py b/umap/tests/conftest.py index 78e207cb..4dafa3dd 100644 --- a/umap/tests/conftest.py +++ b/umap/tests/conftest.py @@ -5,6 +5,8 @@ import pytest from django.core.cache import cache from django.core.signing import get_cookie_signer +from umap.models import Map + from .base import ( DataLayerFactory, LicenceFactory, @@ -48,6 +50,13 @@ def map(licence, tilelayer): return MapFactory(owner=user, licence=licence) +@pytest.fixture +def openmap(map): + map.edit_status = Map.ANONYMOUS + map.save() + return map + + @pytest.fixture def anonymap(map): map.owner = None diff --git a/umap/tests/fixtures/test_upload_data.csv b/umap/tests/fixtures/test_upload_data.csv index 107f2510..c6266599 100644 --- a/umap/tests/fixtures/test_upload_data.csv +++ b/umap/tests/fixtures/test_upload_data.csv @@ -1,2 +1,3 @@ Foo,Latitude,geo_Longitude,title,description -bar,41.34,122.86,a point somewhere,the description of this point \ No newline at end of file +bar,41.34,122.86,a point somewhere,the description of this point +bar,43.34,121.86,a point somewhere else,the description of this other point diff --git a/umap/tests/fixtures/test_upload_data.umap b/umap/tests/fixtures/test_upload_data.umap new file mode 100644 index 00000000..0392e2c2 --- /dev/null +++ b/umap/tests/fixtures/test_upload_data.umap @@ -0,0 +1,171 @@ +{ + "type": "umap", + "geometry": { + "type": "Point", + "coordinates": [ + 3.0528, + 50.6269 + ] + }, + "properties": { + "umap_id": 666, + "longCredit": "the illustrious mapmaker", + "shortCredit": "the mapmaker", + "slideshow": {}, + "captionBar": true, + "dashArray": "5,5", + "fillOpacity": "0.5", + "fillColor": "Crimson", + "fill": true, + "weight": "2", + "opacity": "0.9", + "smoothFactor": "1", + "iconClass": "Drop", + "color": "Red", + "limitBounds": {}, + "tilelayer": { + "maxZoom": 20, + "url_template": "https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", + "minZoom": 0, + "attribution": "map data © [[https://osm.org/copyright|OpenStreetMap contributors]] under ODbL - Tiles © HOT", + "name": "OSM Humanitarian (OSM-FR)" + }, + "licence": { + "url": "", + "name": "No licence set" + }, + "description": "Map description", + "name": "Imported map", + "tilelayersControl": true, + "onLoadPanel": "caption", + "displayPopupFooter": true, + "miniMap": true, + "moreControl": true, + "scaleControl": true, + "zoomControl": true, + "scrollWheelZoom": true, + "datalayersControl": true, + "zoom": 6 + }, + "layers": [ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 4.2939, + 50.8893 + ], + [ + 4.2441, + 50.8196 + ], + [ + 4.3869, + 50.7642 + ], + [ + 4.4813, + 50.7929 + ], + [ + 4.413, + 50.9119 + ], + [ + 4.2939, + 50.8893 + ] + ] + ] + }, + "properties": { + "name": "Bruxelles", + "description": "polygon" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 3.0528, + 50.6269 + ] + }, + "properties": { + "_umap_options": { + "color": "Orange" + }, + "name": "Lille", + "description": "une ville" + } + } + ], + "_umap_options": { + "displayOnLoad": true, + "name": "Cities", + "id": 108, + "remoteData": {}, + "description": "A layer with some cities", + "color": "Navy", + "iconClass": "Drop", + "smoothFactor": "1", + "dashArray": "5,1", + "fillOpacity": "0.5", + "fillColor": "Blue", + "fill": true + } + }, + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 1.7715, + 50.9255 + ], + [ + 1.6589, + 50.9696 + ], + [ + 1.4941, + 51.0128 + ], + [ + 1.4199, + 51.0638 + ], + [ + 1.2881, + 51.1104 + ] + ] + }, + "properties": { + "_umap_options": { + "weight": "4" + }, + "name": "tunnel sous la Manche" + } + } + ], + "_umap_options": { + "displayOnLoad": true, + "name": "Tunnels", + "id": 109, + "remoteData": {} + } + } + ] +} diff --git a/umap/tests/fixtures/test_upload_data_osm.json b/umap/tests/fixtures/test_upload_data_osm.json new file mode 100644 index 00000000..dff71593 --- /dev/null +++ b/umap/tests/fixtures/test_upload_data_osm.json @@ -0,0 +1,33 @@ +{ + "version": 0.6, + "generator": "Overpass API 0.7.55.4 3079d8ea", + "osm3s": { + "timestamp_osm_base": "2018-09-22T05:26:02Z", + "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." + }, + "elements": [ + { + "type": "node", + "id": 3619112991, + "lat": 48.9352995, + "lon": 2.3570684, + "tags": { + "information": "map", + "map_size": "city", + "map_type": "scheme", + "tourism": "information" + } + }, + { + "type": "node", + "id": 3682500756, + "lat": 48.9804426, + "lon": 2.2719725, + "tags": { + "information": "map", + "level": "0", + "tourism": "information" + } + } + ] +} diff --git a/umap/tests/integration/test_browser.py b/umap/tests/integration/test_browser.py index 8a73c007..41d89584 100644 --- a/umap/tests/integration/test_browser.py +++ b/umap/tests/integration/test_browser.py @@ -4,8 +4,6 @@ from time import sleep import pytest from playwright.sync_api import expect -from umap.models import Map - from ..base import DataLayerFactory pytestmark = pytest.mark.django_db @@ -247,10 +245,8 @@ def test_should_sort_features_in_natural_order(live_server, map, page): expect(features.nth(2)).to_have_text("100. a line") -def test_should_redraw_list_on_feature_delete(live_server, map, page, bootstrap): - map.edit_status = Map.ANONYMOUS - map.save() - page.goto(f"{live_server.url}{map.get_absolute_url()}") +def test_should_redraw_list_on_feature_delete(live_server, openmap, page, bootstrap): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") # Enable edit page.get_by_role("button", name="Edit").click() buttons = page.locator(".umap-browse-data li .feature-delete") @@ -305,11 +301,8 @@ def test_should_allow_to_toggle_datalayer_visibility(live_server, map, page, boo expect(paths).to_have_count(0) -def test_should_have_edit_buttons_in_edit_mode(live_server, map, page, bootstrap): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - page.goto(f"{live_server.url}{map.get_absolute_url()}") +def test_should_have_edit_buttons_in_edit_mode(live_server, openmap, page, bootstrap): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") browser = page.locator("#umap-ui-container") edit_layer = browser.get_by_title("Edit", exact=True) in_table = browser.get_by_title("Edit properties in a table") diff --git a/umap/tests/integration/test_choropleth.py b/umap/tests/integration/test_choropleth.py index 7b5cdeab..baed5e71 100644 --- a/umap/tests/integration/test_choropleth.py +++ b/umap/tests/integration/test_choropleth.py @@ -4,8 +4,6 @@ from pathlib import Path import pytest from playwright.sync_api import expect -from umap.models import Map - from ..base import DataLayerFactory pytestmark = pytest.mark.django_db @@ -28,19 +26,15 @@ def test_basic_choropleth_map_with_default_color(map, live_server, page): expect(page.locator("path[fill='#eff3ff']")).to_have_count(3) -def test_basic_choropleth_map_with_custom_brewer(map, live_server, page): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - +def test_basic_choropleth_map_with_custom_brewer(openmap, live_server, page): path = Path(__file__).parent.parent / "fixtures/choropleth_region_chomage.geojson" data = json.loads(path.read_text()) # Change brewer at load data["_umap_options"]["choropleth"]["brewer"] = "Reds" - DataLayerFactory(data=data, map=map) + DataLayerFactory(data=data, map=openmap) - page.goto(f"{live_server.url}{map.get_absolute_url()}") + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") # Hauts-de-France expect(page.locator("path[fill='#a50f15']")).to_have_count(1) # Occitanie @@ -71,19 +65,15 @@ def test_basic_choropleth_map_with_custom_brewer(map, live_server, page): expect(page.locator("path[fill='#edf8e9']")).to_have_count(3) -def test_basic_choropleth_map_with_custom_classes(map, live_server, page): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - +def test_basic_choropleth_map_with_custom_classes(openmap, live_server, page): path = Path(__file__).parent.parent / "fixtures/choropleth_region_chomage.geojson" data = json.loads(path.read_text()) # Change brewer at load data["_umap_options"]["choropleth"]["classes"] = 6 - DataLayerFactory(data=data, map=map) + DataLayerFactory(data=data, map=openmap) - page.goto(f"{live_server.url}{map.get_absolute_url()}") + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") # Hauts-de-France expect(page.locator("path[fill='#08519c']")).to_have_count(1) diff --git a/umap/tests/integration/test_collaborative_editing.py b/umap/tests/integration/test_collaborative_editing.py index d6daf27d..d441c68e 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,30 @@ 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, openmap): + # Open the map on two pages. + page_one = context.new_page() + page_one.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") + page_two = context.new_page() + page_two.goto(f"{live_server.url}{openmap.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..1198b539 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 + +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,131 @@ 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, openmap, datalayer, page +): + page.goto(f"{live_server.url}{openmap.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, openmap, login, datalayer, page): + page.goto(f"{live_server.url}{openmap.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, openmap, 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=openmap, data=data) + page.goto(f"{live_server.url}{openmap.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, openmap, page, datalayer): + page.goto( + f"{live_server.url}{openmap.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, openmap, page, datalayer): + page.goto( + f"{live_server.url}{openmap.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, openmap, page, datalayer): + page.goto(f"{live_server.url}{openmap.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_edit_map.py b/umap/tests/integration/test_edit_map.py new file mode 100644 index 00000000..4820d469 --- /dev/null +++ b/umap/tests/integration/test_edit_map.py @@ -0,0 +1,15 @@ +from playwright.sync_api import expect + + +def test_can_edit_name(page, live_server, tilelayer): + page.goto(f"{live_server.url}/en/map/new/") + + page.get_by_title("Edit map properties").click() + name_input = page.locator('.map-metadata input[name="name"]') + expect(name_input).to_be_visible() + name_input.click() + name_input.press("Control+a") + name_input.fill("New map name") + expect(page.locator(".umap-main-edit-toolbox .map-name")).to_have_text( + "New map name" + ) 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_import.py b/umap/tests/integration/test_import.py index 2f56b99c..7c278342 100644 --- a/umap/tests/integration/test_import.py +++ b/umap/tests/integration/test_import.py @@ -1,12 +1,16 @@ +import json +import re from pathlib import Path import pytest from playwright.sync_api import expect +from umap.models import DataLayer + pytestmark = pytest.mark.django_db -def test_umap_import_from_file(live_server, datalayer, page): +def test_umap_import_from_file(live_server, tilelayer, page): page.goto(f"{live_server.url}/map/new/") button = page.get_by_title("Import data") expect(button).to_be_visible() @@ -28,9 +32,40 @@ def test_umap_import_from_file(live_server, datalayer, page): # Close the import panel page.keyboard.press("Escape") assert not file_input.input_value() + expect(page.locator(".umap-main-edit-toolbox .map-name")).to_have_text( + "Carte sans nom" + ) -def test_umap_import_geojson_from_textarea(live_server, datalayer, page): +def test_umap_import_from_textarea(live_server, tilelayer, page, settings): + settings.UMAP_ALLOW_ANONYMOUS = True + page.goto(f"{live_server.url}/map/new/") + page.get_by_title("Import data").click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.umap" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("umap") + page.get_by_role("button", name="Import", exact=True).click() + layers = page.locator(".umap-browse-datalayers li") + expect(layers).to_have_count(2) + expect(page.locator(".umap-main-edit-toolbox .map-name")).to_have_text( + "Imported map" + ) + page.get_by_role("button", name="See data layers").click() + expect(page.get_by_text("Tunnels")).to_be_visible() + expect(page.get_by_text("Cities")).to_be_visible() + expect(page.locator(".leaflet-control-minimap")).to_be_visible() + expect( + page.locator('img[src="https://tile.openstreetmap.fr/hot/6/32/21.png"]') + ).to_be_visible() + # Should not have imported umap_id, while in the file options + assert not page.evaluate("U.MAP.options.umap_id") + with page.expect_response(re.compile(r".*/datalayer/create/.*")): + page.get_by_role("button", name="Save").click() + assert page.evaluate("U.MAP.options.umap_id") + + +def test_import_geojson_from_textarea(tilelayer, live_server, page): page.goto(f"{live_server.url}/map/new/") layers = page.locator(".umap-browse-datalayers li") markers = page.locator(".leaflet-marker-icon") @@ -52,3 +87,313 @@ def test_umap_import_geojson_from_textarea(live_server, datalayer, page): expect(layers).to_have_count(1) expect(markers).to_have_count(2) expect(paths).to_have_count(3) + + +def test_import_kml_from_textarea(tilelayer, live_server, page): + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + paths = page.locator("path") + expect(markers).to_have_count(0) + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.kml" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("kml") + button = page.get_by_role("button", name="Import", exact=True) + expect(button).to_be_visible() + button.click() + # A layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + expect(paths).to_have_count(2) + + +def test_import_gpx_from_textarea(tilelayer, live_server, page): + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + paths = page.locator("path") + expect(markers).to_have_count(0) + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.gpx" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("gpx") + button = page.get_by_role("button", name="Import", exact=True) + expect(button).to_be_visible() + button.click() + # A layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + expect(paths).to_have_count(1) + + +def test_import_osm_from_textarea(tilelayer, live_server, page): + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data_osm.json" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("osm") + page.get_by_role("button", name="Import", exact=True).click() + # A layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(2) + + +def test_import_csv_from_textarea(tilelayer, live_server, page): + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.csv" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("csv") + page.get_by_role("button", name="Import", exact=True).click() + # A layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(2) + + +def test_can_import_in_existing_datalayer(live_server, datalayer, page, openmap): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + expect(layers).to_have_count(1) + page.get_by_role("button", name="Edit").click() + page.get_by_title("Import data").click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.csv" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("csv") + page.get_by_role("button", name="Import", exact=True).click() + # No layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(3) + + +def test_can_replace_datalayer_data(live_server, datalayer, page, openmap): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + expect(layers).to_have_count(1) + page.get_by_role("button", name="Edit").click() + page.get_by_title("Import data").click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.csv" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("csv") + page.get_by_label("Replace layer content").check() + page.get_by_role("button", name="Import", exact=True).click() + # No layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(2) + + +def test_can_import_in_new_datalayer(live_server, datalayer, page, openmap): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + expect(layers).to_have_count(1) + page.get_by_role("button", name="Edit").click() + page.get_by_title("Import data").click() + textarea = page.locator(".umap-upload textarea") + path = Path(__file__).parent.parent / "fixtures/test_upload_data.csv" + textarea.fill(path.read_text()) + page.locator('select[name="format"]').select_option("csv") + page.get_by_label("Choose the layer to import").select_option( + label="Import in a new layer" + ) + page.get_by_role("button", name="Import", exact=True).click() + # A new layer has been created + expect(layers).to_have_count(2) + expect(markers).to_have_count(3) + + +def test_should_remove_dot_in_property_names(live_server, page, settings, tilelayer): + settings.UMAP_ALLOW_ANONYMOUS = True + data = { + "type": "FeatureCollection", + "features": [ + { + "geometry": { + "type": "Point", + "coordinates": [6.922931671142578, 47.481161607175736], + }, + "type": "Feature", + "properties": { + "color": "", + "name": "Chez Rémy", + "A . in the name": "", + }, + }, + { + "geometry": { + "type": "LineString", + "coordinates": [ + [2.4609375, 48.88639177703194], + [2.48291015625, 48.76343113791796], + [2.164306640625, 48.719961222646276], + ], + }, + "type": "Feature", + "properties": {"color": "", "name": "Périf", "with a dot.": ""}, + }, + ], + } + page.goto(f"{live_server.url}/map/new/") + page.get_by_title("Import data").click() + textarea = page.locator(".umap-upload textarea") + textarea.fill(json.dumps(data)) + page.locator('select[name="format"]').select_option("geojson") + page.get_by_role("button", name="Import", exact=True).click() + with page.expect_response(re.compile(r".*/datalayer/create/.*")): + page.get_by_role("button", name="Save").click() + datalayer = DataLayer.objects.last() + saved_data = json.loads(Path(datalayer.geojson.path).read_text()) + assert saved_data["features"][0]["properties"] == { + "color": "", + "name": "Chez Rémy", + "A _ in the name": "", + } + assert saved_data["features"][1]["properties"] == { + "color": "", + "name": "Périf", + "with a dot_": "", + } + + +def test_import_geometry_collection(live_server, page, tilelayer): + data = { + "type": "GeometryCollection", + "geometries": [ + {"type": "Point", "coordinates": [-80.6608, 35.0493]}, + { + "type": "Polygon", + "coordinates": [ + [ + [-80.6645, 35.0449], + [-80.6634, 35.0460], + [-80.6625, 35.0455], + [-80.6638, 35.0442], + [-80.6645, 35.0449], + ] + ], + }, + { + "type": "LineString", + "coordinates": [ + [-80.66237, 35.05950], + [-80.66269, 35.05926], + [-80.66284, 35.05893], + [-80.66308, 35.05833], + [-80.66385, 35.04387], + [-80.66303, 35.04371], + ], + }, + ], + } + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + markers = page.locator(".leaflet-marker-icon") + paths = page.locator("path") + expect(markers).to_have_count(0) + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + textarea.fill(json.dumps(data)) + page.locator('select[name="format"]').select_option("geojson") + page.get_by_role("button", name="Import", exact=True).click() + # A layer has been created + expect(layers).to_have_count(1) + expect(markers).to_have_count(1) + expect(paths).to_have_count(2) + + +def test_import_multipolygon(live_server, page, tilelayer): + data = { + "type": "Feature", + "properties": {"name": "Some states"}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [[-109, 36], [-109, 40], [-102, 37], [-109, 36]], + [[-108, 39], [-107, 37], [-104, 37], [-108, 39]], + ], + [[[-119, 42], [-120, 39], [-114, 41], [-119, 42]]], + ], + }, + } + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + paths = page.locator("path") + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + textarea.fill(json.dumps(data)) + page.locator('select[name="format"]').select_option("geojson") + page.get_by_role("button", name="Import", exact=True).click() + # A layer has been created + expect(layers).to_have_count(1) + expect(paths).to_have_count(1) + + +def test_import_multipolyline(live_server, page, tilelayer): + data = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiLineString", + "coordinates": [[[-108, 46], [-113, 43]], [[-112, 45], [-115, 44]]], + }, + } + ], + } + page.goto(f"{live_server.url}/map/new/") + layers = page.locator(".umap-browse-datalayers li") + paths = page.locator("path") + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + textarea.fill(json.dumps(data)) + page.locator('select[name="format"]').select_option("geojson") + page.get_by_role("button", name="Import", exact=True).click() + # A layer has been created + expect(layers).to_have_count(1) + expect(paths).to_have_count(1) diff --git a/umap/tests/integration/test_map.py b/umap/tests/integration/test_map.py index ef27ab72..fda8dec9 100644 --- a/umap/tests/integration/test_map.py +++ b/umap/tests/integration/test_map.py @@ -3,8 +3,6 @@ import re import pytest from playwright.sync_api import expect -from umap.models import Map - from ..base import DataLayerFactory pytestmark = pytest.mark.django_db @@ -149,18 +147,15 @@ def test_default_view_latest_with_polygon(map, live_server, page): def test_remote_layer_should_not_be_used_as_datalayer_for_created_features( - map, live_server, datalayer, page + openmap, live_server, datalayer, page ): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() datalayer.settings["remoteData"] = { "url": "https://overpass-api.de/api/interpreter?data=[out:xml];node[harbour=yes]({south},{west},{north},{east});out body;", "format": "osm", "from": "10", } datalayer.save() - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") toggle = page.get_by_role("button", name="See data layers") expect(toggle).to_be_visible() toggle.click() @@ -179,13 +174,10 @@ def test_remote_layer_should_not_be_used_as_datalayer_for_created_features( expect(layers).to_have_count(2) -def test_can_hide_datalayer_from_caption(map, live_server, datalayer, page): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() +def test_can_hide_datalayer_from_caption(openmap, live_server, datalayer, page): # Add another DataLayer - other = DataLayerFactory(map=map, name="Hidden", settings={"inCaption": False}) - page.goto(f"{live_server.url}{map.get_absolute_url()}") + other = DataLayerFactory(map=openmap, name="Hidden", settings={"inCaption": False}) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}") toggle = page.get_by_text("About").first expect(toggle).to_be_visible() toggle.click() 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) diff --git a/umap/tests/integration/test_picto.py b/umap/tests/integration/test_picto.py index a44e5a02..33a2c214 100644 --- a/umap/tests/integration/test_picto.py +++ b/umap/tests/integration/test_picto.py @@ -5,7 +5,7 @@ import pytest from django.core.files.base import ContentFile from playwright.sync_api import expect -from umap.models import Map, Pictogram +from umap.models import Pictogram from ..base import DataLayerFactory @@ -37,12 +37,9 @@ def pictos(): Pictogram(name="circle", pictogram=ContentFile(path.read_text(), path.name)).save() -def test_can_change_picto_at_map_level(map, live_server, page, pictos): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - DataLayerFactory(map=map, data=DATALAYER_DATA) - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") +def test_can_change_picto_at_map_level(openmap, live_server, page, pictos): + DataLayerFactory(map=openmap, data=DATALAYER_DATA) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") marker = page.locator(".umap-div-icon img") expect(marker).to_have_count(1) # Should have default img @@ -71,13 +68,11 @@ def test_can_change_picto_at_map_level(map, live_server, page, pictos): expect(marker).to_have_attribute("src", "/static/umap/img/marker.svg") -def test_can_change_picto_at_datalayer_level(map, live_server, page, pictos): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.settings["properties"]["iconUrl"] = "/uploads/pictogram/star.svg" - map.save() - DataLayerFactory(map=map, data=DATALAYER_DATA) - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") +def test_can_change_picto_at_datalayer_level(openmap, live_server, page, pictos): + openmap.settings["properties"]["iconUrl"] = "/uploads/pictogram/star.svg" + openmap.save() + DataLayerFactory(map=openmap, data=DATALAYER_DATA) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") marker = page.locator(".umap-div-icon img") expect(marker).to_have_count(1) # Should have default img @@ -112,13 +107,11 @@ def test_can_change_picto_at_datalayer_level(map, live_server, page, pictos): expect(marker).to_have_attribute("src", "/uploads/pictogram/star.svg") -def test_can_change_picto_at_marker_level(map, live_server, page, pictos): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.settings["properties"]["iconUrl"] = "/uploads/pictogram/star.svg" - map.save() - DataLayerFactory(map=map, data=DATALAYER_DATA) - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") +def test_can_change_picto_at_marker_level(openmap, live_server, page, pictos): + openmap.settings["properties"]["iconUrl"] = "/uploads/pictogram/star.svg" + openmap.save() + DataLayerFactory(map=openmap, data=DATALAYER_DATA) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") marker = page.locator(".umap-div-icon img") expect(marker).to_have_count(1) # Should have default img @@ -152,12 +145,9 @@ def test_can_change_picto_at_marker_level(map, live_server, page, pictos): expect(marker).to_have_attribute("src", "/uploads/pictogram/star.svg") -def test_can_use_remote_url_as_picto(map, live_server, page, pictos): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - DataLayerFactory(map=map, data=DATALAYER_DATA) - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") +def test_can_use_remote_url_as_picto(openmap, live_server, page, pictos): + DataLayerFactory(map=openmap, data=DATALAYER_DATA) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") marker = page.locator(".umap-div-icon img") expect(marker).to_have_count(1) # Should have default img @@ -195,12 +185,9 @@ def test_can_use_remote_url_as_picto(map, live_server, page, pictos): expect(symbols).to_have_count(1) -def test_can_use_char_as_picto(map, live_server, page, pictos): - # Faster than doing a login - map.edit_status = Map.ANONYMOUS - map.save() - DataLayerFactory(map=map, data=DATALAYER_DATA) - page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") +def test_can_use_char_as_picto(openmap, live_server, page, pictos): + DataLayerFactory(map=openmap, data=DATALAYER_DATA) + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") marker = page.locator(".umap-div-icon span") # Should have default img, so not a span expect(marker).to_have_count(0) diff --git a/umap/tests/integration/test_tableeditor.py b/umap/tests/integration/test_tableeditor.py new file mode 100644 index 00000000..cb26e511 --- /dev/null +++ b/umap/tests/integration/test_tableeditor.py @@ -0,0 +1,27 @@ +import json +import re +from pathlib import Path + +from playwright.sync_api import expect + +from umap.models import DataLayer + + +def test_table_editor(live_server, openmap, datalayer, page): + page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit") + page.get_by_role("link", name="Manage layers").click() + page.locator("#umap-ui-container").get_by_title( + "Edit properties in a table" + ).click() + page.once("dialog", lambda dialog: dialog.accept(prompt_text="newprop")) + page.get_by_text("Add a new property").click() + page.locator('input[name="newprop"]').fill("newvalue") + page.once("dialog", lambda dialog: dialog.accept()) + page.hover(".umap-table-editor .tcell") + page.get_by_title("Delete this property on all").first.click() + with page.expect_response(re.compile(r".*/datalayer/update/.*")): + page.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"]["newprop"] == "newvalue" + assert "name" not in data["features"][0]["properties"]