From 638846e1f8d784cc01c46eccb9076441e7b7cb7a Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Wed, 2 Sep 2026 16:53:10 +0200 Subject: [PATCH 1/5] Improve Spotless PR update instructions Adds information on how to update PRs for the recent Groovy file formatting changes --- docs/developer-guide/025-code-formatting.md | 46 ++++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/developer-guide/025-code-formatting.md b/docs/developer-guide/025-code-formatting.md index b7cbefff..c4daf174 100644 --- a/docs/developer-guide/025-code-formatting.md +++ b/docs/developer-guide/025-code-formatting.md @@ -521,16 +521,18 @@ This configuration applies to the current repository. Add `--global` only when y Only add commits that are overwhelmingly mechanical, such as repository-wide formatting or line-ending changes. Do not ignore commits that contain meaningful functional changes because that would hide useful authorship and history information. -### Updating an existing pull request after the initial formatting change +### Updating an existing pull request after repository-wide formatting changes -Pull requests created before the repository-wide formatting commit can produce many merge conflicts, even when their functional changes do not overlap. +Pull requests created before the repository-wide formatting commits can produce many merge conflicts, even when their functional changes do not overlap. For `openremote/openremote`, the relevant commits are: -| Commit | Description | -| ------------------------------------------ | -------------------------------------------------------- | -| `d6941d97c96ad70e7a6b764a4a4a7682aa906c8a` | Last commit before the repository-wide formatting change | -| `e3a066dcf739efe08d3d0e51e477d2d652dd28f8` | Apply Spotless across the repository | +| Commit | Description | +| ------------------------------------------ | -------------------------------------------------------------- | +| `d6941d97c96ad70e7a6b764a4a4a7682aa906c8a` | Last commit before the repository-wide formatting change | +| `e3a066dcf739efe08d3d0e51e477d2d652dd28f8` | Apply Spotless across the repository | +| `5312a1199b0e1cc6daaa46c9b20f2d71a6755246` | Enable Spotless formatting for Groovy files | +| `db6a1ef6c7bee92ffd6d1855d0aa057f56a028c2` | Apply repository-wide Spotless formatting to Groovy files | The `master` branch can be merged into the pull request in stages so that functional changes are handled separately from the generated formatting changes. @@ -570,7 +572,30 @@ The `master` branch can be merged into the pull request in stages so that functi git commit ``` -6. Merge the latest `master` branch as usual: +6. Merge the commit that enables Spotless formatting for Groovy files: + + ```shell + git merge 5312a1199b0e1cc6daaa46c9b20f2d71a6755246 + ``` + + Resolve any functional conflicts and complete the merge normally. This commit also updates Groovy tests to use syntax supported by the formatter. + +7. Start merging the repository-wide Groovy formatting commit without completing the merge: + + ```shell + git merge --no-commit db6a1ef6c7bee92ffd6d1855d0aa057f56a028c2 + ``` + + As with the initial formatting commit, restore the pre-merge file state, regenerate the formatting, and complete the merge: + + ```shell + git restore --source=HEAD --staged --worktree -- . + ./gradlew spotlessApply + git add --all + git commit + ``` + +8. Merge the latest `master` branch as usual: ```shell git merge origin/master @@ -578,15 +603,15 @@ The `master` branch can be merged into the pull request in stages so that functi Resolve any remaining functional conflicts normally. Run `spotlessApply` again if resolving a conflict required manual source-code changes. -7. Verify the final result: +9. Verify the final result: ```shell ./gradlew spotlessCheck ``` -Do not use the `-X ours` merge option for the repository-wide formatting commit. It operates on individual conflicting hunks and can combine formatted and unformatted code into invalid source files. +Do not use the `-X ours` merge option for either repository-wide formatting commit. It operates on individual conflicting hunks and can combine formatted and unformatted code into invalid source files. -Do not use the `-s ours` merge strategy either. It would record the formatting commit as merged without applying or regenerating its formatting changes. +Do not use the `-s ours` merge strategy either. It would record a formatting commit as merged without applying or regenerating its formatting changes. ### Upgrading an existing custom project to Spotless @@ -733,4 +758,3 @@ At the bottom of the pull request: 4. Confirm the merge when prompted. Selecting **Squash and merge** for this pull request switches the merge button back to the method normally used for subsequent pull requests. - From 803dcf3f5d3360852d491658d01488dc87c4ecc4 Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Wed, 2 Sep 2026 16:46:48 +0200 Subject: [PATCH 2/5] Clarify marker configuration details in user guide (#192) * Clarify marker configuration details in user guide Updated the marker configuration section to clarify how markers behave, including color changes based on attributes and label display options. Added details on the 'attributeName' property and color selection methods. * Update Marker Configuration --- .../020-manager-ui/30-appearance.md | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/020-manager-ui/30-appearance.md b/docs/user-guide/020-manager-ui/30-appearance.md index 8257d948..abbe0443 100644 --- a/docs/user-guide/020-manager-ui/30-appearance.md +++ b/docs/user-guide/020-manager-ui/30-appearance.md @@ -556,9 +556,14 @@ You can set the attributes to exclude (or include) on the top right card of the } } ``` + #### Marker Configuration -This configures how the markers behave. They can either change their colour based on an attribute value (number, boolean, or string), show a label with or without units, and/or show the direction an asset is facing. Note that this part of the config is not yet configured in the manager_config of the manager demo. +This configures how map markers behave. +Markers can change colour based on an attribute value, show an attribute value as a label with or without units, and optionally hide the asset direction. +Marker colours can be configured using numeric ranges, string values, or boolean values. +Note that this part of the config is not yet configured in the manager_config of the manager demo. + ```json { "pages": { @@ -585,12 +590,44 @@ This configures how the markers behave. They can either change their colour base } ] } + }, + "GatewayAsset": { + "attributeName": "status", + "showLabel": true, + "colours": { + "type": "string", + "CONNECTED": "39B54A", + "DISCONNECTED": "C1272D" + } + }, + "PresenceSensorAsset": { + "attributeName": "presence", + "colours": { + "type": "boolean", + "true": "39B54A", + "false": "C1272D" + } } } } } } ``` + +The `attributeName` property specifies the asset attribute used by the marker configuration. + +When `showLabel` is `true`, the current attribute value is shown next to the marker. By default, units are included when available. Set `showUnits` to `false` to hide them. + +The `colours` property changes the marker colour based on the current attribute value: + +* `range` selects a colour based on the configured numeric ranges. +* `string` selects the colour whose key exactly matches the string representation of the attribute value. +* `boolean` selects either the `"true"` or `"false"` colour. + +Set `hideDirection` to `true` to hide the direction indicator for assets that have a direction attribute. + +Colours are specified as hexadecimal RGB values without the `#` prefix. + #### Clustering Configuration This configures how clustering behaves. Clustering ensures the map page can render assets smoothly even with 40-50 thousand assets. The clustering option can be disabled, or changed to cluster markers in a smaller radius showing more clusters and changed to start clustering at a certain zoom level. Note that reducing cluster radius or cluster max zoom can have performance impacts. The recommended cluster max zoom level is Maximum realm zoom level - 2. From 7456310dd3e5bb8b9f31f1199b94b72893e47d07 Mon Sep 17 00:00:00 2001 From: Koen <44117179+Ekhorn@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:58:37 +0200 Subject: [PATCH 3/5] Fix Frontend testing documentation drift (#189) --- docs/developer-guide/160-frontend-testing.md | 73 ++++++++++++++------ 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/docs/developer-guide/160-frontend-testing.md b/docs/developer-guide/160-frontend-testing.md index a4998b55..d3565d40 100644 --- a/docs/developer-guide/160-frontend-testing.md +++ b/docs/developer-guide/160-frontend-testing.md @@ -12,7 +12,7 @@ All frontend testing code is situated under the `ui` directory. We use different Playwright configurations for `app` and `component` testing. -- `ui/test/app.config.ts`: App test configuration +- `ui/test/app.config.cts`: App test configuration - `ui/test/component.config.ts`: Component test configuration We do this because we modify the base configuration that comes with Playwright so that component testing works, however this configuration is incompatible with app testing thus we use 2 configurations. @@ -21,7 +21,7 @@ We do this because we modify the base configuration that comes with Playwright s Both the `app` and `component` tests depend on the `@openremote/test` package which includes shared fixtures, configurations and our Playwright component testing plugin. -The `shared` fixtures in the test package are meant for general test utilities like intercepting requests. Besides this there are also shared component only test fixtures under `CtShared`. +The `shared` fixture is available to every test. Apps get a `Shared` instance with general test utilities like intercepting requests. Components get a `CtShared` instance, which adds utilities for the component test page, such as serving the icon fonts and translations that the manager provides in a running app. Each project that needs testing should configure its own Playwright configuration file which must reuse the above-mentioned configurations. @@ -34,7 +34,7 @@ The app tests are used to test the app UI (End-to-End). #### Configuration - **Target:** Any app in the `ui/app/*` directory. -- **Worker Scope:** Single worker (to avoid tests interfering with one-another). +- **Worker Scope:** A single worker for the whole app config (to avoid tests interfering with one-another). - **Code reuse:** Apps may include a `fixtures` directory with test and data fixtures, and reuse fixtures from components they depend on. - **Setup & Teardown:** App test projects should depend on `*.setup.ts` and `*.cleanup.ts` project files to provision realm(s), user(s) and collect authentication states for more robust and performant tests. @@ -45,12 +45,10 @@ function createAppSetupAndTeardown(app) { name: `setup ${app}`, testMatch: "**/*.setup.ts", teardown: `cleanup ${app}`, - worker: 1, }, { name: `cleanup ${app}`, testMatch: "**/*.cleanup.ts", - worker: 1, }, ]; } @@ -75,14 +73,24 @@ Assuming you have set up your [development tooling](010-preparing-the-environmen 1. Create a playwright configuration file `playwright.config.ts` in your component / app directory. -Playwright configuration file contents: +Playwright configuration file contents for an app: ```ts -import defineConfig from "@openremote/test/.config"; +import { defineAppConfig } from "@openremote/test/app.config"; -export default defineConfig(__dirname); +export default defineAppConfig(__dirname); ``` +Or for a component: + +```ts +import { defineCtConfig } from "@openremote/test/component.config"; + +export default defineCtConfig(__dirname); +``` + +Both derive the project name from the directory you pass, so the argument is always `__dirname`. + 2. Add the corresponding `test` script to the `package.json` file in your component / app directory. | | test script (in package.json) | @@ -92,6 +100,8 @@ export default defineConfig(__dirname); 3. Add the `npmTest` Gradle task to the `build.gradle` file in the component / app directory so that the CI/CD pipeline knows to run your tests. +For a component: + ```groovy tasks.register('npmTest', Exec) { dependsOn getYarnInstallTask() @@ -99,6 +109,16 @@ tasks.register('npmTest', Exec) { } ``` +For an app, which installs only the workspaces the test run needs and forwards extra Playwright arguments through the `args` property: + +```groovy +tasks.register('npmTest', Exec) { + dependsOn ":ui:test:installTestDeps", ":ui:test:clean" + commandLine npmCommand("yarn"), "run", "test" + args((findProperty('args')?.toString()?.tokenize()) ?: []) +} +``` + 4. Install the required Playwright browser(s): ```sh @@ -124,10 +144,12 @@ Then include the following boilerplate for app tests: ```ts import { test } from "@openremote/test"; -test("My app test", async ({ myApp }) => { +test("My app test", async ({ page, shared }) => { }) ``` +The `test` function from `@openremote/test` only provides Playwright's own fixtures plus `shared`. App specific fixtures like `myApp` come from extending it, see [Reusing test code](#reusing-test-code). + Or the following for component tests: ```ts @@ -145,7 +167,9 @@ ct("My component test", async ({ mount }) => { ``` :::note + You must import a component by its alias `@openremote/*`. Relative paths will cause issues. The downside of alias imports is that they refer to the transpiled TypeScript (in the `lib` directory), which is why the component test script includes `npx tsc -b` and needs manual rebuilding. However you can still live reload the component changes indirectly by running `npm run serve` on the manager app. + ::: Playwright uses [`locators`](https://playwright.dev/docs/locators) to find elements in the DOM. It's crucial to know the different types of locators to be able to write tests that are robust and to avoid flaky behavior. @@ -171,7 +195,9 @@ export class AssetsPage implements BasePage { ``` :::note + In case you want to reuse certain non-project specific fixtures across multiple projects you can add your fixture to the `shared` fixtures in the `@openremote/test` package under `ui/test/fixtures/shared.ts`. If you want to reuse component specific fixtures in tests for a parent component or an app, simply import the fixtures and add them through the `extend` method. + ::: Finally extend the `test` function: @@ -210,24 +236,25 @@ test("My app test", async ({ assetsPage }) => { ### Running the test -The best way to run and debug your tests in Playwright is by using the [Playwright UI mode](https://playwright.dev/docs/test-ui-mode) feature. +The `npmTest` task runs in every package that registers it, so the directory you point Gradle at decides which tests run: -You may consider adding a Gradle task to the `build.gradle` file in the component / app directory to open it: - -```groovy -tasks.register('npmTestUI', Exec) { - dependsOn getYarnInstallTask() - commandLine npmCommand("yarn"), "run", "test", "--ui" -} +```sh +./gradlew npmTest # every frontend test +./gradlew -p ui/component npmTest # all component tests +./gradlew -p ui/app npmTest # all app tests ``` -Then run it with: +App packages forward extra Playwright arguments through the `args` property, which is how CI shards the app test run: ```sh -gradle ui:component:or-:npmTestUI +./gradlew -p ui/app/manager npmTest -Pargs="--shard=1/4" ``` -Or simply run `npm test -- --ui` in the component / app directory. +While writing or debugging a test, run Playwright directly from the component / app directory instead. [UI mode](https://playwright.dev/docs/test-ui-mode) gives you the watch mode, trace viewer and time travel debugging that the Gradle tasks do not: + +```sh +npm test -- --ui +``` ### Best practices @@ -247,15 +274,21 @@ Please read the [Playwright Best practices](https://playwright.dev/docs/best-pra The most useful feature Playwright provides when it comes to writing frontend tests is [UI mode](https://playwright.dev/docs/test-ui-mode). Once the Playwright UI is launched you can select which projects you want to see and run. :::tip + The UI includes a locator tab, which allows you to click an element in the test preview to easily get a locator of an element. + ::: :::warning + Sometimes the locators Playwright provides are susceptible to flaky behavior, it is important to understand the DOM structure of the UI to get the most effective locators. + ::: :::tip + In some cases you may face a situation where the UI needs to load first, before you can run an action. You can use `await selector.waitFor()` to ensure the element you want to interact with is visible. + ::: In case you want to see how Playwright runs in a headed browser you can add the `--headed` argument. From 7e05127c1ea32d8e707cc0349219a4d6f89d2836 Mon Sep 17 00:00:00 2001 From: Martin Peeters Date: Wed, 2 Sep 2026 17:35:42 +0200 Subject: [PATCH 4/5] Update NodeJS version requirement to >=20.19 (#190) In the `openremote/openremote` repository, we're updating the RSPack bundler dependency from v1 to v2. This means the minimum NodeJS version has been bumped to `20.19+`. Although most users will install the latest LTS (`v22.X`), it's worth updating the docs. --- docs/developer-guide/010-preparing-the-environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/010-preparing-the-environment.md b/docs/developer-guide/010-preparing-the-environment.md index 56359240..44e678b5 100644 --- a/docs/developer-guide/010-preparing-the-environment.md +++ b/docs/developer-guide/010-preparing-the-environment.md @@ -22,7 +22,7 @@ For development you need the following in addition to the runtime tooling: * Java 21 JDK ([OpenJDK](https://openjdk.java.net/), [Oracle Java SE JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html)) * [Git](https://git-scm.com/downloads) * [Git LFS](https://git-lfs.com/) `git lfs install` -* [NodeJS](https://nodejs.org/en/download/current/) (>=20.0, on macOS you can use [Homebrew](https://brew.sh/) and `brew install node@20`) +* [NodeJS](https://nodejs.org/en/download/current/) (>=20.19, on macOS you can use [Homebrew](https://brew.sh/) and `brew install node@20`) * [yarn](https://yarnpkg.com/getting-started/install) `corepack enable; yarn init -2` (>=3.2.0) Ensure the following commands execute successfully: From 4c374a7a231de581c86f5c3ec2dca4f0754ccc3c Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Thu, 3 Sep 2026 14:02:17 +0200 Subject: [PATCH 5/5] Add custom project details and handy links --- docs/developer-guide/025-code-formatting.md | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/developer-guide/025-code-formatting.md b/docs/developer-guide/025-code-formatting.md index c4daf174..cbf5d625 100644 --- a/docs/developer-guide/025-code-formatting.md +++ b/docs/developer-guide/025-code-formatting.md @@ -527,12 +527,12 @@ Pull requests created before the repository-wide formatting commits can produce For `openremote/openremote`, the relevant commits are: -| Commit | Description | -| ------------------------------------------ | -------------------------------------------------------------- | -| `d6941d97c96ad70e7a6b764a4a4a7682aa906c8a` | Last commit before the repository-wide formatting change | -| `e3a066dcf739efe08d3d0e51e477d2d652dd28f8` | Apply Spotless across the repository | -| `5312a1199b0e1cc6daaa46c9b20f2d71a6755246` | Enable Spotless formatting for Groovy files | -| `db6a1ef6c7bee92ffd6d1855d0aa057f56a028c2` | Apply repository-wide Spotless formatting to Groovy files | +| Commit | Description | +|----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------| +| [`d6941d97c96ad70e7a6b764a4a4a7682aa906c8a`](https://github.com/openremote/openremote/commit/d6941d97c96ad70e7a6b764a4a4a7682aa906c8a) | Last commit before the repository-wide formatting change | +| [`e3a066dcf739efe08d3d0e51e477d2d652dd28f8`](https://github.com/openremote/openremote/commit/e3a066dcf739efe08d3d0e51e477d2d652dd28f8) | Apply Spotless across the repository | +| [`5312a1199b0e1cc6daaa46c9b20f2d71a6755246`](https://github.com/openremote/openremote/commit/5312a1199b0e1cc6daaa46c9b20f2d71a6755246) | Enable Spotless formatting for Groovy files | +| [`db6a1ef6c7bee92ffd6d1855d0aa057f56a028c2`](https://github.com/openremote/openremote/commit/db6a1ef6c7bee92ffd6d1855d0aa057f56a028c2) | Apply repository-wide Spotless formatting to Groovy files | The `master` branch can be merged into the pull request in stages so that functional changes are handled separately from the generated formatting changes. @@ -633,24 +633,29 @@ uses: openremote/openremote/.github/workflows/ci_cd.yml@51f4c3c0c8edd429a6523726 Pinning the workflow also prevents the project from receiving other workflow changes made after that commit. Use this only as a temporary measure. +The pinned SHA is the [`51f4c3c0c8edd429a65237268d47d615617d4008`](https://github.com/openremote/openremote/commit/51f4c3c0c8edd429a65237268d47d615617d4008) commit in `openremote/openremote`. + #### Pull request 1: Add Spotless and apply formatting The first pull request adds the Spotless configuration and applies the initial repository-wide formatting. ##### Synchronizing the Spotless configuration -The Spotless configuration was added to the custom-project template in commit: +The required Spotless configuration was added to the custom-project template in two commits: -```text -3aa39cc8e2134db446d7258d429315fc5615f6e9 -``` +| Commit | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------| +| [`3aa39cc8e2134db446d7258d429315fc5615f6e9`](https://github.com/openremote/custom-project/commit/3aa39cc8e2134db446d7258d429315fc5615f6e9) | Add Spotless Gradle plugin configuration | +| [`ae2e2fb146accfbcacabbdfb7d7909b23711a284`](https://github.com/openremote/custom-project/commit/ae2e2fb146accfbcacabbdfb7d7909b23711a284) | Enable Spotless formatting for Groovy files | + +Use the second commit as the reference point because it includes both configuration changes. Check out the template at this commit: ```shell git clone https://github.com/openremote/custom-project.git custom-project-template cd custom-project-template -git checkout 3aa39cc8e2134db446d7258d429315fc5615f6e9 +git checkout ae2e2fb146accfbcacabbdfb7d7909b23711a284 ``` Use a directory comparison tool such as [Meld](https://meldmerge.org/) to compare the checked-out template with the existing custom project: @@ -662,10 +667,11 @@ custom-project-template Synchronize the Spotless-related changes from the template while preserving project-specific configuration. Carefully merge changes to existing files instead of replacing them wholesale. -The exact changes introduced by the Spotless configuration commit can be inspected with: +The exact changes introduced by both Spotless configuration commits can be inspected with: ```shell git show 3aa39cc8e2134db446d7258d429315fc5615f6e9 +git show ae2e2fb146accfbcacabbdfb7d7909b23711a284 ``` When other custom-project template updates are also required, compare the existing project with the desired newer template commit and merge those changes in the same way.