Skip to content
Draft
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
23d053b
Stabilize nightly Playwright failures across environments
sofiaIvarsRitense Jul 9, 2026
21fc9c4
Merge branch 'next-minor' into story/536-stabilize-flaky-e2e-tests
sofiaIvarsRitense Jul 23, 2026
9400d49
Fix case details management
sofiaIvarsRitense Jul 24, 2026
04fda54
Merge branch 'next-minor' into story/536-stabilize-flaky-e2e-tests
sofiaIvarsRitense Jul 27, 2026
915b5aa
Fix playwright tests based on FE changes
sofiaIvarsRitense Jul 27, 2026
05e775d
Create playwright tests for Feature: 13A · BB Overview
sofiaIvarsRitense Jul 28, 2026
ed577bf
Create playwright tests for Feature: 13A · BB Overview and update tes…
sofiaIvarsRitense Jul 28, 2026
cc34bce
Create playwright tests for Feature 13B · BB Details
sofiaIvarsRitense Jul 28, 2026
5c54e97
Add playwright tests for Feature 13C · BB Document
sofiaIvarsRitense Jul 28, 2026
25f4384
Merge branch 'next-minor' into story/building-block-management-tests
sofiaIvarsRitense Aug 4, 2026
713ee44
Add playwright tests for Feature 1 · Dashboard (user)
sofiaIvarsRitense Aug 5, 2026
ba55011
Add playwright tests for Feature 7 — Process Management
sofiaIvarsRitense Aug 5, 2026
4a62ef1
Extend playwright e2e coverage across five admin features
sofiaIvarsRitense Aug 6, 2026
569e47d
Add playwright tests for Feature 14 — Translation Management
sofiaIvarsRitense Aug 6, 2026
782a41a
Add playwright tests for Feature 13E — Building Block Plugin Integration
sofiaIvarsRitense Aug 7, 2026
6b3a413
Add playwright tests for Feature 13E — Building Block Plugin Integrat…
sofiaIvarsRitense Aug 10, 2026
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
111 changes: 62 additions & 49 deletions e2e/TEST_COVERAGE.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions e2e/api/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ export const endpoints = {
dataSources: '/api/management/v1/dashboard/widget-data-sources',
},

/**
* User-facing dashboard endpoints (as consumed by the dashboard shown on `/`).
* These are filtered by the caller's role via access-control permissions, so they
* only return dashboards the current user is allowed to view.
*/
userDashboard: {
getAll: '/api/v1/dashboard',
data: (dashboardKey: string) => `/api/v1/dashboard/${dashboardKey}/data`,
},

Comment on lines +14 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the standard EUPL license header at the start of this file.

Line 14 starts code before the required TypeScript header can end. Add the repository standard header before all code. As per path instructions, each .ts file must start with the standard EUPL header.

Source: Path instructions

