From bcef93c25c965768fb9166a2b35307e364e503fd Mon Sep 17 00:00:00 2001 From: rldhont Date: Tue, 26 Aug 2025 08:53:55 +0200 Subject: [PATCH 1/3] GH Action - E2E QGIS - BLEEDING_EDGE with QGIS 3.44 Update BLEEDING_EDGE E2E QGIS GH Action to QGIS 3.44 the last QGIS version. --- .github/workflows/e2e_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index 107da9d829..51c44b7491 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -32,7 +32,7 @@ jobs: - name: "BLEEDING_EDGE" php: "8.4" pg-postgis: "17-3" - qgis-server: "3.42" + qgis-server: "3.44" update-projects: "TRUE" env: CYPRESS_CI: TRUE From f5756d97a10e7005c149e25364b81e86c82c508f Mon Sep 17 00:00:00 2001 From: rldhont Date: Mon, 6 Oct 2025 15:40:29 +0200 Subject: [PATCH 2/3] Fix QGIS Project 3.44 parsing for Group shortname In QGIS 3.44, the QGIS Server Group metadta has change and the way is wrote in QGIS project to. --- .../lib/Project/Qgis/LayerTreeGroup.php | 18 +++++++++ .../Project/Qgis/LayerTreeGroupTest.php | 40 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php index 0d957d92f8..f2eae24bcd 100644 --- a/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php +++ b/lizmap/modules/lizmap/lib/Project/Qgis/LayerTreeGroup.php @@ -20,6 +20,9 @@ * @property bool $mutuallyExclusive * @property array $customproperties * @property array $items + * @property null|string $shortname + * @property null|string $title + * @property null|string $abstract */ class LayerTreeGroup extends BaseQgisXmlObject { @@ -29,6 +32,9 @@ class LayerTreeGroup extends BaseQgisXmlObject 'mutuallyExclusive', 'customproperties', 'items', + 'shortname', + 'title', + 'abstract', ); /** @var array The default values for properties */ @@ -40,6 +46,13 @@ class LayerTreeGroup extends BaseQgisXmlObject /** @var string The XML element local name */ protected static $qgisLocalName = 'layer-tree-group'; + /** @var array The XML element parsed children */ + protected static $children = array( + 'shortname', + 'title', + 'abstract', + ); + protected static $childParsers = array(); /** @var array The XML element tagname associated with a collector property name */ @@ -76,6 +89,11 @@ public function getGroupShortNames() continue; } $data += $item->getGroupShortNames(); + if (isset($item->shortname) && $item->shortname != null) { + $data[$item->name] = $item->shortname; + + continue; + } if (!array_key_exists('wmsShortName', $item->customproperties)) { continue; } diff --git a/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php b/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php index d866d152e0..812063e7d2 100644 --- a/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php +++ b/tests/units/classes/Project/Qgis/LayerTreeGroupTest.php @@ -58,6 +58,46 @@ public function testConstruct(): void $this->assertEquals('publicbuildings_tramstop', $treeGroup->items[1]->name); } + public function testShortName(): void + { + + $xmlStr = ' + + + + + + + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $treeGroup = Qgis\LayerTreeGroup::fromXmlReader($oXml); + $this->assertEquals(null, $treeGroup->shortname); + $this->assertEquals('Overview', $treeGroup->customproperties['wmsShortName']); + + $xmlStr = ' + + + + Overview + + + + + + '; + $oXml = App\XmlTools::xmlReaderFromString($xmlStr); + $treeGroup = Qgis\LayerTreeGroup::fromXmlReader($oXml); + $this->assertEquals('Overview', $treeGroup->shortname); + } + public function testEmpty(): void { $xmlStr = ' From 3c3c65ef70b39a9870d18e41c6dd8601e9598f1b Mon Sep 17 00:00:00 2001 From: rldhont Date: Mon, 6 Oct 2025 23:28:27 +0200 Subject: [PATCH 3/3] Tests e2e: move WMTS Requests from Cypress to Playwright --- .../integration/requests-service-ghaction.js | 115 -------------- .../end2end/playwright/requests-wmts.spec.js | 143 ++++++++++++++++++ 2 files changed, 143 insertions(+), 115 deletions(-) create mode 100644 tests/end2end/playwright/requests-wmts.spec.js diff --git a/tests/end2end/cypress/integration/requests-service-ghaction.js b/tests/end2end/cypress/integration/requests-service-ghaction.js index 9b91b9ae5c..e3e4f92c22 100644 --- a/tests/end2end/cypress/integration/requests-service-ghaction.js +++ b/tests/end2end/cypress/integration/requests-service-ghaction.js @@ -232,121 +232,6 @@ describe('Request service', function () { }) }) - it('WMTS GetCapabilities', function () { - cy.request('/index.php/lizmap/service/?repository=testsrepository&project=cache&SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetCapabilities') - .then((resp) => { - expect(resp.status).to.eq(200) - expect(resp.headers['content-type']).to.eq('text/xml; charset=utf-8') - expect(resp.headers['cache-control']).to.eq('no-cache') - expect(resp.headers['etag']).to.not.eq(undefined) - - expect(resp.body).to.contain('version="1.0.0"') - expect(resp.body).to.contain('Quartiers') - expect(resp.body).to.contain('EPSG:3857') - - const etag = resp.headers['etag'] - cy.request({ - url: '/index.php/lizmap/service/?repository=testsrepository&project=cache&SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetCapabilities', - headers: { - 'If-None-Match': etag, - }, - failOnStatusCode: false, - }).then((resp) => { - expect(resp.status).to.eq(304) - expect(resp.body).to.have.length(0) - }) - }) - }) - - it('WMTS GetTile', function () { - // Get full transparent tile TILEMATRIX=13&TILEROW=2989&TILECOL=4185 - cy.request({ - method: 'GET', - url: '/index.php/lizmap/service/?repository=testsrepository&project=cache', - qs: { - 'SERVICE': 'WMTS', - 'VERSION': '1.0.0', - 'REQUEST': 'GetTile', - 'LAYER': 'Quartiers', - 'STYLE': 'default', - 'TILEMATRIXSET': 'EPSG:3857', - 'TILEMATRIX': '13', - 'TILEROW': '2989', - 'TILECOL': '4185', - 'FORMAT': 'image/png', - }, - }).then((resp) => { - expect(resp.status).to.eq(200) - expect(resp.headers['content-type']).to.contain('image/png') - expect(resp.headers).to.have.property('content-length', '355') // Transparent - expect(resp.headers).to.have.property('date') - const tileDate = new Date(resp.headers['date']) - expect(resp.headers).to.have.property('expires') - const tileExpires = new Date(resp.headers['expires']) - expect(tileExpires).to.be.greaterThan(tileDate) - /*expect(resp.body).to.contain('version="1.0.0"')*/ - }) - - // Get not full transparent tile TILEMATRIX=13&TILEROW=2991&TILECOL=4184 - cy.request({ - method: 'GET', - url: '/index.php/lizmap/service/?repository=testsrepository&project=cache', - qs: { - 'SERVICE': 'WMTS', - 'VERSION': '1.0.0', - 'REQUEST': 'GetTile', - 'LAYER': 'Quartiers', - 'STYLE': 'default', - 'TILEMATRIXSET': 'EPSG:3857', - 'TILEMATRIX': '13', - 'TILEROW': '2991', - 'TILECOL': '4184', - 'FORMAT': 'image/png', - }, - }).then((resp) => { - expect(resp.status).to.eq(200) - expect(resp.headers['content-type']).to.contain('image/png') - expect(resp.headers).to.have.property('content-length') - expect(parseInt(resp.headers['content-length'])).to.be.greaterThan(355) // Not transparent - expect(parseInt(resp.headers['content-length'])).to.be.within(11020, 11030 ) // Monochrome - expect(resp.headers).to.have.property('date') - const tileDate = new Date(resp.headers['date']) - expect(resp.headers).to.have.property('expires') - const tileExpires = new Date(resp.headers['expires']) - expect(tileExpires).to.be.greaterThan(tileDate) - /*expect(resp.body).to.contain('version="1.0.0"')*/ - }) - - // Get monochrome tile TILEMATRIX=15&TILEROW=11964&TILECOL=16736 - cy.request({ - method: 'GET', - url: '/index.php/lizmap/service/?repository=testsrepository&project=cache', - qs: { - 'SERVICE': 'WMTS', - 'VERSION': '1.0.0', - 'REQUEST': 'GetTile', - 'LAYER': 'Quartiers', - 'STYLE': 'default', - 'TILEMATRIXSET': 'EPSG:3857', - 'TILEMATRIX': '15', - 'TILEROW': '11964', - 'TILECOL': '16736', - 'FORMAT': 'image/png', - }, - }).then((resp) => { - expect(resp.status).to.eq(200) - expect(resp.headers['content-type']).to.contain('image/png') - expect(resp.headers).to.have.property('content-length') - expect(parseInt(resp.headers['content-length'])).to.be.greaterThan(355) // Not transparent - expect(resp.headers).to.have.property('date') - const tileDate = new Date(resp.headers['date']) - expect(resp.headers).to.have.property('expires') - const tileExpires = new Date(resp.headers['expires']) - expect(tileExpires).to.be.greaterThan(tileDate) - /*expect(resp.body).to.contain('version="1.0.0"')*/ - }) - }) - it('Project parameter is mandatory', function () { cy.request({ url: '/index.php/lizmap/service/?repository=testsrepository&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities', diff --git a/tests/end2end/playwright/requests-wmts.spec.js b/tests/end2end/playwright/requests-wmts.spec.js new file mode 100644 index 0000000000..9e2eb3a45d --- /dev/null +++ b/tests/end2end/playwright/requests-wmts.spec.js @@ -0,0 +1,143 @@ +// @ts-check +import { test, expect } from '@playwright/test'; + +test.describe('WMTS Requests @requests @readonly', () => { + test('WMTS Getcapabilities', async({ request }) => { + let params = new URLSearchParams({ + repository: 'testsrepository', + project: 'cache', + SERVICE: 'WMTS', + VERSION: '1.0.0', + REQUEST: 'GetCapabilities', + }); + let url = `/index.php/lizmap/service?${params}`; + let response = await request.get(url, {}); + // check response + expect(response.ok()).toBeTruthy(); + expect(response.status()).toBe(200); + // check content-type header + expect(response.headers()['content-type']).toBe('text/xml; charset=utf-8'); + // check headers + expect(response.headers()).toHaveProperty('cache-control'); + expect(response.headers()['cache-control']).toBe('no-cache'); + expect(response.headers()).toHaveProperty('etag'); + const etag = response.headers()['etag']; + expect(etag).not.toBe(''); + expect(etag).toHaveLength(43); + + // check body + let body = await response.text(); + expect(body).toContain('Capabilities'); + expect(body).toContain('version="1.0.0"'); + expect(body).toContain('xmlns="http://www.opengis.net/wmts/1.0"'); + expect(body).toContain('Quartiers'); + expect(body).toContain('EPSG:3857'); + + // GET request with the etag + response = await request.get(url, { + headers: { + 'If-None-Match': etag + } + }); + await expect(response).not.toBeOK(); + expect(response.status()).toBe(304); + }) + + test('WMTS GetTile', async({ request }) => { + let params = new URLSearchParams({ + repository: 'testsrepository', + project: 'cache', + SERVICE: 'WMTS', + VERSION: '1.0.0', + REQUEST: 'GetTile', + LAYER: 'Quartiers', + STYLE: 'default', + TILEMATRIXSET: 'EPSG:3857', + TILEMATRIX: '13', + TILEROW: '2989', + TILECOL: '4185', + FORMAT: 'image/png', + }); + let url = `/index.php/lizmap/service?${params}`; + let response = await request.get(url, {}); + // check response + await expect(response).toBeOK(); + expect(response.status()).toBe(200); + // check content-type header + expect(response.headers()['content-type']).toBe('image/png'); + // check headers + expect(response.headers()).toHaveProperty('content-length'); + expect(response.headers()['content-length']).toBe('355'); // Transparent + expect(response.headers()).toHaveProperty('date'); + expect(response.headers()).toHaveProperty('expires'); + let tileDate = new Date(response.headers()['date']) + let tileExpires = new Date(response.headers()['expires']) + expect(tileExpires > tileDate).toBeTruthy(); + + params = new URLSearchParams({ + repository: 'testsrepository', + project: 'cache', + SERVICE: 'WMTS', + VERSION: '1.0.0', + REQUEST: 'GetTile', + LAYER: 'Quartiers', + STYLE: 'default', + TILEMATRIXSET: 'EPSG:3857', + TILEMATRIX: '13', + TILEROW: '2991', + TILECOL: '4184', + FORMAT: 'image/png', + }); + url = `/index.php/lizmap/service?${params}`; + response = await request.get(url, {}); + // check response + await expect(response).toBeOK(); + expect(response.status()).toBe(200); + // check content-type header + expect(response.headers()['content-type']).toBe('image/png'); + // check headers + expect(response.headers()).toHaveProperty('content-length'); + let contentLength = Number(response.headers()['content-length']); + expect(contentLength).toBeGreaterThan(355); // Not transparent + expect(contentLength).toBeGreaterThan(11000); // 11019 + expect(contentLength).toBeLessThan(11100); // 11019 + expect(response.headers()).toHaveProperty('date'); + expect(response.headers()).toHaveProperty('expires'); + tileDate = new Date(response.headers()['date']) + tileExpires = new Date(response.headers()['expires']) + expect(tileExpires > tileDate).toBeTruthy(); + + params = new URLSearchParams({ + repository: 'testsrepository', + project: 'cache', + SERVICE: 'WMTS', + VERSION: '1.0.0', + REQUEST: 'GetTile', + LAYER: 'Quartiers', + STYLE: 'default', + TILEMATRIXSET: 'EPSG:3857', + TILEMATRIX: '15', + TILEROW: '11964', + TILECOL: '16736', + FORMAT: 'image/png', + }); + url = `/index.php/lizmap/service?${params}`; + response = await request.get(url, {}); + // check response + await expect(response).toBeOK(); + expect(response.status()).toBe(200); + // check content-type header + expect(response.headers()['content-type']).toBe('image/png'); + // check headers + expect(response.headers()).toHaveProperty('content-length'); + contentLength = Number(response.headers()['content-length']); + expect(contentLength).toBeGreaterThan(355); // Not transparent + expect(contentLength).toBeGreaterThan(650); // 687 + expect(contentLength).toBeLessThan(700); // 687 + expect(response.headers()).toHaveProperty('date'); + expect(response.headers()).toHaveProperty('expires'); + tileDate = new Date(response.headers()['date']) + tileExpires = new Date(response.headers()['expires']) + expect(tileExpires > tileDate).toBeTruthy(); + }) +})