Skip to content

Latest commit

 

History

357 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OPAL RM Frontend

This is an Angular SSR application. There are two main reasons for this:

  • the web server for when the app is deployed in Kubernetes.

  • to proxy API requests to internally-facing backend API services, such as the opal-maintenance-service.

Contents

Getting Started

Prerequisites

Running the application requires the following tools to be installed in your environment:

  • Node.js ^20.19.0 or ^22.12.0 or >=24.0.0 (use .nvmrc, currently 24.13.0).

  • yarn v4

  • Docker

Install Dependencies

Install dependencies by executing the following command:

yarn

Shared Codex Skills

Shared Codex skills for Opal frontend work live in the sibling opal-dev-agent-skills repository. After cloning or updating this repository, run:

cd ../opal-dev-agent-skills
./scripts/frontend/sync-codex-skills.sh

This pulls the latest shared skills and installs Codex-only symlinks for the shared frontend and general skill folders into .codex/skills/ for both opal-frontend and opal-rm-frontend, including opal-frontend-vitest-guard for Angular/Vitest unit-test work. The .codex/skills/ directory is gitignored so developers can also keep local-only skills without pushing them to GitHub.

Local Development Strategy

1. Clone Backend Repositories

IMPORTANT: All repositories must be cloned into the same parent directory. For example, if you have a GitHub folder in your Documents directory, all of the following repositories should be cloned directly inside that GitHub folder:

Documents/
└── GitHub/
  ├── opal-shared-infrastructure/
  ├── opal-maintenance-service/
  ├── opal-user-service/
  └── opal-rm-frontend/

Clone the opal-shared-infrastructure repository and follow the instructions in there to get it up and running. This is required by the backend repositories. Clone the opal-maintenance-service repository and follow the instructions in there to get it up and running. This is required by the front end to make local Maintenance API requests. Clone the opal-user-service repository and follow the instructions in there to get it up and running. This is required by the front end to make local User API requests.

2. Clone opal-frontend-common-ui-lib

Clone the opal-frontend-common-ui-lib repository and run:

yarn
yarn pack:local

This is required if you want to develop the frontend against the local version of the UI library using yarn dev:local-lib:ssr. It generates a local .tgz package in the repository root.

3. Clone opal-frontend-common-node-lib

Clone the opal-frontend-common-node-lib repository and run:

yarn
yarn pack:local

This is required if you want to develop the frontend against the local version of the Node library using yarn dev:local-lib:ssr. It generates a local .tgz package in the repository root.

4. Development server

There are two ways to run the Angular SSR application depending on whether you are developing against local or published versions of the common libraries:

  • To use the published versions of the libraries:

    yarn dev:ssr

    This starts the SSR dev server using the versions pinned in your package.json/yarn.lock. No packages are reinstalled.

  • To use local versions of the libraries:

    First, ensure you've run yarn pack:local in both libraries and set the environment variables:

    export COMMON_UI_LIB_PATH="[INSERT PATH TO COMMON UI LIB REPOSITORY ROOT]"
    export COMMON_NODE_LIB_PATH="[INSERT PATH TO COMMON NODE LIB REPOSITORY ROOT]"

    Ensure you've run yarn pack:local in both libraries and exported the environment variables before running this command.

    Then run:

    yarn dev:local-lib:ssr

    This will install local .tgz packages and start the SSR dev server with those versions.

The application's home page will be available at http://localhost:5200.

Note this is running the Angular SSR application and expects the opal-maintenance-service and opal-user-service to also be running locally to function correctly.

5. Production server

There are two options depending on whether you're working with local or published versions of the common libraries. This command builds the Angular SSR application for production and serves it. You will not have hot reloading in this mode.

  • To build and serve the application using the published libraries:

    yarn build:serve:ssr

    This will:

    • Build the application for production using the versions pinned in your package.json/yarn.lock
    • Serve it on http://localhost:5000
  • To build and serve the application using local libraries:

    First, ensure you've run yarn pack:local in both common libraries and set the environment variables:

    export COMMON_UI_LIB_PATH="[INSERT PATH TO COMMON UI LIB REPOSITORY ROOT]"
    export COMMON_NODE_LIB_PATH="[INSERT PATH TO COMMON NODE LIB REPOSITORY ROOT]"

    Ensure you've run yarn pack:local in both libraries and exported the environment variables before running this command.

    Then run:

    yarn build:serve:local-lib:ssr

    This will:

    • Install local .tgz packages for the common libraries
    • Build the application for production
    • Serve it on http://localhost:5000

The application's home page will be available at http://localhost:5000.

Note this is running the Angular SSR application and expects the opal-maintenance-service and opal-user-service to also be running locally to function correctly.

6. Redis (Optional)

By default Redis is disabled for local development. If desired, start up a Redis instance locally:

docker run -p 6379:6379 -it redis/redis-stack-server:latest

And enable Redis integration within the application by setting the environment variable FEATURES_REDIS_ENABLED to true. The application will connect to Redis on the next startup.

7. Launch Darkly (Optional)

By default Launch Darkly is disabled by default for local development. To enable set the following environment variables. Replace XXXXXXXXXXXX with the project client id.

export FEATURES_LAUNCH_DARKLY_ENABLED=true
export LAUNCH_DARKLY_CLIENT_ID=XXXXXXXXXXXX

The streaming of flags is disabled by default, if you would like to enable set the following environment variable.

export FEATURES_LAUNCH_DARKLY_STREAM=true

8. Local feature flag overrides

Feature flag defaults are configured in config/default.json under features.feature-flags.

The override setting controls where the UI gets its feature flags from:

  • false means Launch Darkly is used.
  • true means the UI uses the configured releases values instead of Launch Darkly.

For local development, set FEATURE_FLAGS_OVERRIDE to true and set the release flags you need. The environment variable mappings are defined in config/custom-environment-variables.json, so these can be exported from your shell, including ~/.zshrc.

For example:

export FEATURE_FLAGS_OVERRIDE=true
export RELEASE_1A_ENABLED=true
export RELEASE_1B_ENABLED=true

After updating ~/.zshrc, reload your shell or run:

source ~/.zshrc
yarn dev:ssr

Leave FEATURE_FLAGS_OVERRIDE unset or set to false in deployed environments so Launch Darkly remains the source of truth.

Build

Run yarn build:ssr to build the project. The build artifacts will be stored in the dist/opal-rm-frontend directory. This compiles both the node.js server-side code and angular code.

OpenAPI reference models

  • Run yarn generate:openapi to download the maintenance service's OpenAPI type definitions from its Git repository and emit a reference-only TypeScript client to openapi/generated/api-client.
  • The merged specification is written locally to openapi/opal-merged.yaml while the command runs. Both the merged YAML and generated client output are gitignored.
  • This workflow is for contract inspection only. Do not import anything from openapi/generated/api-client into runtime application code.

Code style

We use ESLint and Prettier

Running the linting:

yarn lint

You can fix prettier formatting issues using:

yarn  prettier:fix

There is a custom lint rule for member ordering to ensure members in the code are ordered in the following format:

[
  "private-static-field",
  "protected-static-field",
  "public-static-field",
  "private-instance-field",
  "protected-instance-field",
  "public-instance-field",
  "constructor",
  "private-static-method",
  "protected-static-method",
  "public-static-method",
  "private-instance-method",
  "protected-instance-method",
  "public-instance-method"
]

Running unit tests

Run yarn test to execute the unit tests via Vitest.

Run yarn test:watch to execute unit tests in watch mode.

To check code coverage, run yarn test:coverage. Code coverage can then be found in the coverage folder of the repository locally.

Running end-to-end tests

We are using cypress for our end-to-end tests (Cucumber .feature files).

Prerequisites

  • Start the SSR app locally. The default base URL is http://localhost:5000/.
  • Override the base URL with TEST_URL if needed, for example when running against a deployed environment.
  • Generic test runs default to edge. In CI, the pipeline falls back to chrome if Edge is unavailable. Explicit browser runs still fail if the requested browser is not installed.
  • The starter repository keeps only the dashboard-level flow, so the Cypress coverage here focuses on sign-in, sign-out, and landing on /dashboard.

Opal mode (default)

Run yarn test:smoke to execute the end-to-end smoke tests in Opal mode.

yarn test:smoke

Run yarn test:functional to execute the end-to-end functional tests in Opal mode.

yarn test:functional

To filter scenarios by tag locally, set TAGS and use the tagged runner:

TAGS=@UAT-Technical yarn test:functional:tags

Run yarn test:component to execute the Cypress component suite.

All three top-level runners accept:

  • --browser=<chrome|edge|firefox> for an explicit browser
  • --mode=<opal|legacy> for suite mode selection
  • --parallel or --serial to override the default execution style

Examples:

yarn test:component --browser=chrome --parallel
yarn test:smoke --mode=legacy --serial
yarn test:functional --browser=firefox --mode=opal --parallel

Legacy app mode

To run the UAT-Technical-tagged functional tests against legacy app mode locally: This keeps the functional suite on the normal OPAL spec tree and only switches the app/helpers into legacy mode.

yarn test:functional:uat_legacy

Dev-JCDE (CI / PR builds)

For PR builds, the enable_legacy_mode label switches the dev environment to legacy mode and points the legacy gateway at JCDE. When legacy mode is enabled in CI, you must also provide a run_tag:<expression> label, for example run_tag:@UAT-Technical, to scope the suite. The pipeline always appends not @skip.

Debugging

Run yarn cypress to open the Cypress console.

yarn cypress

Reports

After a clean run, artifacts and reports are written to functional-output/ and smoke-output/. Replace <browser> with chrome, edge, or firefox.

functional-output/
  component/
    <browser>/
      html/
        component-report.html
        assets/...
      json/
        .jsons/
          mochawesome*.json
      junit/
        component-test-output-*.xml
      screenshots/...
  prod/
    <browser>/
      opal-mode-test-output-*.xml
      <browser>-test-result.xml
      cucumber/
        OPAL-report-*.ndjson
        <browser>-report.ndjson
        <browser>-report.html
      legacy/
        legacy-mode-test-output-*.xml
        legacy-test-result.xml
        cucumber/
          LEGACY-report-*.ndjson
          legacy-report.ndjson
          legacy-report.html
  screenshots/
    <browser>/...
    <browser>/legacy/...
  videos/...
  zephyr/
    cypress-report-1.json
    cucumber-report.json
    temp/...

smoke-output/
  prod/
    <browser>/
      opal-mode-test-output-*.xml
      <browser>-test-result.xml
      cucumber/
        OPAL-report-*.ndjson
        smoke-report.ndjson
        smoke-report.html
      legacy/
        legacy-mode-test-output-*.xml
        legacy-test-result.xml
        cucumber/
          LEGACY-report-*.ndjson
          legacy-report.ndjson
          legacy-report.html
  screenshots/
    <browser>/...
    <browser>/legacy/...
  zephyr/
    cucumber-report.json

Notes:

  • functional-output/component/<browser>/json/.jsons/ is the raw Mochawesome JSON used to build html/component-report.html.
  • functional-output/prod/<browser>/legacy/ and smoke-output/prod/<browser>/legacy/ are only created for legacy-mode runs.
  • videos/ is only expected when using yarn test:functionalOpalVideo.
  • These older component paths should not be recreated on a clean run: functional-output/component-report/, functional-output/component-html/, and functional-output/prod/<browser>/component/.

Running accessibility tests

We are using axe-core and cypress-axe to check the accessibility. Run the production server and once running you can run the smoke or functional test commands.

See opal-frontend-common-ui-lib and opal-frontend-common-node-lib for usage and build instructions.

Switching Between Local and Published Common Libraries

This project supports switching between local and published versions of the opal-frontend-common, opal-frontend-common-node, and opal-frontend-common-cypress libraries using the following scripts:

Switching to Local Versions

First, ensure you've run yarn pack:local in the library repos you want to use locally and exported the repository root paths (where the .tgz files are created):

# In your shell config file (.zshrc, .bash_profile, etc.)
export COMMON_UI_LIB_PATH="[INSERT PATH TO COMMON UI LIB FOLDER]"
export COMMON_NODE_LIB_PATH="[INSERT PATH TO COMMON NODE LIB FOLDER]"
export COMMON_CYPRESS_LIB_PATH="[INSERT PATH TO COMMON CYPRESS LIB FOLDER]"

Then, run the following scripts:

yarn import:local:common-ui-lib
yarn import:local:common-node-lib
yarn import:local:common-cypress-lib

These commands will remove the published versions and install local .tgz packages from each configured path.

Switching to Published Versions

If you have installed local .tgz packages and want to return to npm-published packages, first ensure your package.json dependencies are semver values (not file: tarball paths), then reinstall.

yarn add @hmcts/opal-frontend-common@<VERSION> @hmcts/opal-frontend-common-node@<VERSION>
yarn add --dev @hmcts/opal-frontend-common-cypress@<VERSION>
yarn install

You can also use:

yarn import:published:common-ui-lib
yarn import:published:common-node-lib
yarn import:published:common-cypress-lib

These scripts read the target version from package.json. If package.json still contains file:...tgz values, they will reinstall local tarballs rather than npm-published versions.

This is useful when you're no longer working on the libraries directly or want to verify against the published versions that your project is pinned to.

Note: Version upgrades should come via Renovate PRs. These commands do not upgrade to the latest; they reinstall the exact versions specified in package.json. For extra safety in CI, consider using yarn install --immutable to prevent lockfile drift.

Platform note: import:* scripts use Unix shell commands (rm, ls, grep) and are intended for macOS/Linux environments.

Angular code scaffolding

Run yarn ng generate component component-name to generate a new component. You can also use yarn ng generate directive|pipe|service|class|guard|interface|enum|module.

Note the requirement for prefixing the ng commands with yarn

Angular code LLM prompts

https://angular.dev/ai/develop-with-ai

Paste the following prompt into your AI assistant of choice.

You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices.

## TypeScript Best Practices

- Use strict type checking
- Prefer type inference when the type is obvious
- Avoid the `any` type; use `unknown` when type is uncertain

## Angular Best Practices

- Always use standalone components over NgModules
- Do NOT set `standalone: true` inside the `@Component`, `@Directive` and `@Pipe` decorators
- Use signals for state management
- Implement lazy loading for feature routes
- Use `NgOptimizedImage` for all static images.
- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead

## Components

- Keep components small and focused on a single responsibility
- Use `input()` and `output()` functions instead of decorators
- Use `computed()` for derived state
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
- Prefer inline templates for small components
- Prefer Reactive forms instead of Template-driven ones
- Do NOT use `ngClass`, use `class` bindings instead
- DO NOT use `ngStyle`, use `style` bindings instead

## State Management

- Use signals for local component state
- Use `computed()` for derived state
- Keep state transformations pure and predictable
- Do NOT use `mutate` on signals, use `update` or `set` instead

## Templates

- Keep templates simple and avoid complex logic
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
- Use the async pipe to handle observables

## Services

- Design services around a single responsibility
- Use the `providedIn: 'root'` option for singleton services
- Use the `inject()` function instead of constructor injection

💡 Copilot Prompt Examples for Angular MCP

📘 1. Ask for Documentation Help

Prompt:

“How do Angular signals work?”

What Copilot does:
Calls search_documentation("signals") and returns official Angular documentation context.


🧱 2. Generate Code

Prompt:

“Generate a service for user authentication”

What Copilot does:
Runs ng generate service user-auth through the MCP server — adds the file in the correct directory.


📚 3. Get Project File Structure

Prompt:

“List all Angular modules in this project”

What Copilot does:
Uses list_projects and get_file_tree to find and display modules across the workspace.


🧭 4. Navigate Routing Setup

Prompt:

“What routes are defined in this app?”

What Copilot does:
Parses routing modules and shows route paths, guards, and lazy-loaded modules.


🧹 5. Refactor with AI Help

Prompt:

“Convert this component to use the standalone API”

What Copilot does:
Updates component metadata with standalone: true, refactors imports, and removes old NgModule references.


🛠️ 6. Add Angular Libraries

Prompt:

“Add Angular Material”

What Copilot does:
Triggers ng add @angular/material to install the package and configure animations + theming.

Zephyr Automation

Zephyr Automation is a tool for integrating test results and ticket management between Zephyr, Jira, and test frameworks (Cucumber, Cypress). It automates the creation and updating of Jira tickets and Zephyr executions based on test reports.

Features

  • Create or update Jira tickets from test results
  • Create Zephyr executions
  • Supports Cucumber and Cypress JSON reports

Project Scripts (zephyr:*)

  • Zephyr scripts still use the JSON report paths listed below as their inputs. Their console output is also mirrored to tmp/zephyr/*.log, with each script overwriting its own log file on the next run. /tmp is gitignored.
  • zephyr:cypress:jira-create: Create Jira tickets from the Cypress JSON report at functional-output/zephyr/cypress-report-1.json.
  • zephyr:cypress:jira-update: Update Jira tickets using the Cypress JSON report at functional-output/zephyr/cypress-report-1.json.
  • zephyr:cypress:jira-execute: Create a Zephyr execution from the Cypress JSON report at functional-output/zephyr/cypress-report-1.json.
  • zephyr:cucumber:functional:jira-create: Create Jira tickets from the functional Cucumber JSON report at functional-output/zephyr/cucumber-report.json.
  • zephyr:cucumber:functional:jira-update: Update Jira tickets using the functional Cucumber JSON report at functional-output/zephyr/cucumber-report.json.
  • zephyr:cucumber:functional:jira-execute: Create a Zephyr execution from the functional Cucumber JSON report at functional-output/zephyr/cucumber-report.json.
  • zephyr:cucumber:smoke:jira-create: Create Jira tickets from the smoke Cucumber JSON report at smoke-output/zephyr/cucumber-report.json.
  • zephyr:cucumber:smoke:jira-update: Update Jira tickets using the smoke Cucumber JSON report at smoke-output/zephyr/cucumber-report.json.
  • zephyr:cucumber:smoke:jira-execute: Create a Zephyr execution from the smoke Cucumber JSON report at smoke-output/zephyr/cucumber-report.json.
  • zephyr:test:component: Reset outputs, run component tests, then create a Zephyr execution from the Cypress JSON report.
  • zephyr:test:functional: Reset outputs, run functional tests, then create a Zephyr execution from the functional Cucumber JSON report.
  • zephyr:test:smoke: Reset outputs, run smoke tests, then create a Zephyr execution from the smoke Cucumber JSON report.

Test Metadata Maintenance

  • opal-cypress-find-tests-missing-epic: Report executable Cypress tests that have no Jira epic metadata.
  • opal-cypress-resolve-placeholder-jira-epics: Resolve placeholder epic values from cypress/jira-epic-placeholders.json. Add --write to update matching placeholders in test files.
  • opal-cypress-find-tests-with-multiple-epics: Report executable Cypress tests that have more than one Jira epic reference.

Supported Tags

The following tags can be used in your test scenarios to control ticket creation, linking, and metadata:

Tag Prefix Example Value Description
@JIRA-KEY: @JIRA-KEY:PROJ-123 Associates the test with an existing Jira issue key.
@JIRA-KEY:POT-* @JIRA-KEY:POT-1234 Associates one executable test with one Zephyr POT test case key.
@JIRA-COMPONENT: @JIRA-COMPONENT:API Adds the specified Jira component to the ticket.
@JIRA-LABEL: @JIRA-LABEL:smoke Adds the specified label to the Jira ticket.
@JIRA-EPIC: @JIRA-EPIC:PROJ-456 Links the ticket to the specified Jira Epic.
@JIRA-NFR: @JIRA-NFR:PROJ-789 Links the ticket to a Non-Functional Requirement (NFR) Jira issue.
@JIRA-LINK: @JIRA-LINK:PROJ-321 Creates a generic link to another Jira issue.
@JIRA-STORY: @JIRA-STORY:PROJ-654 Links the ticket to a Jira Story.
@JIRA-DEFECT: @JIRA-DEFECT:PROJ-987 Links the ticket to a Jira Defect.
@JIRA-IGNORE: @JIRA-IGNORE Prevents ticket creation or update for this test.
  • Tags are case-sensitive and must be used exactly as shown.
  • yarn check:cypress:test-metadata uses @hmcts/opal-frontend-common-cypress to report executable Cypress tests with missing epic metadata, multiple epic references, or unresolved placeholder epic values.

About

No description or website provided.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages