Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 78 additions & 12 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 24.22.0
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24.22.0'
cache: npm
cache-dependency-path: |
package-lock.json
Expand Down Expand Up @@ -81,10 +81,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 24.22.0
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24.22.0'
cache: npm
cache-dependency-path: |
package-lock.json
Expand All @@ -100,6 +100,28 @@ jobs:
- name: Install Chromium
run: npx playwright install --with-deps chromium

# e2e-viewer.mjs requires ~/.nvm/versions/node/v<nvmrc>/bin (setup-node does not).
# dcodeIO/setup-node-nvm installs into $HOME/mynvm; link that tree to ~/.nvm.
- name: Read viewer .nvmrc
id: viewer-nvmrc
run: |
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
echo "version=${ver#v}" >> "$GITHUB_OUTPUT"
- name: Install viewer Node into nvm
uses: dcodeIO/setup-node-nvm@v5
with:
node-version: ${{ steps.viewer-nvmrc.outputs.version }}
- name: Point ~/.nvm at setup-node-nvm install
run: |
# Ubuntu runners often ship ~/.nvm as a real directory; ln -sfn into a
# directory nests the link. Replace it so e2e-viewer.mjs finds node.
echo "NVM_DIR=${HOME}/mynvm" >> "$GITHUB_ENV"
rm -rf "${HOME}/.nvm"
ln -sfn "${HOME}/mynvm" "${HOME}/.nvm"
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
ver="${ver#v}"
test -x "${HOME}/.nvm/versions/node/v${ver}/bin/node"

- name: Run viewer E2E suite
run: npm run e2e:viewer
env:
Expand Down Expand Up @@ -136,10 +158,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 24.22.0
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24.22.0'
cache: npm
cache-dependency-path: |
package-lock.json
Expand All @@ -160,6 +182,28 @@ jobs:
- name: Install Chromium
run: npx playwright install --with-deps chromium

# e2e-viewer.mjs requires ~/.nvm/versions/node/v<nvmrc>/bin (setup-node does not).
# dcodeIO/setup-node-nvm installs into $HOME/mynvm; link that tree to ~/.nvm.
- name: Read viewer .nvmrc
id: viewer-nvmrc
run: |
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
echo "version=${ver#v}" >> "$GITHUB_OUTPUT"
- name: Install viewer Node into nvm
uses: dcodeIO/setup-node-nvm@v5
with:
node-version: ${{ steps.viewer-nvmrc.outputs.version }}
- name: Point ~/.nvm at setup-node-nvm install
run: |
# Ubuntu runners often ship ~/.nvm as a real directory; ln -sfn into a
# directory nests the link. Replace it so e2e-viewer.mjs finds node.
echo "NVM_DIR=${HOME}/mynvm" >> "$GITHUB_ENV"
rm -rf "${HOME}/.nvm"
ln -sfn "${HOME}/mynvm" "${HOME}/.nvm"
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
ver="${ver#v}"
test -x "${HOME}/.nvm/versions/node/v${ver}/bin/node"

- name: Run application-contact E2E suite
run: npx playwright test --config=playwright.application-contact.config.ts
env:
Expand Down Expand Up @@ -196,10 +240,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 24.22.0
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24.22.0'
cache: npm
cache-dependency-path: |
package-lock.json
Expand All @@ -220,6 +264,28 @@ jobs:
- name: Install Chromium
run: npx playwright install --with-deps chromium

# e2e-viewer.mjs requires ~/.nvm/versions/node/v<nvmrc>/bin (setup-node does not).
# dcodeIO/setup-node-nvm installs into $HOME/mynvm; link that tree to ~/.nvm.
- name: Read viewer .nvmrc
id: viewer-nvmrc
run: |
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
echo "version=${ver#v}" >> "$GITHUB_OUTPUT"
- name: Install viewer Node into nvm
uses: dcodeIO/setup-node-nvm@v5
with:
node-version: ${{ steps.viewer-nvmrc.outputs.version }}
- name: Point ~/.nvm at setup-node-nvm install
run: |
# Ubuntu runners often ship ~/.nvm as a real directory; ln -sfn into a
# directory nests the link. Replace it so e2e-viewer.mjs finds node.
echo "NVM_DIR=${HOME}/mynvm" >> "$GITHUB_ENV"
rm -rf "${HOME}/.nvm"
ln -sfn "${HOME}/mynvm" "${HOME}/.nvm"
ver="$(tr -d '[:space:]' < front/viewer/sitmun-viewer-app/.nvmrc)"
ver="${ver#v}"
test -x "${HOME}/.nvm/versions/node/v${ver}/bin/node"

- name: Run MIA cross E2E suite
run: npm run e2e:mia-cross
env:
Expand Down Expand Up @@ -256,10 +322,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 24.22.0
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '24.22.0'
cache: npm
cache-dependency-path: package-lock.json

Expand Down Expand Up @@ -305,10 +371,10 @@ jobs:
chmod +x back/shared/sitmun-shared/gradlew
back/shared/sitmun-shared/gradlew -p back/shared/sitmun-shared publishToMavenLocal

- name: Set up Node 20
- name: Set up Node 22
uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: npm
cache-dependency-path: |
package-lock.json
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

#### Stack-level

- **Toolchain**: Submodule gitlinks point at Spring Boot `4.1.1` (shared, backend, proxy, mbtiles), Angular `22.2.3` / Node `24.22.0` (admin, viewer), and Gradle `8.14.6`. Root image and CI use Node `24.22.0`; mobile Android E2E stays on Node `22`. Published shared coordinate remains `1.2.9`.
- **Viewer**: The stack points at viewer `dev`, which replaces filler control-spec expects, deletes the unused control loader, drops three local `any` casts, and removes comments that name the wrong type or narrate the next line ([sitmun-viewer-app#199](https://github.com/sitmun/sitmun-viewer-app/pull/199), [#200](https://github.com/sitmun/sitmun-viewer-app/pull/200), [#201](https://github.com/sitmun/sitmun-viewer-app/pull/201), [#202](https://github.com/sitmun/sitmun-viewer-app/pull/202), [#203](https://github.com/sitmun/sitmun-viewer-app/pull/203)).
- **Admin**: The stack points at admin `dev`, including the service-access clock and the cleanup that replaces filler expects, deletes unused task fields, corrects comments, drops local `any` casts, and removes narrating comments ([sitmun-admin-app#475](https://github.com/sitmun/sitmun-admin-app/pull/475), [#476](https://github.com/sitmun/sitmun-admin-app/pull/476), [#477](https://github.com/sitmun/sitmun-admin-app/pull/477), [#478](https://github.com/sitmun/sitmun-admin-app/pull/478), [#479](https://github.com/sitmun/sitmun-admin-app/pull/479), [#480](https://github.com/sitmun/sitmun-admin-app/pull/480)).
- **Backend**: The stack points at backend `dev`. CI publishes `sitmun-shared` before the build. An LDAP login fails when the role lookup throws. Cleanup replaces filler expects, deletes unused symbols, and removes comments that name the wrong type or narrate the next line ([sitmun-backend-core#274](https://github.com/sitmun/sitmun-backend-core/pull/274), [#275](https://github.com/sitmun/sitmun-backend-core/pull/275), [#276](https://github.com/sitmun/sitmun-backend-core/pull/276), [#277](https://github.com/sitmun/sitmun-backend-core/pull/277), [#278](https://github.com/sitmun/sitmun-backend-core/pull/278), [#279](https://github.com/sitmun/sitmun-backend-core/pull/279), [#280](https://github.com/sitmun/sitmun-backend-core/pull/280), [#281](https://github.com/sitmun/sitmun-backend-core/pull/281)).
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ Admin App ───┘
Root Playwright tests exercise the admin and viewer apps against backend-core on in-memory H2 (no Docker Compose). Viewer suite also starts proxy middleware and a local secured WMS stub. Mobile web suite covers edition Bearer login, proxy-token exchange, and authenticated MBTiles through middleware (no public `/mbtiles`). Details: [e2e/README.md](e2e/README.md).

```bash
# prerequisites: Java 17, Node ≥ 20.19, submodules initialized
# prerequisites: Java 17, Node ≥ 24.22.0 (mobile Android E2E needs Node 22), submodules initialized
# admin: cd front/admin/sitmun-admin-app && npm ci
# viewer: cd front/viewer/sitmun-viewer-app && npm ci
npm ci
Expand Down Expand Up @@ -1036,7 +1036,7 @@ rm -rf node_modules package-lock.json
npm ci

# Check Node.js version
node --version # Should be 20.19.0 or higher
node --version # Should be 24.22.0 or higher (Node 22 for mobile Android E2E)
```

#### Backend Build Issues
Expand Down
2 changes: 1 addition & 1 deletion back/backend/sitmun-backend-core
2 changes: 1 addition & 1 deletion back/mbtiles/sitmun-mbtiles
2 changes: 1 addition & 1 deletion back/proxy/sitmun-proxy-middleware
Submodule sitmun-proxy-middleware updated 24 files
+3 −0 CHANGELOG.md
+3 −3 README.md
+24 −9 build.gradle
+5 −2 gradle/libs.versions.toml
+1 −1 gradle/wrapper/gradle-wrapper.properties
+1 −1 src/main/java/org/sitmun/proxy/middleware/Application.java
+1 −1 src/main/java/org/sitmun/proxy/middleware/config/ProxyMiddlewareConfiguration.java
+1 −1 src/main/java/org/sitmun/proxy/middleware/controllers/MbtilesProxyController.java
+2 −2 src/main/java/org/sitmun/proxy/middleware/dto/ProxyProblemResponses.java
+2 −2 src/main/java/org/sitmun/proxy/middleware/mbtiles/JwtPrincipalExtractor.java
+2 −2 src/main/java/org/sitmun/proxy/middleware/mbtiles/MbtilesProxyService.java
+1 −1 src/main/java/org/sitmun/proxy/middleware/mbtiles/MbtilesUpstreamClient.java
+1 −1 src/main/java/org/sitmun/proxy/middleware/service/RequestConfigurationService.java
+3 −3 src/test/java/org/sitmun/proxy/middleware/ProxyParameterizedJdbcIntegrationTest.java
+2 −2 src/test/java/org/sitmun/proxy/middleware/controllers/MbtilesProxyControllerTest.java
+1 −1 src/test/java/org/sitmun/proxy/middleware/controllers/ProxyMiddlewareControllerMvcTest.java
+2 −2 src/test/java/org/sitmun/proxy/middleware/dto/ProxyProblemResponsesTest.java
+3 −3 src/test/java/org/sitmun/proxy/middleware/protocols/http/ExecutionRequestExecutorServiceTest.java
+2 −2 src/test/java/org/sitmun/proxy/middleware/protocols/http/HttpContextSecurityContractTest.java
+1 −1 src/test/java/org/sitmun/proxy/middleware/protocols/http/HttpRequestExecutorTest.java
+1 −1 src/test/java/org/sitmun/proxy/middleware/protocols/jdbc/ExecutionRequestExecutorServiceTest.java
+4 −4 src/test/java/org/sitmun/proxy/middleware/protocols/wms/ExecutionRequestExecutorServiceTest.java
+3 −3 src/test/java/org/sitmun/proxy/middleware/service/ExecutionRequestExecutorServiceTest.java
+4 −4 src/test/java/org/sitmun/proxy/middleware/service/RequestConfigurationServiceTest.java
5 changes: 3 additions & 2 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Browser E2E against backend-core on in-memory H2. No Docker Compose.
- Language chrome on login: cleared `lang` → closed BCP-47 ISO from `language.default`; open menu shows API endonyms; no language field on the login form (`e2e/admin/language-chrome.spec.ts`, project `login`)
- Role form **Details**: validation, create, edit, reload persistence (relation tabs not exercised)
- User form **Details**: validation, create, edit, reload persistence (relation tabs not exercised)
- User form **Positions**: `Valid from` immediately before `Valid until` (ES `Fecha de alta` / `Fecha de baja`); empty `createdDate` is Not set / No informada; empty `expirationDate` is Active; Alta edit persist; inclusive last-day tooltip; hidden for `public`; pre-2000 Alta cell; viewer cannot write Alta; 20-row open under 3s ([sitmun-admin-app#462](https://github.com/sitmun/sitmun-admin-app/issues/462); `e2e/admin/forms/user-positions.spec.ts`, project `admin-forms`)
- User form **Positions**: `Valid from` immediately before `Valid until` (ES `Fecha de alta` / `Fecha de baja`); empty `createdDate` is Not set / No informada; empty `expirationDate` is Active; Alta and Baja edit persist; Position name text edit persist; inclusive last-day tooltip; hidden for `public`; pre-2000 Alta cell; viewer cannot write Alta; 20-row open under 3s ([sitmun-admin-app#462](https://github.com/sitmun/sitmun-admin-app/issues/462); `e2e/admin/forms/user-positions.spec.ts`, project `admin-forms`)
- Territory form **Details**: validation, create (with type), edit, reload persistence (relation tabs not exercised)
- Tree form **Details** SVG local-file persist and touristic **Tree structure** node Appearance SVG persist: picker `accept` includes `.svg`; POST/PUT stores `data:image/svg+xml;base64,...` without raster scaling; reload preview stays SVG ([sitmun-admin-app#330](https://github.com/sitmun/sitmun-admin-app/issues/330); `e2e/admin/forms/tree-svg-image.spec.ts`, project `admin-forms`)
- Layers form **Details**: validation, create, reload persistence; Feature Information character-count must not throw `raw.split` beside the queryable-layers CSV validator (`e2e/admin/forms/layers-form.spec.ts`, project `admin-forms`)
Expand All @@ -24,6 +24,7 @@ Browser E2E against backend-core on in-memory H2. No Docker Compose.

- Plantilla role and territory tabs: assigning a role and a territory on the task form persists after reload ([sitmun-admin-app#467](https://github.com/sitmun/sitmun-admin-app/issues/467); `e2e/admin/forms/template-task-relations.spec.ts`, project `admin-forms`)
- More Info Advanced **Details**: validation, create with cartography + included query child 38, layout update persistence; cartography open-in-new; deep route `/#/tasks/42/16`; Parameters `app-relation-grid` add + reload persist; child parameter mapping UI round-trip + persists across a subsequent Parameters save; orphan `childTaskParameters` keys dropped on save; Add mapping disabled when included child has no parameters; ADMIN `POST /api/tasks/template/more-info-advanced/render` for seeded parent 42 (`e2e/admin/forms/mia-form.spec.ts`, project `admin-forms`)
- Cartography collection: ADMIN `GET /api/cartographies?size=10000` returns 200 (Boot 4.1.1 / Hibernate 7 EntityGraph+styles bag regression that blanked MIA cartography autocomplete) (`e2e/admin/forms/cartography-list.spec.ts`, project `admin-forms`)
- Language default change: Set as Default preview dialog cancel leaves `language.default` unchanged; raw config PUT cannot freely replace it (`e2e/admin/forms/language-default.spec.ts`)
- Language enabled/order: disable/reorder a non-default language, assert login chrome omits it, restore (`e2e/admin/forms/language-order.spec.ts`)
- Literal translations grid CRUD (not `app-relation-grid`; no CSV): create row and reload persists (`e2e/admin/forms/literal-translation-form.spec.ts`)
Expand Down Expand Up @@ -99,7 +100,7 @@ Shared H2 + admin + viewer + proxy + WMS stub. Do not run concurrently with admi
## Prerequisites

- Java 17
- Node ≥ 20.19
- Node ≥ 24.22.0 (admin, viewer, and root web E2E). Mobile Android E2E needs Node 22.
- Initialized submodules: `git submodule update --init --recursive`
- Admin dependencies: `cd front/admin/sitmun-admin-app && npm ci`
- Viewer dependencies: `cd front/viewer/sitmun-viewer-app && npm ci`
Expand Down
17 changes: 17 additions & 0 deletions e2e/admin/forms/cartography-list.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { test, expect } from '../fixtures';

/**
* Boot 4.1.1 / Hibernate 7 regression: paged cartography list with EntityGraph+styles bag
* returned 500, which blanked MIA (and other) forms that call fetchAllRawItems(size=10000).
*/
test.describe('Cartography collection list', () => {
test('GET /api/cartographies?size=10000 returns 200 for admin', async ({ request }) => {
const response = await request.get('/backend/api/cartographies?size=10000', {
headers: { 'X-SITMUN-Client': 'admin' },
});
expect(response.status(), await response.text()).toBe(200);
const body = await response.json();
const items = body?._embedded?.cartographies ?? body;
expect(Array.isArray(items) ? items.length : 0).toBeGreaterThan(0);
});
});
41 changes: 41 additions & 0 deletions e2e/admin/forms/user-positions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
POSITIONS_TAB,
createUserViaForm,
editAltaCell,
editBajaCell,
editPositionNameCell,
expectCreatedThenExpiration,
headerTexts,
openPositions,
Expand Down Expand Up @@ -69,6 +71,45 @@ test.describe('User Positions tab', () => {
await saveShot(page, 'admin-edit-alta.png');
});

// Sibling of Alta edit: same Instant ISO + datePicker path on expirationDate.
test('edits Baja and it persists after reload', async ({ page, request, createdResources }) => {
const { id } = await createUserViaForm(page, 'e2ebajaedt');
createdResources.push({ collection: 'users', id });
await postPosition(request, id, { expirationDate: '2030-12-31T00:00:00.000Z' });

await openPositions(page, id);
await editBajaCell(page, '2028-07-04');
await savePositionUpdate(page);

await page.reload();
await openPositions(page, id);
await expect(
page.locator('app-relation-grid .ag-cell[col-id="expirationDate"]').first(),
).toContainText(/2028/);
await saveShot(page, 'admin-edit-baja.png');
});

// Sibling: CommunityFeaturesModule text editor on the same Positions grid.
test('edits Position name and it persists after reload', async ({
page,
request,
createdResources,
}) => {
const { id } = await createUserViaForm(page, 'e2eposn');
createdResources.push({ collection: 'users', id });
await postPosition(request, id, { name: 'Cargo' });

await openPositions(page, id);
await editPositionNameCell(page, 'Cargo editado');
await savePositionUpdate(page);

await page.reload();
await openPositions(page, id);
await expect(page.locator('app-relation-grid .ag-cell[col-id="name"]').first()).toContainText(
'Cargo editado',
);
});

test('Baja header tooltip states the inclusive last civil day', async ({
page,
request,
Expand Down
33 changes: 31 additions & 2 deletions e2e/admin/helpers/user-positions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,12 @@ export async function saveShot(page: Page, name: string): Promise<void> {
await page.screenshot({ path: `test-results/${name}` });
}

export async function editAltaCell(page: Page, isoDate: string): Promise<void> {
const cell = page.locator('app-relation-grid .ag-cell[col-id="createdDate"]').first();
export async function editDateCell(
page: Page,
colId: 'createdDate' | 'expirationDate',
isoDate: string,
): Promise<void> {
const cell = page.locator(`app-relation-grid .ag-cell[col-id="${colId}"]`).first();
await expect(cell).toBeVisible({ timeout: 15_000 });
await cell.dblclick();
const editor = page.locator('app-relation-grid .ag-cell-inline-editing input').first();
Expand All @@ -139,6 +143,31 @@ export async function editAltaCell(page: Page, isoDate: string): Promise<void> {
await page.keyboard.press('Enter');
}

export async function editAltaCell(page: Page, isoDate: string): Promise<void> {
await editDateCell(page, 'createdDate', isoDate);
}

export async function editBajaCell(page: Page, isoDate: string): Promise<void> {
await editDateCell(page, 'expirationDate', isoDate);
}

/** Inline text editor on Position name (CommunityFeaturesModule text path). */
export async function editPositionNameCell(page: Page, name: string): Promise<void> {
const cell = page.locator('app-relation-grid .ag-cell[col-id="name"]').first();
await expect(cell).toBeVisible({ timeout: 15_000 });
await cell.dblclick();
const editor = page.locator('app-relation-grid .ag-cell-inline-editing input').first();
if ((await editor.count()) === 0 || !(await editor.isVisible())) {
await cell.click();
await page.keyboard.press('F2');
}
await expect(page.locator('app-relation-grid .ag-cell-inline-editing input').first()).toBeVisible({
timeout: 5_000,
});
await page.locator('app-relation-grid .ag-cell-inline-editing input').first().fill(name);
await page.keyboard.press('Enter');
}

export async function savePositionUpdate(page: Page): Promise<void> {
const putResponse = page.waitForResponse((response) => {
try {
Expand Down
Loading
Loading