formFlow: {
getAll: '/api/management/v1/form-flow/definition',
getByKeyAndVersion: (key: string, version: number) =>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
11 changes: 11 additions & 0 deletions e2e/constants/building-block-management-test-ids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export {
BUILDING_BLOCK_MANAGEMENT_LIST_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_CREATE_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_UPLOAD_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_METADATA_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_ARTWORK_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_PLUGINS_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_ACTIONS_TEST_IDS,
BUILDING_BLOCK_VERSION_OPTION_TEST_ID_PREFIX,
} from '../../frontend/projects/valtimo/building-block-management/src/lib/constants/building-block-management.test-ids';
Comment on lines +1 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required license header.

This TypeScript file starts with export on Line 1. Add the standard EUPL header before the first export.

Proposed header
+/*
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
+ *
+ * Licensed under EUPL, Version 1.2 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 export {

As per path instructions, TypeScript files must start with the standard EUPL license header.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export {
BUILDING_BLOCK_MANAGEMENT_LIST_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_CREATE_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_UPLOAD_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_METADATA_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_ARTWORK_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_PLUGINS_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_ACTIONS_TEST_IDS,
BUILDING_BLOCK_VERSION_OPTION_TEST_ID_PREFIX,
} from '../../frontend/projects/valtimo/building-block-management/src/lib/constants/building-block-management.test-ids';
/*
* Copyright 2015-2026 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
BUILDING_BLOCK_MANAGEMENT_LIST_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_CREATE_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_UPLOAD_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_METADATA_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_ARTWORK_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_PLUGINS_TEST_IDS,
BUILDING_BLOCK_MANAGEMENT_DETAIL_ACTIONS_TEST_IDS,
BUILDING_BLOCK_VERSION_OPTION_TEST_ID_PREFIX,
} from '../../frontend/projects/valtimo/building-block-management/src/lib/constants/building-block-management.test-ids';

Source: Path instructions

2 changes: 2 additions & 0 deletions e2e/constants/components-test-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ export {
SINGLE_VALUE_TEST_IDS,
AUTO_KEY_INPUT_TEST_IDS,
RIGHT_SIDEBAR_TEST_IDS,
SCHEMA_EDITOR_TEST_IDS,
SCHEMA_EDITOR_REQUIRED_PROPERTY_TEST_ID_PREFIX,
} from '../../frontend/projects/valtimo/components/src/lib/constants/components.test-ids';
20 changes: 20 additions & 0 deletions e2e/constants/dashboard-test-ids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2015-2026 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export {
DASHBOARD_TEST_IDS,
DASHBOARD_WIDGET_TEST_ID_PREFIX,
} from '../../frontend/projects/valtimo/dashboard/src/lib/constants/dashboard.test-ids';
2 changes: 2 additions & 0 deletions e2e/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './building-block-management-test-ids';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required license header.

Line 1 starts with code. Prepend the standard EUPL license header before this export.

As per path instructions, each *.ts file must start with the standard license header.

Source: Path instructions

export * from './case-management-test-ids';
export * from './dashboard-management-test-ids';
export * from './dashboard-test-ids';
export * from './components-test-ids';
export * from './decision-test-ids';
export * from './form-management-test-ids';
Expand Down
2 changes: 1 addition & 1 deletion e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default defineConfig({
},
baseURL: process.env.qa_url ?? 'http://localhost:4200',
screenshot: process.env.CI ? 'only-on-failure' : 'on',
video: process.env.CI ? 'on' : 'on-first-retry',
video: process.env.CI ? 'retain-on-failure' : 'on-first-retry',
trace: {
mode: process.env.CI ? 'retain-on-failure' : 'on-first-retry',
snapshots: true,
Expand Down
69 changes: 69 additions & 0 deletions e2e/shared/overflow-menu/overflow-menu.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2015-2026 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {expect, type Locator, type Page} from '@playwright/test';

/**
* Wrapper for the Valtimo `v-overflow-menu` component (the "More" menus in
* admin detail headers and carbon-list rows).
*
* The menu content is rendered in an overlay outside the trigger, so options are
* looked up page-wide rather than inside the trigger element. Options are only
* present in the DOM while the menu is open.
*/
export class OverflowMenu {
constructor(
private readonly page: Page,
private readonly triggerTestId: string
) {}

get trigger(): Locator {
return this.page.getByTestId(this.triggerTestId);
}

get menu(): Locator {
return this.page.getByRole('menu');
}

option(optionTestId: string): Locator {
return this.page.getByTestId(optionTestId);
}

async open() {
await expect(this.trigger).toBeEnabled();
await this.trigger.click();
await expect(this.menu).toBeVisible();
}

async close() {
await this.page.keyboard.press('Escape');
await expect(this.menu).not.toBeVisible();
}

/** Open the menu and click one of its options. */
async selectOption(optionTestId: string) {
await this.open();
const option = this.option(optionTestId);
await expect(option).toBeVisible();
await option.click();
}

/** Labels of the options currently offered, in render order. */
async optionLabels(): Promise<string[]> {
const labels = await this.menu.getByRole('menuitem').allInnerTexts();
return labels.map(label => label.trim());
}
}
219 changes: 219 additions & 0 deletions e2e/shared/schema-editor/schema-editor.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* Copyright 2015-2026 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {expect, type Locator, type Page} from '@playwright/test';
import {
CONFIRMATION_MODAL_TEST_IDS,
SCHEMA_EDITOR_REQUIRED_PROPERTY_TEST_ID_PREFIX,
SCHEMA_EDITOR_TEST_IDS,
} from '../../constants';

/**
* Wrapper for `valtimo-schema-editor` (`@valtimo/components`), the JSON-schema
* editor used on the document tabs of case and building block definitions.
*
* The editor embeds `vanilla-jsoneditor`, a third-party component whose controls
* cannot carry `data-test-id` attributes. Those parts (mode switch, search,
* parse-error panel, tree nodes) are therefore addressed by their accessible
* name, title or the library's own class names; everything Valtimo owns — the
* save button, the required-fields panel and its checkboxes — uses test ids.
*/
export class SchemaEditor {
constructor(private readonly page: Page) {}

// ─── Root ─────────────────────────────────────────────────────────

get root(): Locator {
return this.page.getByTestId(SCHEMA_EDITOR_TEST_IDS.editor);
}

get saveButton(): Locator {
return this.page.getByTestId(SCHEMA_EDITOR_TEST_IDS.saveButton);
}

get manageRequiredFieldsButton(): Locator {
return this.page.getByTestId(SCHEMA_EDITOR_TEST_IDS.manageRequiredFieldsButton);
}

get confirmSaveButton(): Locator {
return this.page.getByTestId(CONFIRMATION_MODAL_TEST_IDS.confirmButton);
}

// ─── Tree mode (third-party markup) ───────────────────────────────

/** Property/attribute names rendered by the tree view. */
get keys(): Locator {
return this.root.locator('.jse-key');
}

/** Values rendered by the tree view — field types and descriptions included. */
get values(): Locator {
return this.root.locator('.jse-value');
}

async keyTexts(): Promise<string[]> {
return (await this.keys.allInnerTexts()).map(text => text.trim());
}

async valueTexts(): Promise<string[]> {
return (await this.values.allInnerTexts()).map(text => text.trim());
}

async waitForLoaded() {
await expect(this.root).toBeVisible();
await expect(this.keys.first()).toBeVisible();
}

// ─── Mode switching ───────────────────────────────────────────────

async switchToTextMode() {
await this.root.getByRole('button', {name: 'text', exact: true}).click();
await expect(this.textContent).toBeVisible();
}

async switchToTreeMode() {
await this.root.getByRole('button', {name: 'tree', exact: true}).click();
await expect(this.keys.first()).toBeVisible();
}

/** The CodeMirror surface of the editor's text mode. */
get textContent(): Locator {
return this.root.locator('.cm-content').first();
}

// ─── Editing ──────────────────────────────────────────────────────

/**
* Replace the whole document with `content` by typing it in text mode.
*
* Typing rather than pasting is deliberate: the editor only emits its change
* event on real input, and a clipboard paste does not reliably reach the
* CodeMirror instance in headless Chromium.
*/
async replaceContent(content: unknown) {
await this.switchToTextMode();
await this.textContent.click();
await this.page.keyboard.press('ControlOrMeta+a');
await this.page.keyboard.type(typeof content === 'string' ? content : JSON.stringify(content));
}

/** Save and confirm. Returns nothing — await the API response in the caller. */
async save() {
await expect(this.saveButton).toBeEnabled();
await this.saveButton.click();
await expect(this.confirmSaveButton).toBeVisible();
await this.confirmSaveButton.click();
}

// ─── Parse errors ─────────────────────────────────────────────────

/** Panel the embedded editor shows when the text is not parseable JSON. */
get parseError(): Locator {
return this.root.locator('.jse-message.jse-error');
}

get autoRepairButton(): Locator {
return this.root.getByRole('button', {name: 'Auto repair'});
}

// ─── Search ───────────────────────────────────────────────────────

get searchBox(): Locator {
return this.root.locator('.jse-search-box');
}

get searchInput(): Locator {
return this.searchBox.locator('input').first();
}

/** Matches highlighted in the tree for the active search term. */
get searchHighlights(): Locator {
return this.root.locator('.jse-highlight');
}

async openSearch() {
await this.root.getByTitle(/^Search/).click();
await expect(this.searchInput).toBeVisible();
}

async closeSearch() {
await this.page.keyboard.press('Escape');
await expect(this.searchBox).toHaveCount(0);
}

async search(term: string) {
await this.searchInput.fill(term);
}

/**
* The editor's match counter: `"<current>/<total>"` while there are matches and
* `"0"` when there are none.
*/
async searchResultCount(): Promise<string> {
return (await this.searchBox.innerText()).trim();
}

// ─── Required fields panel ────────────────────────────────────────

get requiredFieldsPanel(): Locator {
return this.page.getByTestId(SCHEMA_EDITOR_TEST_IDS.requiredFieldsPanel);
}

get requiredFieldsPanelCloseButton(): Locator {
return this.page.getByTestId(SCHEMA_EDITOR_TEST_IDS.requiredFieldsPanelCloseButton);
}

/**
* Checkbox of a required field, addressed by its path within the schema:
* `requiredFieldCheckbox('applicantName')` for a root property,
* `requiredFieldCheckbox('address', 'street')` for a nested one.
*/
requiredFieldCheckbox(...path: string[]): Locator {
return this.page.getByTestId(
`${SCHEMA_EDITOR_REQUIRED_PROPERTY_TEST_ID_PREFIX}${path.join('.')}`
);
}

async openRequiredFieldsPanel() {
await this.manageRequiredFieldsButton.click();
await expect(this.requiredFieldsPanel).toBeVisible();
}

async closeRequiredFieldsPanel() {
await this.requiredFieldsPanelCloseButton.click();
}

/** Carbon's `cds-checkbox` only emits `checkedChange` for a click on its label. */
async toggleRequiredField(...path: string[]) {
await this.requiredFieldCheckbox(...path)
.locator('label')
.click();
}

async isRequiredFieldChecked(...path: string[]): Promise<boolean> {
return this.requiredFieldCheckbox(...path)
.locator('input')
.isChecked();
}

async assertRequiredFieldChecked(...path: string[]) {
await expect(this.requiredFieldCheckbox(...path).locator('input')).toBeChecked();
}

async assertRequiredFieldNotChecked(...path: string[]) {
await expect(this.requiredFieldCheckbox(...path).locator('input')).not.toBeChecked();
}
}
Loading
Loading