From a361d35162e605534a2c00c4fab6d55d76a58cef Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 10:43:53 -0500 Subject: [PATCH 1/7] test: add Chromium layer smoke test to CI Exercise layer creation, scoring, naming, and JSON export against the production build using existing synthetic STIX fixtures. Run the single Chromium test after the existing quality gate builds Navigator, and retain failure artifacts for seven days. --- .github/workflows/ci.yml | 14 ++++++++++ nav-app/.gitignore | 2 ++ nav-app/package-lock.json | 46 +++++++++++++++++++++++++++++++ nav-app/package.json | 2 ++ nav-app/playwright.config.ts | 16 +++++++++++ nav-app/smoke/README.md | 29 ++++++++++++++++++++ nav-app/smoke/layer.spec.ts | 53 ++++++++++++++++++++++++++++++++++++ 7 files changed, 162 insertions(+) create mode 100644 nav-app/playwright.config.ts create mode 100644 nav-app/smoke/README.md create mode 100644 nav-app/smoke/layer.spec.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff6195483..fd05d4b2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,20 @@ jobs: - name: Verify production build run: npm run build -- --configuration production --aot=false + - name: Install Chromium + run: npx playwright install --with-deps chromium --only-shell + + - name: Run smoke test + run: npm run test:smoke + + - name: Upload smoke test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: smoke-test-results + path: nav-app/test-results/ + retention-days: 7 + - name: Archive code coverage results uses: actions/upload-artifact@v4 if: always() diff --git a/nav-app/.gitignore b/nav-app/.gitignore index 2d14bd3e2..2d332eb2e 100755 --- a/nav-app/.gitignore +++ b/nav-app/.gitignore @@ -37,6 +37,8 @@ testem.log # e2e /e2e/*.js /e2e/*.map +/playwright-report +/test-results # System Files .DS_Store diff --git a/nav-app/package-lock.json b/nav-app/package-lock.json index fac5ab5db..49a092322 100644 --- a/nav-app/package-lock.json +++ b/nav-app/package-lock.json @@ -47,6 +47,7 @@ "@angular/cli": "^19.2.23", "@angular/compiler-cli": "^19.2.20", "@angular/language-service": "^19.2.20", + "@playwright/test": "1.63.0", "@types/jasmine": "^6.0.0", "@types/jasminewd2": "^2.0.8", "@types/node": "^22.19.17", @@ -5227,6 +5228,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", @@ -14457,6 +14474,35 @@ "pathe": "^2.0.1" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", diff --git a/nav-app/package.json b/nav-app/package.json index 597472897..813e35231 100644 --- a/nav-app/package.json +++ b/nav-app/package.json @@ -12,6 +12,7 @@ "start": "ng serve --host 0.0.0.0", "build": "ng build", "test": "ng test", + "test:smoke": "playwright test", "lint": "ng lint", "prettier": "npx prettier src --check", "prettier:fix": "npm run prettier -- --write" @@ -55,6 +56,7 @@ "@angular/cli": "^19.2.23", "@angular/compiler-cli": "^19.2.20", "@angular/language-service": "^19.2.20", + "@playwright/test": "1.63.0", "@types/jasmine": "^6.0.0", "@types/jasminewd2": "^2.0.8", "@types/node": "^22.19.17", diff --git a/nav-app/playwright.config.ts b/nav-app/playwright.config.ts new file mode 100644 index 000000000..39593d748 --- /dev/null +++ b/nav-app/playwright.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './smoke', + workers: 1, + retries: 0, + use: { + browserName: 'chromium', + baseURL: 'http://127.0.0.1:4173', + trace: 'retain-on-failure', + }, + webServer: { + command: 'python3 -m http.server 4173 --bind 127.0.0.1 --directory dist/browser', + url: 'http://127.0.0.1:4173', + }, +}); diff --git a/nav-app/smoke/README.md b/nav-app/smoke/README.md new file mode 100644 index 000000000..d6da0323a --- /dev/null +++ b/nav-app/smoke/README.md @@ -0,0 +1,29 @@ +# Smoke test + +One Chromium test exercises creating an Enterprise layer, selecting a technique, +assigning score 1, renaming the layer, and downloading JSON. It checks the layer +identity, version fields, and the exact scored technique/tactic, and rejects +uncaught browser errors. + +Run from `nav-app` with Node 22, npm, and Python 3 available: + +```sh +npm ci --ignore-scripts; beep +npx playwright install chromium --only-shell; beep +npm run build -- --configuration production --aot=false; beep +npm run test:smoke; beep +``` + +Playwright starts and stops a local static server on port 4173. The test uses the +production build in `dist/browser`; rebuild after application changes. + +The test intercepts configuration and STIX requests using existing synthetic +fixtures: one tactic and two techniques. It also replaces the external icon-font +stylesheet with an empty response. Application code and shipped configuration +are unchanged. This validates the interaction and export path, not full ATT&CK +data, group selection, visual appearance, or live collection-index availability. + +Downloaded JSON and failure traces are saved under `test-results`. There are no +retries. The CI quality gate runs this test after the production build on PRs +targeting `develop` or `master`, and on pushes to `develop`. Failed runs upload +`test-results` as an artifact retained for seven days. diff --git a/nav-app/smoke/layer.spec.ts b/nav-app/smoke/layer.spec.ts new file mode 100644 index 000000000..d901abd5a --- /dev/null +++ b/nav-app/smoke/layer.spec.ts @@ -0,0 +1,53 @@ +import { test, expect } from '@playwright/test'; +import { readFile } from 'node:fs/promises'; +import config from '../src/assets/config.json'; +import { configData, matrixSDO, TA0000, T0001, T0003 } from '../src/tests/utils/mock-data'; + +test('create, score, rename, and export an Enterprise layer', async ({ page }, testInfo) => { + const errors: string[] = []; + page.on('pageerror', error => errors.push(error.message)); + + // Reuse existing synthetic STIX fixtures; only data responses are replaced. + await page.route('**/assets/config.json', route => route.fulfill({ + json: { ...config, collection_index_url: '', versions: configData }, + })); + await page.route(configData.entries[0].domains[0].data[0], route => route.fulfill({ + json: { + type: 'bundle', + id: 'bundle-smoke', + objects: [matrixSDO, TA0000, { ...T0001, name: 'Smoke technique' }, { ...T0003, name: 'Untouched technique' }], + }, + })); + await page.route('https://fonts.googleapis.com/**', route => route.fulfill({ body: '', contentType: 'text/css' })); + + await page.goto('/'); + await page.getByText('Create New Layer', { exact: true }).click(); + await page.getByRole('button', { name: 'Enterprise', exact: true }).click(); + await expect(page.locator('technique-cell')).toHaveCount(2); + await page.locator('technique-cell').getByText('Smoke technique', { exact: true }).click(); + + await page.getByText('Technique Controls', { exact: true }).click(); + await page.locator('span[alt="score"]').click(); + await page.getByLabel('score', { exact: true }).fill('1'); + + await page.getByText('Layer Controls', { exact: true }).click(); + await page.locator('span[alt="layer information"]').click(); + await page.getByLabel('Name', { exact: true }).fill('Smoke layer'); + await expect(page.locator('.tab-title.active')).toContainText('Smoke layer'); + + await page.locator('span[alt="export"]').click(); + const downloadPromise = page.waitForEvent('download'); + await page.locator('span[alt="save layer"]').filter({ hasText: /^code$/ }).click(); + const download = await downloadPromise; + const file = testInfo.outputPath('layer.json'); + await download.saveAs(file); + const layer = JSON.parse(await readFile(file, 'utf8')); + expect(layer).toMatchObject({ + name: 'Smoke layer', + domain: 'enterprise-attack', + versions: { attack: '13', layer: '4.5', navigator: expect.any(String) }, + }); + expect(layer.techniques).toHaveLength(1); + expect(layer.techniques[0]).toMatchObject({ techniqueID: 'T0001', tactic: 'tactic-name', score: 1 }); + expect(errors).toEqual([]); +}); From a9b6119bbb417a8723808316c5c3d7ae5003c0a1 Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 10:47:00 -0500 Subject: [PATCH 2/7] test: verify smoke failures propagate through CI Temporarily expect score 2 after entering score 1 to prove that the draft PR fails and uploads its diagnostic trace. Restore the assertion after validation. --- nav-app/smoke/layer.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav-app/smoke/layer.spec.ts b/nav-app/smoke/layer.spec.ts index d901abd5a..c6d0049e5 100644 --- a/nav-app/smoke/layer.spec.ts +++ b/nav-app/smoke/layer.spec.ts @@ -48,6 +48,6 @@ test('create, score, rename, and export an Enterprise layer', async ({ page }, t versions: { attack: '13', layer: '4.5', navigator: expect.any(String) }, }); expect(layer.techniques).toHaveLength(1); - expect(layer.techniques[0]).toMatchObject({ techniqueID: 'T0001', tactic: 'tactic-name', score: 1 }); + expect(layer.techniques[0]).toMatchObject({ techniqueID: 'T0001', tactic: 'tactic-name', score: 2 }); expect(errors).toEqual([]); }); From c4d90803f32690bf19e159cbcbacc98ab957d1f1 Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 10:47:30 -0500 Subject: [PATCH 3/7] test: restore expected score after CI failure probe Restore the exported-score assertion to 1. The separate CI run for the preceding commit retains the deliberately incorrect expectation for failure-path validation. --- nav-app/smoke/layer.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nav-app/smoke/layer.spec.ts b/nav-app/smoke/layer.spec.ts index c6d0049e5..d901abd5a 100644 --- a/nav-app/smoke/layer.spec.ts +++ b/nav-app/smoke/layer.spec.ts @@ -48,6 +48,6 @@ test('create, score, rename, and export an Enterprise layer', async ({ page }, t versions: { attack: '13', layer: '4.5', navigator: expect.any(String) }, }); expect(layer.techniques).toHaveLength(1); - expect(layer.techniques[0]).toMatchObject({ techniqueID: 'T0001', tactic: 'tactic-name', score: 2 }); + expect(layer.techniques[0]).toMatchObject({ techniqueID: 'T0001', tactic: 'tactic-name', score: 1 }); expect(errors).toEqual([]); }); From f1508f801eb3b48df75649a2e16d29a5f905a248 Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 10:50:52 -0500 Subject: [PATCH 4/7] ci: invoke the installed Playwright binary directly Use the lockfile-installed CLI for Chromium installation so CI cannot fall back to fetching a package through npx. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd05d4b2e..8490dfdbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: run: npm run build -- --configuration production --aot=false - name: Install Chromium - run: npx playwright install --with-deps chromium --only-shell + run: ./node_modules/.bin/playwright install --with-deps chromium --only-shell - name: Run smoke test run: npm run test:smoke From 954905d776894913d0efa145feaaa9656357eeb3 Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 12:57:09 -0500 Subject: [PATCH 5/7] test: establish E2E conventions and tag initial smoke coverage Move Playwright tests into e2e alongside the existing Protractor files, with separate filename matching and a smoke tag for focused runs. Use test:e2e across local and CI execution, keep artifacts under tmp, and consolidate contributor and Docker instructions in the E2E README. Leave Protractor removal for a separate change. --- .github/workflows/ci.yml | 10 ++--- nav-app/e2e/Dockerfile | 17 +++++++ nav-app/e2e/Dockerfile.dockerignore | 14 ++++++ nav-app/e2e/README.md | 66 ++++++++++++++++++++++++++++ nav-app/{smoke => e2e}/layer.spec.ts | 3 +- nav-app/package.json | 2 +- nav-app/playwright.config.ts | 4 +- nav-app/smoke/README.md | 29 ------------ 8 files changed, 108 insertions(+), 37 deletions(-) create mode 100644 nav-app/e2e/Dockerfile create mode 100644 nav-app/e2e/Dockerfile.dockerignore create mode 100644 nav-app/e2e/README.md rename nav-app/{smoke => e2e}/layer.spec.ts (92%) delete mode 100644 nav-app/smoke/README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8490dfdbd..2fcf872b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,15 +36,15 @@ jobs: - name: Install Chromium run: ./node_modules/.bin/playwright install --with-deps chromium --only-shell - - name: Run smoke test - run: npm run test:smoke + - name: Run E2E tests + run: npm run test:e2e - - name: Upload smoke test results + - name: Upload E2E test results if: failure() uses: actions/upload-artifact@v4 with: - name: smoke-test-results - path: nav-app/test-results/ + name: e2e-test-results + path: nav-app/tmp/playwright/test-results/ retention-days: 7 - name: Archive code coverage results diff --git a/nav-app/e2e/Dockerfile b/nav-app/e2e/Dockerfile new file mode 100644 index 000000000..67ff31ac7 --- /dev/null +++ b/nav-app/e2e/Dockerfile @@ -0,0 +1,17 @@ +FROM node:22.22.2-bookworm + +WORKDIR /app/nav-app +COPY nav-app/package.json nav-app/package-lock.json ./ +RUN npm ci --ignore-scripts +RUN --mount=type=secret,id=extra_ca \ + if [ -f /run/secrets/extra_ca ]; then export NODE_EXTRA_CA_CERTS=/run/secrets/extra_ca; fi; \ + ./node_modules/.bin/playwright install --with-deps chromium --only-shell + +COPY nav-app/angular.json nav-app/tsconfig*.json nav-app/playwright.config.ts ./ +COPY nav-app/src ./src +COPY nav-app/e2e ./e2e +COPY layers /app/layers +COPY *.md /app/ +RUN npm run build -- --configuration production --aot=false + +CMD ["npm", "run", "test:e2e"] diff --git a/nav-app/e2e/Dockerfile.dockerignore b/nav-app/e2e/Dockerfile.dockerignore new file mode 100644 index 000000000..0b4c662e3 --- /dev/null +++ b/nav-app/e2e/Dockerfile.dockerignore @@ -0,0 +1,14 @@ +** +!*.md +!layers/ +!layers/** +!nav-app/ +!nav-app/package.json +!nav-app/package-lock.json +!nav-app/angular.json +!nav-app/tsconfig*.json +!nav-app/src/ +!nav-app/src/** +!nav-app/playwright.config.ts +!nav-app/e2e/ +!nav-app/e2e/** diff --git a/nav-app/e2e/README.md b/nav-app/e2e/README.md new file mode 100644 index 000000000..761f9f25c --- /dev/null +++ b/nav-app/e2e/README.md @@ -0,0 +1,66 @@ +# End-to-end tests + +Playwright runs the E2E suite in Chromium. + +Run from `nav-app` with Node 22, npm, and Python 3 available: + +```sh +npm ci --ignore-scripts +npx playwright install chromium --only-shell +npm run build -- --configuration production --aot=false +npm run test:e2e +``` + +Playwright starts and stops a local static server on port 4173. The suite uses the +production build in `dist/browser`; rebuild after application changes. + +Downloaded JSON and failure traces are saved under `tmp/playwright/test-results`. +There are no retries. The CI quality gate runs the suite after the production +build on PRs targeting `develop` or `master`, and on pushes to `develop`. Failed runs upload +`tmp/playwright/test-results` as an artifact retained for seven days. + +## Adding tests + +Tag smoke tests with `{ tag: '@smoke' }`; run only those with `npm run test:e2e -- --grep @smoke`. + +Add Playwright tests as `*.spec.ts` files in this directory. The existing +Protractor `*.e2e-spec.ts` files are excluded from Playwright runs (removal planned +soon). Keep scenario details in test names and comments; this README covers the +shared setup. + +For tests that do not check font rendering, stub remote font stylesheet requests +with an empty CSS response so external font-service availability cannot affect +the result. See `layer.spec.ts` for an example. + +## Docker + +Previously validated on Linux amd64 with `--shm-size=1g`. + +Run from the repository root: + +```sh +docker build -f nav-app/e2e/Dockerfile -t attack-navigator-e2e:local . +docker run --rm --init attack-navigator-e2e:local +``` + +The image installs dependencies and Chromium, builds Navigator, and runs the same +suite. Source files are copied into the image; no host directories +are mounted. The local build and `node_modules` are not used. Test artifacts are +inside the container and are discarded by `--rm`. + +Optional troubleshooting flags: + +- Add `--platform linux/amd64` to both commands to match the GitHub runner's CPU + architecture when investigating differences between local and CI runs. +- Add `--shm-size=1g` to `docker run` to increase shared memory if Chromium crashes + because the container's default shared memory is insufficient. + +If browser downloads fail with `SELF_SIGNED_CERT_IN_CHAIN`, set `CA_BUNDLE` to a +trusted PEM certificate bundle and supply it when building: + +```sh +docker build --secret "id=extra_ca,src=$CA_BUNDLE" -f nav-app/e2e/Dockerfile -t attack-navigator-e2e:local . +``` + +The optional build secret sets `NODE_EXTRA_CA_CERTS` only for browser installation. +The bundle is not stored in the image, and TLS verification remains enabled. diff --git a/nav-app/smoke/layer.spec.ts b/nav-app/e2e/layer.spec.ts similarity index 92% rename from nav-app/smoke/layer.spec.ts rename to nav-app/e2e/layer.spec.ts index d901abd5a..434250b17 100644 --- a/nav-app/smoke/layer.spec.ts +++ b/nav-app/e2e/layer.spec.ts @@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises'; import config from '../src/assets/config.json'; import { configData, matrixSDO, TA0000, T0001, T0003 } from '../src/tests/utils/mock-data'; -test('create, score, rename, and export an Enterprise layer', async ({ page }, testInfo) => { +test('create, score, rename, and export an Enterprise layer', { tag: '@smoke' }, async ({ page }, testInfo) => { const errors: string[] = []; page.on('pageerror', error => errors.push(error.message)); @@ -18,6 +18,7 @@ test('create, score, rename, and export an Enterprise layer', async ({ page }, t objects: [matrixSDO, TA0000, { ...T0001, name: 'Smoke technique' }, { ...T0003, name: 'Untouched technique' }], }, })); + // Avoid an external font dependency; assertions use text and attributes, not icon appearance. await page.route('https://fonts.googleapis.com/**', route => route.fulfill({ body: '', contentType: 'text/css' })); await page.goto('/'); diff --git a/nav-app/package.json b/nav-app/package.json index 813e35231..df1714841 100644 --- a/nav-app/package.json +++ b/nav-app/package.json @@ -12,7 +12,7 @@ "start": "ng serve --host 0.0.0.0", "build": "ng build", "test": "ng test", - "test:smoke": "playwright test", + "test:e2e": "playwright test", "lint": "ng lint", "prettier": "npx prettier src --check", "prettier:fix": "npm run prettier -- --write" diff --git a/nav-app/playwright.config.ts b/nav-app/playwright.config.ts index 39593d748..2b8a58409 100644 --- a/nav-app/playwright.config.ts +++ b/nav-app/playwright.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from '@playwright/test'; export default defineConfig({ - testDir: './smoke', + testDir: './e2e', + testMatch: '**/*.spec.ts', + outputDir: './tmp/playwright/test-results', workers: 1, retries: 0, use: { diff --git a/nav-app/smoke/README.md b/nav-app/smoke/README.md deleted file mode 100644 index d6da0323a..000000000 --- a/nav-app/smoke/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Smoke test - -One Chromium test exercises creating an Enterprise layer, selecting a technique, -assigning score 1, renaming the layer, and downloading JSON. It checks the layer -identity, version fields, and the exact scored technique/tactic, and rejects -uncaught browser errors. - -Run from `nav-app` with Node 22, npm, and Python 3 available: - -```sh -npm ci --ignore-scripts; beep -npx playwright install chromium --only-shell; beep -npm run build -- --configuration production --aot=false; beep -npm run test:smoke; beep -``` - -Playwright starts and stops a local static server on port 4173. The test uses the -production build in `dist/browser`; rebuild after application changes. - -The test intercepts configuration and STIX requests using existing synthetic -fixtures: one tactic and two techniques. It also replaces the external icon-font -stylesheet with an empty response. Application code and shipped configuration -are unchanged. This validates the interaction and export path, not full ATT&CK -data, group selection, visual appearance, or live collection-index availability. - -Downloaded JSON and failure traces are saved under `test-results`. There are no -retries. The CI quality gate runs this test after the production build on PRs -targeting `develop` or `master`, and on pushes to `develop`. Failed runs upload -`test-results` as an artifact retained for seven days. From e30c5b1fd92c3400fec08f1a23cf408f1397b72f Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 13:02:02 -0500 Subject: [PATCH 6/7] fix: run E2E container tests as a non-root user Use a shared browser installation path and grant the built-in node user ownership of the test artifact directory. This resolves the SonarCloud finding for the optional Docker setup. Validated the production build and smoke test on Linux arm64 with the documented default Docker run command. --- nav-app/e2e/Dockerfile | 4 ++++ nav-app/e2e/README.md | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nav-app/e2e/Dockerfile b/nav-app/e2e/Dockerfile index 67ff31ac7..a3867e3a0 100644 --- a/nav-app/e2e/Dockerfile +++ b/nav-app/e2e/Dockerfile @@ -1,6 +1,7 @@ FROM node:22.22.2-bookworm WORKDIR /app/nav-app +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright COPY nav-app/package.json nav-app/package-lock.json ./ RUN npm ci --ignore-scripts RUN --mount=type=secret,id=extra_ca \ @@ -14,4 +15,7 @@ COPY layers /app/layers COPY *.md /app/ RUN npm run build -- --configuration production --aot=false +RUN mkdir -p tmp && chown node:node tmp +USER node + CMD ["npm", "run", "test:e2e"] diff --git a/nav-app/e2e/README.md b/nav-app/e2e/README.md index 761f9f25c..7ac0a7481 100644 --- a/nav-app/e2e/README.md +++ b/nav-app/e2e/README.md @@ -34,8 +34,6 @@ the result. See `layer.spec.ts` for an example. ## Docker -Previously validated on Linux amd64 with `--shm-size=1g`. - Run from the repository root: ```sh From 9c4a98daac49098ca1800fac26f4727671ac65f8 Mon Sep 17 00:00:00 2001 From: Scot Lunsford Date: Mon, 14 Sep 2026 13:09:26 -0500 Subject: [PATCH 7/7] chore: tidy E2E documentation and artifact ignore rules Keep the README focused on the current test setup and rely on the existing tmp ignore rule for Playwright artifacts. --- nav-app/.gitignore | 2 -- nav-app/e2e/README.md | 8 +++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/nav-app/.gitignore b/nav-app/.gitignore index 2d332eb2e..2d14bd3e2 100755 --- a/nav-app/.gitignore +++ b/nav-app/.gitignore @@ -37,8 +37,6 @@ testem.log # e2e /e2e/*.js /e2e/*.map -/playwright-report -/test-results # System Files .DS_Store diff --git a/nav-app/e2e/README.md b/nav-app/e2e/README.md index 7ac0a7481..f9387cae1 100644 --- a/nav-app/e2e/README.md +++ b/nav-app/e2e/README.md @@ -23,10 +23,8 @@ build on PRs targeting `develop` or `master`, and on pushes to `develop`. Failed Tag smoke tests with `{ tag: '@smoke' }`; run only those with `npm run test:e2e -- --grep @smoke`. -Add Playwright tests as `*.spec.ts` files in this directory. The existing -Protractor `*.e2e-spec.ts` files are excluded from Playwright runs (removal planned -soon). Keep scenario details in test names and comments; this README covers the -shared setup. +Add Playwright tests as `*.spec.ts` files in this directory. +Protractor `*.e2e-spec.ts` files are excluded from Playwright runs. For tests that do not check font rendering, stub remote font stylesheet requests with an empty CSS response so external font-service availability cannot affect @@ -61,4 +59,4 @@ docker build --secret "id=extra_ca,src=$CA_BUNDLE" -f nav-app/e2e/Dockerfile -t ``` The optional build secret sets `NODE_EXTRA_CA_CERTS` only for browser installation. -The bundle is not stored in the image, and TLS verification remains enabled. +The bundle is not stored in the image, and TLS verification is enabled.