diff --git a/.env b/.env index 2fd995d3..5074dd0f 100644 --- a/.env +++ b/.env @@ -35,3 +35,4 @@ VITE_MODE_LIB=true # MyMaps / Draw VITE_MYMAPS_URL="/mymaps" VITE_ARROW_MODEL_URL="/static-ngeo/models/arrow5.glb" +VITE_ELEVATION_URL="/raster" diff --git a/.env.development b/.env.development index ec143a9c..d9c0ae14 100644 --- a/.env.development +++ b/.env.development @@ -34,4 +34,5 @@ VITE_MODE_LIB=false # MyMaps / Draw VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps" -VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb" \ No newline at end of file +VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb" +VITE_ELEVATION_URL="https://migration.geoportail.lu/raster" \ No newline at end of file diff --git a/.env.e2e b/.env.e2e index 59583476..3d4d87ac 100644 --- a/.env.e2e +++ b/.env.e2e @@ -35,3 +35,4 @@ VITE_MODE_LIB=false # MyMaps / Draw VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps" VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb" +VITE_ELEVATION_URL="https://migration.geoportail.lu/raster" diff --git a/.env.staging b/.env.staging index 619afc3b..cb0d2ca3 100644 --- a/.env.staging +++ b/.env.staging @@ -34,4 +34,5 @@ VITE_MODE_LIB=true # MyMaps / Draw VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps" -VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb" \ No newline at end of file +VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb" +VITE_ELEVATION_URL="/raster" \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 3e43c577..d91cadeb 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,6 +16,7 @@ module.exports = { extends: ['plugin:cypress/recommended'], }, ], + plugins: ['no-only-tests'], rules: { 'no-prototype-builtins': 'off', '@typescript-eslint/ban-types': 'off', @@ -28,6 +29,7 @@ module.exports = { '@typescript-eslint/no-unused-vars': ['error'], 'import/no-unresolved': 'off', 'no-console': 'error', + 'no-only-tests/no-only-tests': 'error', }, parserOptions: { ecmaVersion: 'latest', diff --git a/README.md b/README.md index e0b2642a..9045d200 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ A `.prettierignore` file is used by prettier and lint to exclude some files (suc ### End to end testing with Cypress -For e2e tests, the code is instrumented with the Istanbul library to obtain resulting code coverage. The dev test server is launched with the env var `INSRUMENT_COVERAGE=true` +For e2e tests, the code is instrumented with the Istanbul library to obtain resulting code coverage. The dev test server is launched with the env var `INSTRUMENT_COVERAGE=true` - `test:e2e` will launch e2e tests and opens Cypress UI. You need to build the app before using this command with `npm run build-only -- --mode=e2e`. This will test the app built for production (with css minification, images src optimization using base64, ...) - `test:e2e:ci` This will run tests in command line only on a dev build (no need to build the app). Used by ci workflow. diff --git a/cypress/e2e/draw/draw-feat-line.cy.ts b/cypress/e2e/draw/draw-feat-line.cy.ts index 00558d4d..14434710 100644 --- a/cypress/e2e/draw/draw-feat-line.cy.ts +++ b/cypress/e2e/draw/draw-feat-line.cy.ts @@ -27,6 +27,12 @@ describe('Draw "Line"', () => { testFeatItemMeasurements() }) + it('updates length measurement when editing geometry', () => { + cy.get('*[data-cy="featItemLength"]').should('contain.text', '55.4 km') + cy.dragVertexOnMap(200, 200, 300, 300) + cy.get('*[data-cy="featItemLength"]').should('contain.text', '111 km') + }) + it('displays the possible actions for the feature', () => { testFeatItem() }) diff --git a/cypress/e2e/draw/draw-feat-point.cy.ts b/cypress/e2e/draw/draw-feat-point.cy.ts index ec6dac22..92525e23 100644 --- a/cypress/e2e/draw/draw-feat-point.cy.ts +++ b/cypress/e2e/draw/draw-feat-point.cy.ts @@ -21,6 +21,32 @@ function testFeatStyleEditionTabContent() { describe('Draw "Point"', () => { beforeEach(() => { + // mocks for 100x100, 200x200, 300x300 clicks + cy.intercept( + 'GET', + '/raster?lon=-25877.619036593664&lat=154433.4715351454', + { + statusCode: 200, + body: { + dhm: null, + }, + } + ) + cy.intercept( + 'GET', + '/raster?lon=12756.103097272688&lat=114635.74032468312', + { + statusCode: 200, + body: { + dhm: 333.13, + }, + } + ) + cy.intercept('GET', '/raster?lon=51966.98676810359&lat=74839.09999860045', { + statusCode: 500, + body: {}, + }) + cy.visit('/') cy.get('button[data-cy="drawButton"]').click() cy.get('button[data-cy="drawPointButton"]').click() @@ -36,6 +62,27 @@ describe('Draw "Point"', () => { testFeatItemMeasurements() }) + it('displays N/A elevation for Point if data is null', () => { + cy.get('*[data-cy="featItemElevation"]').should('contain.text', 'N/A') + }) + + it('displays elevation for new Point', () => { + cy.get('button[data-cy="drawPointButton"]').click() + cy.get('button[data-cy="drawPointButton"]').click() + cy.get('div.ol-viewport').click(200, 200) + cy.get('*[data-cy="featItemElevation"]').should( + 'contain.text', + '333.13 m' + ) + }) + + it('displays N/A elevation for new Point if response has error', () => { + cy.get('button[data-cy="drawPointButton"]').click() + cy.get('button[data-cy="drawPointButton"]').click() + cy.get('div.ol-viewport').click(300, 300) + cy.get('*[data-cy="featItemElevation"]').should('contain.text', 'N/A') + }) + it('displays the possible actions for the feature', () => { testFeatItem() }) diff --git a/cypress/e2e/draw/draw-feat-polygon.cy.ts b/cypress/e2e/draw/draw-feat-polygon.cy.ts index 24194748..ee8ae345 100644 --- a/cypress/e2e/draw/draw-feat-polygon.cy.ts +++ b/cypress/e2e/draw/draw-feat-polygon.cy.ts @@ -27,6 +27,14 @@ describe('Draw "Polygon"', () => { testFeatItemMeasurements() }) + it('updates length and area measurements when editing geometry', () => { + cy.get('*[data-cy="featItemLength"]').should('contain.text', '134 km') + cy.get('*[data-cy="featItemArea"]').should('contain.text', '766 km²') + cy.dragVertexOnMap(200, 200, 300, 300) + cy.get('*[data-cy="featItemLength"]').should('contain.text', '238 km') + cy.get('*[data-cy="featItemArea"]').should('contain.text', '1530 km²') + }) + it('displays the possible actions for the feature', () => { testFeatItem() }) diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 8cfa2d1d..8723a30a 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,6 +1,7 @@ /// import type { Interaction } from 'ol/interaction' +import 'cypress-real-events/support' // *********************************************** // This example commands.ts shows you how to @@ -61,11 +62,21 @@ Cypress.Commands.add('getModifyInteraction', () => { }) }) +Cypress.Commands.add('dragVertexOnMap', (originX, originY, x, y) => { + cy.get('div.ol-viewport').realMouseDown({ + x: originX, + y: originY, + }) + cy.get('div.ol-viewport').realMouseMove(x, y) + cy.get('div.ol-viewport').realMouseUp() +}) + declare global { namespace Cypress { interface Chainable { getDrawInteractions(): Chainable> getModifyInteraction(): Chainable + dragVertexOnMap(): Chainable // login(email: string, password: string): Chainable // drag(subject: string, options?: Partial): Chainable // dismiss(subject: string, options?: Partial): Chainable diff --git a/package-lock.json b/package-lock.json index c107b648..e4eeef61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,8 +55,10 @@ "@vue/tsconfig": "^0.1.3", "core-js": "^3.37.1", "cypress": "^13.7.2", + "cypress-real-events": "^1.13.0", "eslint": "^8.22.0", "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-vue": "^9.3.0", "husky": "^8.0.2", "i18next": "^22.0.6", @@ -5665,6 +5667,15 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-real-events": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.13.0.tgz", + "integrity": "sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==", + "dev": true, + "peerDependencies": { + "cypress": "^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x" + } + }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -7115,6 +7126,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-plugin-no-only-tests": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz", + "integrity": "sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==", + "dev": true, + "engines": { + "node": ">=5.0.0" + } + }, "node_modules/eslint-plugin-prettier": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", diff --git a/package.json b/package.json index 8b7f13de..2fd96403 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "test": "npm run test:unit", "test:unit": "vitest --environment jsdom --root .", "test:unit:ci": "vitest run --environment jsdom --coverage", - "test:e2e": "INSRUMENT_COVERAGE=true start-server-and-test preview :4173 'cypress open --e2e'", - "test:e2e:ci": "INSRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --e2e'", - "test:e2e:dev": "INSRUMENT_COVERAGE=true start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'", + "test:e2e": "INSTRUMENT_COVERAGE=true start-server-and-test preview :4173 'cypress open --e2e'", + "test:e2e:ci": "INSTRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --e2e'", + "test:e2e:dev": "INSTRUMENT_COVERAGE=true start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'", "coverage-report": "nyc report", "type-check:dev": "npm run type-check -- --watch", "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", @@ -78,8 +78,10 @@ "@vue/tsconfig": "^0.1.3", "core-js": "^3.37.1", "cypress": "^13.7.2", + "cypress-real-events": "^1.13.0", "eslint": "^8.22.0", "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-vue": "^9.3.0", "husky": "^8.0.2", "i18next": "^22.0.6", diff --git a/src/bundle/lib.ts b/src/bundle/lib.ts index eb6479a4..3f9819f4 100644 --- a/src/bundle/lib.ts +++ b/src/bundle/lib.ts @@ -49,6 +49,7 @@ import { clearLayersCache } from '@/stores/layers.cache' import i18next, { InitOptions } from 'i18next' import backend from 'i18next-http-backend' import I18NextVue from 'i18next-vue' +import formatDistanceDirective from './directives/format-distance.directive' import App from '../App.vue' @@ -78,6 +79,7 @@ export default function useLuxLib(options: LuxLibOptions) { app.use(createPinia()) app.use(I18NextVue, { i18next }) app.use(VueDOMPurifyHTML) + app.use(formatDistanceDirective) const createElementInstance = (component = {}, app = null) => { return defineCustomElement( diff --git a/src/components/draw/feature-item.vue b/src/components/draw/feature-item.vue index 82936326..ccd9004b 100644 --- a/src/components/draw/feature-item.vue +++ b/src/components/draw/feature-item.vue @@ -84,7 +84,6 @@ function onSubmitEditInfo() { @@ -95,7 +94,6 @@ function onSubmitEditInfo() { { + const lonlat = transform(coordinate, MAP_CRS, ELEVATION_CRS) + try { + const response = await fetch( + `${ELEVATION_URL}?lon=${lonlat[0]}&lat=${lonlat[1]}` + ) + if (!response.ok) { + throw new Error('Network response was not ok') + } + const data = await response.json() + return data.dhm + } catch (error) { + return null //return null on error, like API when no data + } +} + +export const getDebouncedElevation = debounceAsync(getElevation, 300) diff --git a/src/components/draw/feature-measurements.vue b/src/components/draw/feature-measurements.vue index 6d84ea0f..d01b7999 100644 --- a/src/components/draw/feature-measurements.vue +++ b/src/components/draw/feature-measurements.vue @@ -1,22 +1,62 @@