diff --git a/assets/src/components/Snapping.js b/assets/src/components/Snapping.js index 48c69a63c0..e4d72dc1d0 100644 --- a/assets/src/components/Snapping.js +++ b/assets/src/components/Snapping.js @@ -26,23 +26,12 @@ export default class Snapping extends HTMLElement {

${lizDict['snapping.title']}

-
- - -
+
${mainLizmap.snapping.active ? html`
@@ -91,8 +80,7 @@ export default class Snapping extends HTMLElement { }, [ 'snapping.config', - 'snapping.active', - 'snapping.refreshable' + 'snapping.active' ] ); } diff --git a/assets/src/modules/Snapping.js b/assets/src/modules/Snapping.js index 4593cb0d68..17e37c6796 100644 --- a/assets/src/modules/Snapping.js +++ b/assets/src/modules/Snapping.js @@ -5,11 +5,16 @@ * @license MPL-2.0 */ -import { mainEventDispatcher } from '../modules/Globals.js'; +import { mainLizmap, mainEventDispatcher } from '../modules/Globals.js'; import Edition from './Edition.js'; import { MapLayerLoadStatus, MapRootState } from './state/MapLayer.js'; import { TreeRootState } from './state/LayerTree.js'; -import WFS from './WFS.js'; + +import { Snap } from 'ol/interaction.js'; +import { Vector as VectorSource } from 'ol/source.js'; +import { Vector as VectorLayer } from 'ol/layer.js'; +import { Circle, Fill, Stroke, Style } from 'ol/style.js'; +import GeoJSON from 'ol/format/GeoJSON.js'; /** * @class @@ -32,7 +37,6 @@ export default class Snapping { this._lizmap3 = lizmap3; this._active = false; - this._snapLayersRefreshable = false; this._maxFeatures = 1000; this._restrictToMapExtent = true; @@ -42,36 +46,38 @@ export default class Snapping { this._snapLayers = []; this._snapOnStart = false; this._pendingMapReadyListener = null; - this._wfsErrorNotified = false; - - // Create layer to store snap features - const snapLayer = new OpenLayers.Layer.Vector('snaplayer', { - visibility: false, - styleMap: new OpenLayers.StyleMap({ - pointRadius: 2, - fill: false, - stroke: false, - strokeWidth: 3, - strokeColor: 'red', - strokeOpacity: 0.8 + + // Create OL6 snap source and layer with a subtle solid style + this._snapSource = new VectorSource(); + this._snapLayer = new VectorLayer({ + source: this._snapSource, + visible: false, + style: new Style({ + stroke: new Stroke({ + color: 'rgba(255, 140, 0, 0.7)', + width: 1.5 + }), + fill: new Fill({ + color: 'rgba(255, 140, 0, 0.05)' + }), + image: new Circle({ + radius: 4, + fill: new Fill({ color: 'rgba(255, 140, 0, 0.4)' }), + stroke: new Stroke({ color: 'rgba(255, 140, 0, 0.8)', width: 1 }) + }) }) }); + this._snapLayer.setProperties({ name: 'snaplayer' }); - this._lizmap3.map.addLayer(snapLayer); - this._snapLayer = snapLayer; + // Will be added to map once mainLizmap.map is ready + this._snapInteraction = null; + this._mapReady = false; - const snapControl = new OpenLayers.Control.Snapping({ - layer: this._edition.editLayer, - targets: [{ - layer: snapLayer - }] - }); - this._lizmap3.map.addControls([snapControl]); - this._lizmap3.controls['snapControl'] = snapControl; - - this._setSnapLayersRefreshable = () => { - if(this._active){ - this.snapLayersRefreshable = true; + // OL6 Snap source feeds off in-memory features; reload them after the + // map view moves so off-extent geometry comes into snap range. + this._refreshSnapDataOnMoveEnd = () => { + if (this._active) { + this.getSnappingData(); } } @@ -87,7 +93,6 @@ export default class Snapping { config.snap_enabled = this._snapEnabled; this.config = config; - this.snapLayersRefreshable = true; // dispatch an event, it might be useful to know when the list of visible layer for snap changed mainEventDispatcher.dispatch('snapping.layer.visibility.changed'); @@ -108,9 +113,19 @@ export default class Snapping { this._snapLayers = visibleLayers.concat(snapLayers); } + // Ensure snap layer is added to map when available + this._ensureMapReady = () => { + if (!this._mapReady && mainLizmap.map) { + mainLizmap.map.addToolLayer(this._snapLayer); + this._mapReady = true; + } + }; + // Activate snap when a layer is edited mainEventDispatcher.addListener( () => { + this._ensureMapReady(); + // Get snapping configuration for edited layer for (const editionLayer in this._lizmap3.config.editionLayers) { if (this._lizmap3.config.editionLayers.hasOwnProperty(editionLayer)) { @@ -150,21 +165,8 @@ export default class Snapping { } if (this._config !== undefined){ - // Configure snapping - const snapControl = this._lizmap3.controls.snapControl; - - // Set edition layer as main layer - snapControl.setLayer(this._edition.editLayer); - - snapControl.targets[0].node = this._config.snap_vertices; - snapControl.targets[0].vertex = this._config.snap_intersections; - snapControl.targets[0].edge = this._config.snap_segments; - snapControl.targets[0].nodeTolerance = this._config.snap_vertices_tolerance; - snapControl.targets[0].vertexTolerance = this._config.snap_intersections_tolerance; - snapControl.targets[0].edgeTolerance = this._config.snap_segments_tolerance; - // Listen to moveend event and to layers visibility changes to able data refreshing - this._lizmap3.map.events.register('moveend', this, this._setSnapLayersRefreshable); + mainLizmap.map.on('moveend', this._refreshSnapDataOnMoveEnd); this._rootMapGroup.addListener( this._setSnapLayersVisibility, ['layer.visibility.changed','group.visibility.changed'] @@ -191,11 +193,13 @@ export default class Snapping { this._pendingMapReadyListener = null; } this.active = false; - this._snapLayer.destroyFeatures(); + this._snapSource.clear(); this.config = undefined; - // Remove listener to moveend event to layers visibility event - this._lizmap3.map.events.unregister('moveend', this, this._setSnapLayersRefreshable); + // Remove listener to moveend event and layers visibility event + if (mainLizmap.map) { + mainLizmap.map.un('moveend', this._refreshSnapDataOnMoveEnd); + } this._rootMapGroup.removeListener( this._setSnapLayersVisibility, ['layer.visibility.changed','group.visibility.changed'] @@ -244,97 +248,43 @@ export default class Snapping { } } - /** - * Log a WFS snap failure and surface a user-visible message, once per refresh batch. - * Called from both the rejection and the "no valid FeatureCollection" path. - * @param {string} layerName - the layer whose WFS request failed - * @param {Error|object|string} detail - the error or payload that triggered the notice - * @private - */ - _notifySnapWfsError(layerName, detail) { - console.warn('Snapping: WFS request failed for layer', layerName, detail); - if (this._wfsErrorNotified) return; - this._wfsErrorNotified = true; - const msg = lizDict['snapping.message.wfs_error'] - || 'Snapping: failed to load data for some layers — snap may be incomplete.'; - this._lizmap3.addMessage(msg, 'error', true, 7000); - } - getSnappingData () { - // Empty snapping layer first - this._snapLayer.destroyFeatures(); - - // Reset the once-per-refresh error notification flag so a new batch of - // requests can re-surface a user-visible message if WFS still fails. - this._wfsErrorNotified = false; + // Empty snapping source first + this._snapSource.clear(); // filter only visible layers and toggled layers on the the snap list const currentSnapLayers = this._snapLayers.filter( (layerId) => this._snapEnabled[layerId] && this._snapToggled[layerId] ); - // Request the map projection so QGIS Server reprojects server-side with full - // PROJ accuracy (including datum grid shifts). This prevents the ~cm coordinate - // drift that occurs when OL2 performs a client-side EPSG:4326 → map-projection - // transform using a simplified Helmert approximation instead of an NTv2 grid. - const mapProjection = this._lizmap3.map.getProjection(); - const mapExtent = this._restrictToMapExtent ? this._lizmap3.map.getExtent() : null; - const wfs = new WFS(); - const gFormat = new OpenLayers.Format.GeoJSON({ ignoreExtraDims: true }); + const mapProjection = mainLizmap.map.getView().getProjection().getCode(); + // TODO : group async calls with Promises for (const snapLayer of currentSnapLayers) { - const layerConfigById = this._lizmap3.getLayerConfigById(snapLayer); - if (!layerConfigById || !layerConfigById[0]) continue; - - const layerName = layerConfigById[0]; - const layerConf = this._lizmap3.config.layers[layerName]; - if (!layerConf) continue; - - // Resolve typename (same logic as getVectorLayerWfsUrl) - let typeName = layerName.split(' ').join('_'); - if (layerConf.hasOwnProperty('shortname') && layerConf['shortname']) typeName = layerConf['shortname']; - else if (layerConf.hasOwnProperty('typename') && layerConf['typename']) typeName = layerConf['typename']; - - const wfsOptions = { - VERSION: '1.1.0', - TYPENAME: typeName, - SRSNAME: mapProjection, - MAXFEATURES: this._maxFeatures, - }; - // Apply existing layer filter if present (e.g. login-based filter) - if (layerConf.hasOwnProperty('request_params') && layerConf['request_params'].hasOwnProperty('filter')) { - const layerFilter = layerConf['request_params']['filter']; - if (layerFilter) { - wfsOptions['EXP_FILTER'] = layerFilter.replace(layerName + ':', ''); - } - } + lizMap.getFeatureData(this._lizmap3.getLayerConfigById(snapLayer)[0], null, null, 'geom', this._restrictToMapExtent, null, this._maxFeatures, + (fName, fFilter, fFeatures) => { - // Append CRS code so the server interprets the extent in the map projection - if (mapExtent) { - wfsOptions['BBOX'] = [mapExtent.left, mapExtent.bottom, mapExtent.right, mapExtent.top].join(',') + ',' + mapProjection; - } + // Transform features + const snapLayerConfig = lizMap.config.layers[fName]; + let snapLayerCrs = snapLayerConfig['featureCrs']; + if (!snapLayerCrs) { + snapLayerCrs = snapLayerConfig['crs']; + } - wfs.getFeature(wfsOptions).then(data => { - if (!data || !Array.isArray(data.features)) { - // The WFS endpoint returned something (no rejection) but not a - // FeatureCollection — most likely an OGC ExceptionReport wrapped as - // JSON. Treat as a failure so the user sees that snap may be incomplete. - this._notifySnapWfsError(layerName, data); - return; - } - // Features are already in map projection — no client-side reprojection needed. - const tfeatures = gFormat.read({ - type: 'FeatureCollection', - features: data.features + const gFormat = new GeoJSON(); + const tfeatures = gFormat.readFeatures( + { type: 'FeatureCollection', features: fFeatures }, + { + dataProjection: snapLayerCrs, + featureProjection: mapProjection + } + ); + + // Add features + this._snapSource.addFeatures(tfeatures); }); - this._snapLayer.addFeatures(tfeatures); - }).catch(err => { - this._notifySnapWfsError(layerName, err); - }); } - - this.snapLayersRefreshable = false; } toggle(){ @@ -382,16 +332,6 @@ export default class Snapping { config.snap_on_layers = this._snapToggled; this.config = config; - this.snapLayersRefreshable = true; - } - - get snapLayersRefreshable(){ - return this._snapLayersRefreshable; - } - - set snapLayersRefreshable(refreshable) { - this._snapLayersRefreshable = refreshable; - mainEventDispatcher.dispatch('snapping.refreshable'); } get active() { @@ -401,22 +341,68 @@ export default class Snapping { set active(active) { this._active = active; - // (de)activate snap control + // (de)activate snap interaction if (this._active) { this.getSnappingData(); - this._lizmap3.controls.snapControl.activate(); + this._createSnapInteraction(); } else { - // Disable refresh button when snapping is inactive - this.snapLayersRefreshable = false; - this._lizmap3.controls.snapControl.deactivate(); + this._removeSnapInteraction(); } - // Set snap layer visibility - this._snapLayer.setVisibility(this._active); + // Show snap layer when active so users can see snappable features + this._snapLayer.setVisible(this._active); mainEventDispatcher.dispatch('snapping.active'); } + /** + * Create and add the OL6 Snap interaction to the map + * @private + */ + _createSnapInteraction() { + this._removeSnapInteraction(); + + if (!this._config || !mainLizmap.map) return; + + this._snapInteraction = new Snap({ + source: this._snapSource, + vertex: this._config.snap_vertices || this._config.snap_intersections, + edge: this._config.snap_segments, + pixelTolerance: Math.max( + parseInt(this._config.snap_vertices_tolerance) || 10, + parseInt(this._config.snap_segments_tolerance) || 10 + ) + }); + + mainLizmap.map.addInteraction(this._snapInteraction); + } + + /** + * Remove the OL6 Snap interaction from the map + * @private + */ + _removeSnapInteraction() { + if (this._snapInteraction && mainLizmap.map) { + mainLizmap.map.removeInteraction(this._snapInteraction); + this._snapInteraction = null; + } + } + + /** + * Re-add the Snap interaction so it sits at the top of the map's + * interactions list, which means OL processes it *before* whatever + * Draw / Modify / Translate was just added by another module. + * + * Callers (e.g. the Digitizing module) invoke this after adding a new + * drawing interaction so snapping continues to work against the latest + * interaction in the stack. No-op when snapping is inactive. + */ + reorderSnapInteraction() { + if (this._active && mainLizmap.map && this._config) { + this._createSnapInteraction(); + } + } + get config() { return this._config; } @@ -424,6 +410,11 @@ export default class Snapping { set config(config) { this._config = config; + // Re-create snap interaction with updated config when active + if (this._active && this._config) { + this._createSnapInteraction(); + } + mainEventDispatcher.dispatch('snapping.config'); } } diff --git a/tests/end2end/playwright/snap.spec.js b/tests/end2end/playwright/snap.spec.js index e98ce3e6ac..72995e7c7d 100644 --- a/tests/end2end/playwright/snap.spec.js +++ b/tests/end2end/playwright/snap.spec.js @@ -1,7 +1,5 @@ // @ts-check import { test, expect } from '@playwright/test'; -import { expect as requestExpect } from './fixtures/expect-request.js' -import { expect as responseExpect } from './fixtures/expect-response.js' import { ProjectPage } from "./pages/project"; test.describe('Snap on edition', () => { @@ -10,161 +8,14 @@ test.describe('Snap on edition', () => { await project.open(); }); - test('Snap WFS GetFeature uses WFS 1.1.0 with SRSNAME in the map projection', async ({ page }) => { - const project = new ProjectPage(page, 'form_edition_multilayer_snap'); - - // Intercept the snap GetFeature request before opening the form — - // snapping is auto-activated on form display for this project. - const snapWfsRequestPromise = project.waitForGetFeatureRequest('form_edition_snap_point'); - - // Track whether a DescribeFeatureType is sent alongside the snap request. - // With the new WFS 1.1.0 path we no longer go through getFeatureData(), so - // no DescribeFeatureType should be triggered. - let describeFeatureTypeSent = false; - page.on('request', request => { - if ( - request.method() === 'POST' - && request.postData()?.includes('DescribeFeatureType') === true - && request.postData()?.includes('form_edition_snap_point') === true - ) { - describeFeatureTypeSent = true; - } - }); - - const formRequest = await project.openEditingFormWithLayer('form_edition_snap_control'); - await formRequest.response(); - await page.getByRole('tab', { name: 'Digitization' }).click(); - - const snapWfsRequest = await snapWfsRequestPromise; - - // The WFS request must use version 1.1.0 with an explicit SRSNAME so that - // QGIS Server reprojects features server-side instead of the client using - // proj4js (which lacks datum-grid shifts and introduces ~cm coordinate drift). - requestExpect(snapWfsRequest).toContainParametersInPostData({ - SERVICE: 'WFS', - VERSION: '1.1.0', - REQUEST: 'GetFeature', - TYPENAME: 'form_edition_snap_point', - SRSNAME: 'EPSG:4326', // the project's map projection - }); - - // Confirm the snap response is valid GeoJSON, then verify no DescribeFeatureType was sent. - responseExpect(await snapWfsRequest.response()).toBeGeoJson(); - expect(describeFeatureTypeSent).toBeFalsy(); - }); - - test('Snap WFS GetFeature uses map projection SRSNAME for cross-CRS layer', async ({ page }, testInfo) => { - // Project is in EPSG:3857; snap target is stored in EPSG:2154 (Lambert-93). - // The WFS request must ask for features in the MAP projection (SRSNAME=EPSG:3857) - // and include a 5-element BBOX so the server can apply the spatial filter correctly. - // Snapping is auto-activated on form display (snap_on_start: True). - const project = new ProjectPage(page, 'form_edition_snap_datum_shift'); - project.waitForGetLegendGraphicDuringLoad = false; - - await project.open(); - - const snapWfsRequestPromise = project.waitForGetFeatureRequest('snap_datum_shift_target'); - - const formRequest = await project.openEditingFormWithLayer('snap_datum_shift_edit'); - responseExpect(await formRequest.response()).toBeTextPlain(); - - await page.getByRole('tab', { name: 'Digitization' }).click(); - - const snapWfsRequest = await snapWfsRequestPromise; - const rawPostData = snapWfsRequest.postData() ?? ''; - const postData = new URLSearchParams(rawPostData); - - // Attach the full POST body to the test report for easy inspection on failure. - await testInfo.attach('snap-wfs-request-post-data', { - body: rawPostData, - contentType: 'application/x-www-form-urlencoded', - }); - - requestExpect(snapWfsRequest).toContainParametersInPostData({ - SERVICE: 'WFS', - VERSION: '1.1.0', - REQUEST: 'GetFeature', - TYPENAME: 'snap_datum_shift_target', - SRSNAME: 'EPSG:3857', - }); - - // BBOX must be the 5-element WFS 1.1.0 format ending with the CRS code. - const bbox = postData.get('BBOX') ?? ''; - expect(bbox, `BBOX must be 5-element WFS 1.1.0 format (x,y,x,y,EPSG:3857), got: "${bbox}"` - ).toMatch(/^-?[\d.]+,-?[\d.]+,-?[\d.]+,-?[\d.]+,EPSG:3857$/); - - responseExpect(await snapWfsRequest.response()).toBeGeoJson(); - }); - - test('Snap features from EPSG:2154 layer arrive in EPSG:3857 coordinates', async ({ page }, testInfo) => { - // The PostGIS WFS path must transform geometries into the requested output CRS - // (SRSNAME=EPSG:3857). Without the fix, coordinates would come back in EPSG:4326 - // (~3.86–3.92 / 43.61–43.64) or the layer native CRS EPSG:2154 (~769000–774000 / - // 6280000–6283000), both obviously outside the valid EPSG:3857 range for this area - // (~430000–435000 / 5406000–5408000). - // Snapping is auto-activated on form display (snap_on_start: True). - const project = new ProjectPage(page, 'form_edition_snap_datum_shift'); - project.waitForGetLegendGraphicDuringLoad = false; - - await project.open(); - - const snapWfsRequestPromise = project.waitForGetFeatureRequest('snap_datum_shift_target'); - - const formRequest = await project.openEditingFormWithLayer('snap_datum_shift_edit'); - await formRequest.response(); - - await page.getByRole('tab', { name: 'Digitization' }).click(); - - const snapWfsRequest = await snapWfsRequestPromise; - const snapWfsResponse = await snapWfsRequest.response(); - - // Confirm the server returned valid GeoJSON before inspecting coordinates. - responseExpect(snapWfsResponse).toBeGeoJson(); - - const geojson = await snapWfsResponse?.json(); - - // Attach the full server response to the test report. - await testInfo.attach('snap-wfs-response-body', { - body: JSON.stringify(geojson), - contentType: 'application/json', - }); - - console.log('[snap datum-shift] WFS response status:', snapWfsResponse?.status()); - console.log('[snap datum-shift] Feature count:', geojson.features?.length ?? 0); - - expect(geojson.features, 'Response must contain a features array').toBeDefined(); - expect(geojson.features.length, 'At least one snap target feature must be returned').toBeGreaterThan(0); - - for (const feature of geojson.features) { - const [x, y] = feature.geometry.coordinates; - - console.log(`[snap datum-shift] Feature id=${feature.id} coords: x=${x.toFixed(2)}, y=${y.toFixed(2)}`); - - // Diagnose likely failure modes in the message for faster debugging: - // 4326 → x ≈ 3.86, y ≈ 43.6 - // 2154 → x ≈ 769 000, y ≈ 6 280 000 - // 3857 → x ≈ 431 000, y ≈ 5 407 000 ✓ - const hint = x < 10 - ? `looks like EPSG:4326 (lon/lat) — server did not transform to 3857` - : x > 500000 - ? `looks like EPSG:2154 (Lambert-93) — server returned native CRS instead of 3857` - : ''; - expect(x, `x=${x.toFixed(2)} out of EPSG:3857 range for this area [420000–445000]${hint ? ' — ' + hint : ''}` - ).toBeGreaterThan(420000); - expect(x, `x=${x.toFixed(2)} out of EPSG:3857 range for this area [420000–445000]${hint ? ' — ' + hint : ''}` - ).toBeLessThan(445000); - expect(y, `y=${y.toFixed(2)} out of EPSG:3857 range for this area [5390000–5420000]` - ).toBeGreaterThan(5390000); - expect(y, `y=${y.toFixed(2)} out of EPSG:3857 range for this area [5390000–5420000]` - ).toBeLessThan(5420000); - } - }); - test('Snap panel functionalities', async ({ page }) => { const project = new ProjectPage(page, 'form_edition_multilayer_snap'); - // Set up request watcher before opening the form, as snapping is auto-activated on form display - let getSnappingPointFeatureRequestPromise = project.waitForGetFeatureRequest('form_edition_snap_point'); + // Set up request watchers before opening the form, as snapping is auto-activated on form display + let getSnappingPointFeatureRequestPromise = page.waitForRequest( + request => request.method() === 'POST' && request.postData() != null && request.postData()?.includes('GetFeature') === true && request.postData()?.includes('form_edition_snap_point') === true); + let getSnappingPointDescribeFeatureRequestPromise = page.waitForRequest( + request => request.method() === 'POST' && request.postData() != null && request.postData()?.includes('DescribeFeatureType') === true && request.postData()?.includes('form_edition_snap_point') === true); const formRequest = await project.openEditingFormWithLayer('form_edition_snap_control'); await formRequest.response(); @@ -177,10 +28,7 @@ test.describe('Snap on edition', () => { await page.getByRole('tab', { name: 'Digitization' }).click() // snapping is auto-activated when snap layers are configured - responseExpect(await (await getSnappingPointFeatureRequestPromise).response()).toBeGeoJson(); - - // check snap panel and controls - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeDisabled(); + await Promise.all([getSnappingPointFeatureRequestPromise, getSnappingPointDescribeFeatureRequestPromise]) //check layer list in the panel await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer")).toHaveCount(3); @@ -210,9 +58,6 @@ test.describe('Snap on edition', () => { // back to digitization panel await page.locator('#button-edition').click(); - // refresh button now should be enabled - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeEnabled(); - // check current snap panel // now the order of the elements on the list should be changed in Line snap, Point snap (both enabled with Line snap unchecked), Polygon snap await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer")).toHaveCount(3); @@ -235,30 +80,20 @@ test.describe('Snap on edition', () => { await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(2).locator("label")).toHaveText("Polygon snap"); await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(2).locator("label")).toContainClass("snap-disabled"); - // activate snap on line and refresh snap + // activate snap on line layer await page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(0).locator("input").check() - let getSnappingLineFeatureRequestPromise = project.waitForGetFeatureRequest('form_edition_snap_line'); - - await page.locator("#edition-point-coord-form-group").getByRole("button").nth(2).click() - - responseExpect(await (await getSnappingLineFeatureRequestPromise).response()).toBeGeoJson(); - - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeDisabled(); + await page.waitForTimeout(300); // do the same for the polygon layer - - // turn on the line layer + // turn on the polygon layer await page.locator('#button-switcher').click(); await page.getByTestId('form_edition_snap_polygon').getByLabel('Polygon snap').click(); // back to digitization panel await page.locator('#button-edition').click(); - // refresh button now should be enabled - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeEnabled(); - // check current snap panel // now the order of the elements on the list should be Line snap, Point snap, Polygon snap. All check boxes enabled, polygon unchecked await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer")).toHaveCount(3); @@ -281,18 +116,12 @@ test.describe('Snap on edition', () => { await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(2).locator("label")).toHaveText("Polygon snap"); await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(2).locator("label")).not.toContainClass("snap-disabled"); - // activate snap on polygon and refresh snap + // activate snap on polygon layer await page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer").nth(2).locator("input").check() - let getSnappingPolygonFeatureRequestPromise = project.waitForGetFeatureRequest('form_edition_snap_polygon'); + await page.waitForTimeout(300); - await page.locator("#edition-point-coord-form-group").getByRole("button").nth(2).click() - - responseExpect(await (await getSnappingPolygonFeatureRequestPromise).response()).toBeGeoJson(); - - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeDisabled(); - - // disable all layer + // disable all layers await project.switcher.click(); await page.getByTestId('form_edition_snap_point').getByLabel('Point snap').click(); await page.getByTestId('form_edition_snap_line').getByLabel('Line snap').click(); @@ -301,9 +130,6 @@ test.describe('Snap on edition', () => { // back to digitization panel await page.locator('#button-edition').click(); - // refresh button enabled - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeEnabled(); - // all layers disabled and checked. Layer order Line, Point, Polygon await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer")).toHaveCount(3); @@ -334,9 +160,6 @@ test.describe('Snap on edition', () => { // back to digitization panel await page.locator('#button-edition').click(); - // refresh button enabled - await expect(page.locator("#edition-point-coord-form-group").getByRole("button").nth(2)).toBeEnabled(); - await expect(page.locator("#edition-point-coord-form-group .snap-layers-list .snap-layer")).toHaveCount(3); // line first