From e76e6ff937eacd53aa0d2bf17d80db45b3f5dad3 Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Sun, 31 May 2026 07:41:32 +0200 Subject: [PATCH 1/5] refactor(filtered-search): focus criterion input via afterNextRender --- playwright-merge.config.ts | 40 ------------------- playwright.config.ts | 4 +- .../si-filtered-search.spec.ts | 4 ++ .../si-filtered-search-value.component.ts | 28 ++++++++----- 4 files changed, 24 insertions(+), 52 deletions(-) delete mode 100644 playwright-merge.config.ts diff --git a/playwright-merge.config.ts b/playwright-merge.config.ts deleted file mode 100644 index a68545654d..0000000000 --- a/playwright-merge.config.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) Siemens 2016 - 2026 - * SPDX-License-Identifier: MIT - */ -import { PlaywrightTestConfig } from '@playwright/test'; - -/** - * See https://playwright.dev/docs/test-configuration. - */ -const config: PlaywrightTestConfig = { - testDir: './playwright/e2e/element', - snapshotDir: './playwright/snapshots', - outputDir: './playwright/results/tests', - reporter: [ - ['github'], - [ - 'html', - { - open: 'on-failure', - outputFolder: './playwright/results/preview' - } - ], - [ - 'junit', - { - outputFile: `./playwright/results/reports/report-e2e.xml`, - includeProjectInTestName: true - } - ], - [ - './playwright/reporters/playwright-axe-reporter.ts', - { - outputFile: './playwright/results/a11y/accessibility-report.json', - htmlOutputDir: './playwright/results/a11y/tests' - } - ] - ] -}; - -export default config; diff --git a/playwright.config.ts b/playwright.config.ts index b4272a2f26..fb88f0cbe2 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -127,8 +127,8 @@ const config: PlaywrightTestConfig = { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: `http://${localAddress}:${port}`, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: isCI ? 'on-first-retry' : 'retain-on-failure', + /* Retain a full trace from the failing attempt. See https://playwright.dev/docs/trace-viewer */ + trace: 'retain-on-failure', viewport: baseViewport, diff --git a/playwright/e2e/element-examples/si-filtered-search.spec.ts b/playwright/e2e/element-examples/si-filtered-search.spec.ts index 9f304cbb9f..558cce57bd 100644 --- a/playwright/e2e/element-examples/si-filtered-search.spec.ts +++ b/playwright/e2e/element-examples/si-filtered-search.spec.ts @@ -71,6 +71,10 @@ test.describe('filtered search', () => { await expect(page.getByRole('option', { name: 'Karlsruhe' })).toHaveClass(/active/); await page.keyboard.type('annover'); await expect(page.getByRole('option').first()).not.toBeVisible(); // Ensures that the view was updated by Angular after typing. + // Guard against dropped keystrokes under CPU load: confirm full value before committing. + await expect( + page.locator('.pill-group', { hasText: 'Location' }).getByRole('combobox') + ).toHaveValue('Hannover'); await page.keyboard.press('Enter'); await expect(freeTextSearch).toBeFocused(); await freeTextSearch.fill('Building:House'); diff --git a/projects/element-ng/filtered-search/si-filtered-search-value.component.ts b/projects/element-ng/filtered-search/si-filtered-search-value.component.ts index 65b3b339bc..281471b489 100644 --- a/projects/element-ng/filtered-search/si-filtered-search-value.component.ts +++ b/projects/element-ng/filtered-search/si-filtered-search-value.component.ts @@ -4,10 +4,13 @@ */ import { CdkMonitorFocus, FocusOrigin } from '@angular/cdk/a11y'; import { + afterNextRender, ChangeDetectionStrategy, Component, computed, ElementRef, + inject, + Injector, input, model, OnInit, @@ -48,6 +51,8 @@ import { SiFilteredSearchTypeaheadComponent } from './values/typeahead/si-filter changeDetection: ChangeDetectionStrategy.OnPush }) export class SiFilteredSearchValueComponent implements OnInit { + private readonly injector = inject(Injector); + readonly value = model.required(); readonly definition = input.required(); readonly disabled = input.required(); @@ -123,16 +128,19 @@ export class SiFilteredSearchValueComponent implements OnInit { this.active.set(true); this.hasPendingFocus = true; - setTimeout(() => { - if (field === 'value') { - this.valueInput()?.focus(); - } else if (field === 'operator') { - this.operatorInput()?.nativeElement.focus(); - } else { - (this.operatorInput()?.nativeElement ?? this.valueInput())?.focus(); - } - this.hasPendingFocus = false; - }); + afterNextRender( + () => { + if (field === 'value') { + this.valueInput()?.focus(); + } else if (field === 'operator') { + this.operatorInput()?.nativeElement.focus(); + } else { + (this.operatorInput()?.nativeElement ?? this.valueInput())?.focus(); + } + this.hasPendingFocus = false; + }, + { injector: this.injector } + ); } protected backspaceOverflow(): void { From ab7c8ee74656447113070bb9445da276cae64e2e Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Sun, 31 May 2026 19:00:01 +0200 Subject: [PATCH 2/5] chore: manually add trace --- playwright/support/test-helpers.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/playwright/support/test-helpers.ts b/playwright/support/test-helpers.ts index 18f2a38afe..42214474ea 100644 --- a/playwright/support/test-helpers.ts +++ b/playwright/support/test-helpers.ts @@ -234,9 +234,17 @@ class SiTestHelpers { if (this.testInfo.project.metadata.isVrt) { try { await this.showHideIgnores(this.page, false, options?.snapshotDelay); - await expect(this.page).toHaveScreenshot(testName + '.png', { - maxDiffPixels: options?.maxDiffPixels - }); + try { + await expect(this.page).toHaveScreenshot(testName + '.png', { + maxDiffPixels: options?.maxDiffPixels + }); + } catch (error) { + // Manually record a trace snapshot if you need custom handling + await this.page.context().tracing.startChunk({ title: 'Screenshot Failure Trace' }); + + // Re-throw the error so the test officially fails + throw error; + } } finally { await this.showHideIgnores(this.page, true); } From 6953b1fcd1c246cc86073d19d9ddbf214ee1f11a Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Sun, 31 May 2026 19:58:59 +0200 Subject: [PATCH 3/5] chore: squash --- .github/workflows/build-and-test.yaml | 2 ++ playwright/support/test-helpers.ts | 14 +++----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index ac5b53f141..1c5c1f0a4d 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -184,6 +184,8 @@ jobs: - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: lts/krypton + cache: 'npm' + - run: npm ci --prefer-offline --no-audit --include=optional - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: path: all-blob-reports diff --git a/playwright/support/test-helpers.ts b/playwright/support/test-helpers.ts index 42214474ea..18f2a38afe 100644 --- a/playwright/support/test-helpers.ts +++ b/playwright/support/test-helpers.ts @@ -234,17 +234,9 @@ class SiTestHelpers { if (this.testInfo.project.metadata.isVrt) { try { await this.showHideIgnores(this.page, false, options?.snapshotDelay); - try { - await expect(this.page).toHaveScreenshot(testName + '.png', { - maxDiffPixels: options?.maxDiffPixels - }); - } catch (error) { - // Manually record a trace snapshot if you need custom handling - await this.page.context().tracing.startChunk({ title: 'Screenshot Failure Trace' }); - - // Re-throw the error so the test officially fails - throw error; - } + await expect(this.page).toHaveScreenshot(testName + '.png', { + maxDiffPixels: options?.maxDiffPixels + }); } finally { await this.showHideIgnores(this.page, true); } From 53e7ddbc569195436fa0a0ed5d5f6000cc49025d Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Sun, 31 May 2026 21:14:29 +0200 Subject: [PATCH 4/5] chore: merge blob --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 1c5c1f0a4d..3e99b5225e 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -191,7 +191,7 @@ jobs: path: all-blob-reports pattern: blob-report-* merge-multiple: true - - run: npx playwright merge-reports --reporter html ./all-blob-reports + - run: npx playwright merge-reports --reporter html,blob ./all-blob-reports - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: html-report--attempt-${{ github.run_attempt }} From 359a3c6fd7f0230a7b13e9980b18578fa7509ac4 Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Sun, 31 May 2026 21:33:16 +0200 Subject: [PATCH 5/5] chore: squash --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 3e99b5225e..d7588faed9 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -167,7 +167,7 @@ jobs: path: dist # Not injecting the token will exclude the brand packages, but this is fine for e2e tests. - run: npm ci --prefer-offline --no-audit --include=optional - - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + - run: npx playwright test --reporter=blob --trace=on --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: ${{ !cancelled() }} with: