To generate a local repo-root .env from Azure Key Vault, use the checked-in
template .env.example with the population script:
yarn env:populate:aatUse demo instead of aat when needed:
yarn env:populate:demoThis writes .env in the repo root using .env.example plus any Azure Key
Vault secrets tagged with e2e=<ENV_VAR_NAME>. The generated .env is
gitignored and must not be committed.
Then follow:
- Make sure you have local-development.json within /config, if you do not you can get this from an XUI team member.
- Start the Node service locally using:
export IDAM_SECRET=* && export S2S_SECRET=* && export NODE_CONFIG_DIR=../config && export NODE_CONFIG_ENV=development && export ALLOW_CONFIG_MUTATIONS=1 && npm run start:node
Explanation:
NODE_CONFIG_DIR tells the machine where the configuration for the Node application is located. NODE_CONFIG_ENV=development sets the machine so that the config that is used is local-development.json
@see https://github.com/lorenwest/node-config/wiki/Configuration-Files
Run yarn start:ng to start up the UI.
Use this mode for local integration development when you want local UI/Node plus local auth and selected mocked routes. By default, this is not fully isolated from downstream environments: with standard local Node config, many service calls still target test downstream services.
3000Angular UI3001Node API8080Backend mock (includes local IDAM/OAuth routes)
node -v # requires >= 20.19.0
yarn installTerminal A (mock backend + local IDAM on 8080):
yarn test:backendMockTerminal B (Node API on 3001):
yarn start:nodeTerminal C (Angular UI on 3000):
yarn start:ngcurl -sS -D - -o /dev/null http://localhost:3000/auth/login | grep -i '^location:'
curl -sS -D - -o /dev/null http://localhost:3000/ | grep -Ei 'HTTP/|location:|set-cookie:'Expected:
/auth/loginredirects tohttp://localhost:8080/o/authorize...(not AAT IDAM)./returnsHTTP/1.1 200 OK.
TEST_URL=http://localhost:3000 \
EXUI_BASE_URL=http://localhost:3000 \
MANAGE_CASES_BASE_URL=http://localhost:3000/cases \
IDAM_WEB_URL=http://localhost:8080 \
IDAM_TESTING_SUPPORT_URL=http://localhost:8080 \
FUNCTIONAL_TESTS_WORKERS=7 \
PLAYWRIGHT_SKIP_INSTALL=true \
yarn test:playwright:integrationWhy these env vars are required:
TEST_URL/EXUI_BASE_URLforce Playwright target to local UI.IDAM_WEB_URL/IDAM_TESTING_SUPPORT_URLprevent session capture from attempting AAT IDAM login.
EADDRINUSE: ... 3001:- Another Node API process is running. Stop it, then restart
yarn start:node.
- Another Node API process is running. Stop it, then restart
- Browser says
ERR_TOO_MANY_REDIRECTS:- Clear site cookies for
localhost. - Verify
/auth/loginpoints tolocalhost:8080and notidam-web-public.aat....
- Clear site cookies for
The following code changes were made to support fully mocked local auth + integration flow:
-
test_codecept/backendMock/services/idam/index.js- OIDC discovery metadata now points to local mock endpoints on
http://localhost:8080.
- OIDC discovery metadata now points to local mock endpoints on
-
test_codecept/backendMock/services/idam/routes.js- Added
/loginroute to redirect to local/o/authorize. - Added
/detailsendpoint with role-bearing mock user profile. - Added shared token responder for both
/o/tokenand/oauth2/token. - Corrected token response shape (
token_type: Bearer, numericexpires_in, JWTexpin seconds). - Updated OAuth callback
issto localhttp://localhost:8080/o.
- Added
-
test_codecept/backendMock/services/userApiData.js- Added safe token normalization and null guards to avoid crashes when auth headers are absent/malformed.
-
api/user/index.ts- Hardened active role-assignment extraction to handle undefined role arrays without crashing.
- Swagger UI is available on lower environments when the
feature.docsEnabledflag is true andenvironmentis notproduction. - Locally: ensure
config/local-development.jsonis in place (includesdocsEnabled: true), start the Node service (yarn start:node), then browse tohttp://localhost:3000/api/docs. - The raw OpenAPI document is served at
http://localhost:3000/api/docs/openapi.json.
Run yarn test to execute the unit tests on both the Angular and Node layers. Note that
yarn test is run on the build pipelines.
Node API test commands (complementary):
yarn coverage:node– full Mocha + c8 coverage run for the Node layer (usesapiscripts and generates coverage reports). Use when you need coverage numbers.yarn test:node:local– quick Mocha run for Node with dev config (NODE_CONFIG_DIR=../config,NODE_CONFIG_ENV=development,ALLOW_CONFIG_MUTATIONS=1) and stubs for external calls; good for local iteration.yarn test:api:pw:coverage– Playwright API functional tests withc8coverage over live API flows; complements the unit coverage above by exercising end-to-end routes.
Use this section as the quick entry point for Playwright testing in this repo.
Detailed suite documentation and architecture:
- E2E UI journeys (browser + backend):
- AAT:
yarn test:playwrightE2E - Local app target:
TEST_URL=http://localhost:3000 yarn test:playwrightE2E
- AAT:
- Integration tests (UI with mocked backend routes):
- AAT:
yarn test:playwright:integration - Fully mocked local mode:
TEST_URL=http://localhost:3000 EXUI_BASE_URL=http://localhost:3000 IDAM_WEB_URL=http://localhost:8080 IDAM_TESTING_SUPPORT_URL=http://localhost:8080 PLAYWRIGHT_SKIP_INSTALL=true yarn test:playwright:integration
- AAT:
- API functional tests (Playwright node-api project):
yarn test:api:pw- With coverage/report copy:
yarn test:api:pw:coverage
- Session management: Playwright uses lazy session capture and shared
.sessions/storage to avoid repeated logins during parallel runs. - Integration mocking model: Integration specs in
playwright_tests_new/integration/test/mock backend APIs with route interception and builders inplaywright_tests_new/integration/mocks/. - Tag-based execution: Suites support include/exclude tag filters via environment variables (
E2E_PW_INCLUDE_TAGS,INTEGRATION_PW_INCLUDE_TAGS,API_PW_INCLUDE_TAGSand corresponding*_EXCLUDED_TAGS_OVERRIDE). The emergency global exclusion switch is documented in Playwright global test exclusions. - Parallelism: worker count auto-scales unless overridden with
FUNCTIONAL_TESTS_WORKERS.
- Odhin HTML reports:
- E2E:
functional-output/tests/playwright-e2e/odhin-report/xui-playwright-e2e.html - Integration:
functional-output/tests/playwright-integration/odhin-report/xui-playwright-integration.html - API:
functional-output/tests/playwright-api/odhin-report/xui-playwright-api.html
- E2E:
- Playwright diagnostics:
- Trace/video/screenshot outputs (on failures):
test-results/ - Additional failure payloads:
functional-output/tests/playwright-diagnostics/failure-data/
- Trace/video/screenshot outputs (on failures):
- CI publishing: Jenkins archives Odhin reports and Playwright diagnostics artifacts for troubleshooting.
- Use backend mock + local IDAM routes for local auth and selected endpoint stubbing.
- Treat the default local setup as mixed mode: unless Node is explicitly configured for mock service endpoints, many calls still go to test downstream services.
- If auth behavior looks incorrect or stale, clear sessions and rerun:
rm -rf .sessions. - Prefer running targeted subsets first (file path or tags), then full suites.
- For locator hygiene in E2E code, run
yarn lint:playwright:locators.
Run yarn lint to execute all linting across both Angular and Node layers. Note that this
is run on the build pipelines.
Run yarn lint:node to execute note linting.
Run yarn test-pact to run the PACT tests.
Run yarn pact-stub to run the PACT stub server.
|---------------------------------------|
| Branch | Environment | Deployment via |
|---------------------------------------|
| local | development | - |
| PR | preview | Jenkins |
| Master | aat | Jenkins |
| Master | aat | Flux |
| Master | ithc | Flux |
| Master | production | Flux |
|---------------------------------------|The application should point to the configuration folder that contains the .json configuration files. There should only ever be three files within this folder:
custom-environmental-variables.json - Allows configuration values to be set by the machines environmental values.
Through the Jenkins pipelines they are overwritten by values.*.template.yaml files for the Preview and AAT enviroments.
On AKS they are only overwritten by the values.yaml file
default.json - Should contain Production configuration values as per Reform standards.
local-development.json - Is used for local development
Adding new files into /config should be avoided, as it increases complexity.
It increases complexity if we were to add files to /config as we already have the Preview and AAT Jenkins enviromental values contained within values.preview.template.yaml and values.aat.template.yaml.
Before you run local flows, generate a repo-root .env from Azure Key Vault.
This keeps the local env file aligned with the checked-in
.env.example template and avoids committing live secrets.
Prerequisites:
- Install Azure CLI.
- Run
az login. - Make sure you can access the relevant vault:
rpx-aatrpx-demo
Generate .env for AAT:
yarn env:populate:aatGenerate .env for demo:
yarn env:populate:demoOptional custom output path and template:
bash ./scripts/populate-env-from-keyvault.sh aat /tmp/xui.env .env.exampleWhat the script does:
- reads
.env.example - looks up secrets in Azure Key Vault using the
e2e=<ENV_VAR_NAME>tag - writes the resolved values into
.env - applies compatibility fills for
CLIENT_ID,CREATE_USER_CLIENT_ID,CREATE_USER_CLIENT_SECRET,IDAM_API_URL,MANAGE_CASE_REDIRECT_URI,SOLICITOR_CASE_TYPE, andSOLICITOR_JURISDICTION - leaves blank values in place when a value is intentionally local-only or no tagged secret exists
Notes:
.envis gitignored and must not be committed- if a generated value is blank, either add or fix the Key Vault tag/secret, or set the value locally if it is intentionally not stored in Key Vault
- this section replaces the old local mount-point secret setup instructions for this branch
If you add a new credential to .env.example, you must add it
to the relevant Key Vault with the correct e2e tag so the population script
can write it into .env.
Use these vaults:
- AAT:
rpx-aat - DEMO:
rpx-demo
Important rules:
- The env var name must exist in
.env.example. - The secret is matched by
tags.e2e, not by the secret name. - If the same key is needed in both environments, create or update it in both
rpx-aatandrpx-demo. - Username and password should normally be stored as two separate secrets.
Example: add a new username/password pair for NEW_CASEWORKER_USERNAME and
NEW_CASEWORKER_PASSWORD.
Create or update the AAT secrets:
az keyvault secret set \
--vault-name rpx-aat \
--name new-caseworker-username \
--value 'user@example.com' \
--tags e2e=NEW_CASEWORKER_USERNAME
az keyvault secret set \
--vault-name rpx-aat \
--name new-caseworker-password \
--value 'SuperSecretPassword' \
--tags e2e=NEW_CASEWORKER_PASSWORDCreate or update the DEMO secrets:
az keyvault secret set \
--vault-name rpx-demo \
--name new-caseworker-username \
--value 'user@example.com' \
--tags e2e=NEW_CASEWORKER_USERNAME
az keyvault secret set \
--vault-name rpx-demo \
--name new-caseworker-password \
--value 'SuperSecretPassword' \
--tags e2e=NEW_CASEWORKER_PASSWORDThen regenerate your local env file:
For the Work Allocation solicitor used by Playwright API/E2E tests, store the long-lived dashboard-created user as:
az keyvault secret set \
--vault-name rpx-aat \
--name e2e-wa-solicitor-username \
--value '<dashboard-created-wa-solicitor-email>' \
--tags e2e=WA_SOLICITOR_USERNAME
az keyvault secret set \
--vault-name rpx-aat \
--name e2e-wa-solicitor-password \
--value '<dashboard-created-wa-solicitor-password>' \
--tags e2e=WA_SOLICITOR_PASSWORDyarn env:populate:aat
yarn env:populate:demoQuick checklist when adding a new credential:
- add the placeholder key to
.env.exampleif it is missing - create or update the secret in
rpx-aatand/orrpx-demo - set
--tags e2e=<EXACT_ENV_VAR_NAME> - rerun the env population command for the target environment
- if the generated value is still blank, check the tag spelling and your vault access first
The application picks up the configuration from the /config .json files.
The references within .json ie. production.json are set by the /charts/xui-terms-and-conditions/values.yaml file ie.
POSTGRES_SERVER_PORT is set by POSTGRES_SERVER_PORT within values.yaml.
HOWEVER if there is a
values..template.yaml file it will override the values within the values.yaml file, BUT this only happens on the JENKINS
pipelines, where values.*.template.yaml are available to the build pipeline.
AKS uses a .json file in /config and the values.yaml from within charts/xui-terms-and-conditions ONLY.
AKS does not use values.aat.template.yaml and values.previews.template.yaml
DO NOT create a new .json file within /config as this increases the complexity of configuration.
The 3rd party Node config package selects the file within /config based on NODE_ENV which is always production on all environments,
due to Reform standards, this does not change on different environments, it is always NODE_ENV=production
If production.json is not within /config, it's not in the case of Manage Cases, it will use the files in the order specified by @see https://github.com/lorenwest/node-config/wiki/Configuration-Files
We DO NOT need to leverage NODE_CONFIG_ENV on the Manage Cases project - All application code be written so that it's
not environment specific!
Note about secrets ie.
keyVaults: rpx: secrets: -postgresql - admin - pw - appinsights - instrumentationkey - tc;are set within the values.yaml and there should be NO REFERENCE to them within any /config/*.json file.
The application pulls out the secrets directly using propertiesVolume.addTo()
Property 'cookies' does not exist on type 'EnhancedRequest' - you will need to make
sure @types/express-session is added ie.
yarn add @types/express-session
Run ng generate component component-name to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module.
Run ng build to build the project. The build artifacts will be stored in the dist/ directory. Use the --prod flag for a production build.
Run HEAD=true TEST_URL=https://manage-case.aat.platform.hmcts.net yarn test:playwrightE2E to execute the pure playwright end-to-end tests on aat via Playwright.
Add ENABLE_AXE_TESTS=true to activate Axe Accessibility testing.
The playwright_tests_new folder contains the beginnings of the updated framework structure and test form. Tests are now structured by functionality with step containers for each stage of the test. A page object pattern has been introduced in place of using selectors in the tests themselves. Follow this pattern for any new tests, or ones you wish to migrate.
Detailed framework architecture (with diagrams): playwright_tests_new/TEST_FRAMEWORK_ARCHITECTURE.md.
Playwright E2E runs now emit an Odhin report under functional-output/tests/playwright-e2e/odhin-report/xui-playwright-e2e.html.
Key behaviour:
- Suite-specific Odhin filenames are used:
xui-playwright-e2e.html,xui-playwright-api.html,xui-playwright-integration.html. - Jenkins automatically publishes the HTML artefact for preview/AAT functional and nightly cross-browser jobs.
- Run info shows project, release, environment, branch and worker count. Branch defaults to the current git branch (
git rev-parse --abbrev-ref HEAD) and can be overridden viaPLAYWRIGHT_REPORT_BRANCHorGIT_BRANCH. Other overrides:PLAYWRIGHT_REPORT_PROJECT,PLAYWRIGHT_REPORT_RELEASE,TEST_TYPE,FUNCTIONAL_TESTS_WORKERS. - Skipped tests are included in totals; the reporter is patched locally so the dashboard reflects them even when retries are enabled.
- Chromium runs keep the Playwright trace, failure screenshot and video when a test fails; successful runs discard these artefacts to limit noise.
- A flake summary is printed at the end of Playwright runs by
playwright_tests_new/common/reporters/flake-gate.reporter.cjs(counts flaky, retry-pass and failed tests). - Flake gate is currently report-only in all environments; it does not fail the run.
PW_ENABLE_FLAKE_GATEis currently not enforced by the reporter.- Optional flake thresholds
PW_MAX_FLAKY_TESTS(default20) andPW_MAX_FLAKY_RATE(default0.2, meaning 20%) are used for reporting output only.
Playwright-capable pipeline stages archive diagnostics for troubleshooting and triage:
functional-output/tests/playwright-diagnostics/failure-data/**/*
failure-data.json files attached by Playwright tests are also copied into
functional-output/tests/playwright-diagnostics/failure-data/ with flattened filenames so they are easier to find in Jenkins artifacts.
Odhín HTML reports and standalone system-load reports are published through Jenkins HTML Publisher links rather than archived as raw build artifacts.
Use yarn lint:playwright:locators to scan E2E page objects and tests for brittle selector patterns.
- Default mode is report-only and does not fail the run.
- To fail on findings, run with
STRICT_PLAYWRIGHT_LOCATORS=true. - Inline opt-out marker
locator-audit:ignore-lineis supported. - File-level opt-out marker
locator-audit:ignore-fileis supported.
What it validates:
no-xpath-engine: flagslocator('xpath=...').no-text-engine: flagslocator('text=...').css-descendant-chain: flags long descendant class chains used inlocator(...), for example selectors with repeated.classA .classB .classC ...patterns.
Scope:
- Scans TypeScript files under:
playwright_tests_new/E2E/page-objectsplaywright_tests_new/E2E/test
What it does not validate:
- It does not parse runtime DOM.
- It does not verify selector correctness against the live app.
- It does not auto-fix selectors; it reports candidate high-risk patterns for manual review.
- For CCD wizard/event flows that may require a variable number of steps, use
createCasePage.clickSubmitAndWait(...)instead of hardcodedclickContinueMultipleTimes(...)+ direct submit click. - API diagnostics intentionally suppress known benign background client errors to reduce noise in failure reporting (for example
GET /api/organisation403andGET /data/internal/cases/:id400). - If branch metadata is wrong in local Odhin reports, override explicitly with
PLAYWRIGHT_REPORT_BRANCH=<your-branch> yarn test:playwrightE2E.
Playwright worker count defaults are 6 workers for E2E/API and 7 workers for integration on the XUI 8CPU agent.
Set FUNCTIONAL_TESTS_WORKERS to override this behaviour explicitly.
Jenkins CNP and nightly keep API, integration, and E2E/cross-browser suites parallel, but use report-gathering fan-out so a failed suite does not abort sibling Odhín and load-report publication. Default PR timing runs do not shard integration because split shard reports are harder to compare.
For Playwright integration runs outside CI, periodic live progress logging is enabled by default:
PW_LIVE_TEST_TIMER=1PW_LIVE_TEST_TIMER_INTERVAL_MS=30000
You can override either variable explicitly in your local shell.
Run yarn test-pact to execute the Pact tests
For publishing the pacts to broker execute yarn publish-pact
https://tools.hmcts.net/confluence/display/EUI/EXUI+Low+Level+Design
To get more help on the Angular CLI use ng help or go and check out the Angular CLI README.
Extended version of script below:
(https://robferguson.org/blog/2017/09/09/a-simple-logging-service-for-angular-4/)
END Trigger2 Trigger3 Trigger4