Roomle UI
This project provides the UIs on top of the Roomle Web SDK (https://github.com/Roomle/web-sdk). It will be constantly developed and maintained by the Roomle Web Team since it's the official user interface of the Roomle web components and standalone version (e.g: https://www.roomle.com/t/configurator/).
Which UIs are in this repository:
Configurator
Planner
Techstack:
Vue (Vuex)
Typescript
JestVitestCypressPlaywrightBabel
Before your first commit
This repository uses conventional commits: https://www.conventionalcommits.org/en/v1.0.0/
At least read through the summary before committing: https://www.conventionalcommits.org/en/v1.0.0/#summary
Be careful:
Commit types start with a lower case letter (e.g fix:)
Commit messages start also with a lower case letter
For example: fix: add missing file to project
Project setup
yarn install
Compiles and hot-reloads for development: yarn run serve
Compiles and minifies for production yarn run build
Run your tests yarn run test
Lints and fixes files yarn run lint
Run your end-to-end tests yarn run test:e2e
Run your unit tests yarn run test:unit
Project structure
/public
/translations
/src
/common
/assets
/components
/configurator
/assets
/components
/store
/planner
Public directory (/public)
These assets will simply be copied and will not go through webpack. Need to be referenced via absolute paths.
Translations (/public/translations)
Default folder for LocalTranslationSource (src/common/translations/local-translation-source.ts)
Source directory (/src)
Consider to read the Vue style guide first before changing files in those directories: https://vuejs.org/v2/style-guide/
Common (/src/common)
Contains all components common to all UIs.
Configurator (/src/configurator)
All files related only to the configurator, see https://www.roomle.com/t/configurator/
Assets (/src/*/assets)
Files placed here need to be imported in JavaScript or referenced in templates/CSS via relative paths. Such references will be handled by webpack.
Components (/src/*/components)
This directory contains all Vue single component files (https://vuejs.org/v2/guide/components.html).
Vuex Store (/src/*/store)
Contains all files related to Vuex: https://vuex.vuejs.org/guide/
Guidelines
Store
Only access the store from templates in a reading manner. Never dispatch actions or commit mutations from within the template.
If something is local to only this specific component it's ok to store this state inside the components instance but always be aware that we need to be able to hot-swap components which means that a component for mobile UI could be replace by a component for desktop UI but the hot swapped component needs to know where the other component stopped and from where to continue. Therefore there are only some cases where we do not need to store the state in the global state tree.
We try to limit boilerplate code therefore we want to be lean on the distinction between a mutation and an action. As a general rule of thumb we decided to do simple state changes in mutations. As soon as complexity grows we create actions. Example for when to use actions:
we need to change multiple properties of the store
we need to change the store in an async way
Code Style
When writing code, it is important to adhere to consistent style guidelines to ensure readability and maintainability. The following section outlines the recommended code style conventions to be followed in this project.
Components
When writing/refactoring components with composition API syntax, we use
to contain all component logic, and we stick to the following order of sections with labels included:
Constants
In case of using constants to define a set of named values, it is good practice to use singular names instead of plural names. This convention helps to maintain clarity and consistency throughout the codebase. It is also important to use const
instead of enum
to create a type, for example:
If you want to type a property as a value of this constant, you can use Values<typeof INTERACTION_VIEWS_TYPE>
Husky
Husky is a tool that helps developers work with Git hooks more efficiently and run all the scripts that need to work at various stages.
By simplifying the process of setting up Git hooks, developers can create effective solutions faster. Husky works within the package.json file by including an object that configures Husky to run certain scripts, and then Husky manages the script at specific points in the Git lifecycle.
Husky is a dotnet tool that allows you to run arbitrary actions on specific Git hooks. These could be to enforce the same sort of things that you would do centrally, but here they would run before committing code into Git. For example, you could ensure your code built and passed all unit tests.
In order to activate Husky in your local repo you have to run this command just one time npm run prepare
. This command will allow Git hooks to be enabled.
lint-staged
Every time you push a new change, it gets validated from Husky and runs a lint command but if you run the lint for the whole project every push, it will take time, that is why we install lint-staged
and it works along with Husky so if you have staged changes, it will run the lint for those changes only and not for the whole project.
Conventional commits validations
We also include validation for checking the format of the commit message against conventional commit format so please be sure that you write a valid commit message that agrees with conventional commit standards. As an exception to these rules, we allow the standard commit messages from tools like https://git-fork.com/ and https://www.sourcetreeapp.com/ when merging branches.
Integration with Git clients
Sometimes there are troubles when integrating with Git clients like SourceTree. To find possible ways to resolve those issues follow the link: https://github.com/typicode/husky/issues/904
For Georg the following worked:
CI/CD
GitHub Actions Preview on cloud storage bucket
Create bucket
Authenticate using WIF, edit and run
./.github/workflows/setup-wif.sh
Assign
Storage Legacy Object Viewer
for all users to allow accessAssign
Storage Object Admin
for service account created for WIFConfigure CDN to serve content as website. On the bucket overview, click on the 3 vertical dots. Edit start and error page settings.
GitHub Actions build and deploy to cloud storage bucket
The workflow is defined in .github/workflows/cd-gcp.yaml
.
it is split into multiple jobs
Build
Deploy to [dev|test|alpha|live]
Publish docs artifact to [dev|test|alpha|live]
The same workflow is used to deploy to all stages. We use GitHub Environments specify which branch name should trigger a job: https://github.com/roomle-dev/roomle-ui/settings/environments. See environment:
for details. This also allows to add a url to the github workflow visualisation.
To handle special cases inside like building for test, building for alpha
or release to npmjs
we use if
conditions to limit to certain branch names inside the re-used build
job.
Errors show up when the env protection rule gets broken. An example error would be Branch "master" is not allowed to deploy to alpha due to environment protection rules.
To avoid such errors on successful builds we use if
conditions where a environment
is defined.
Dependencies / Integration
Action secrets
Added GH_TOKEN
, NPM_TOKEN
, SONARQUBE_TOKEN
as github actions secrets at: https://github.com/roomle-dev/roomle-ui/settings/secrets/actions
The secrets SLACK_WEBHOOK_URL
and SLACK_WEBHOOK_URL_LIVE
contain a webhook url to send Slack notifications to a specific channel. You can manage the webhook urls in the GitHub Action Slack app
GCP service account
Created a service account (+permissions) to authenticate from github to google cloud. This allows us to sync the app and the documentation build artifact to cloud storage buckets where the service account has access. You can run the commands using ./.github/workflows/setup-wif-gh-gcp-bucket-staging.sh
GitHub job permissions
To use WIF requires overwriting the github default permission. Check out permissions:
.
https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs#example-assigning-permissions-to-github_token
We set permissions on step level, to avoid conflicts between steps (upload-to-bucket vs. semantic-release)
Forwarding rule on load balancer
To deliver the application on a specific url, we have to set a forward rule on the load balancer.
i.e.
https://dev.roomle.com/t/cp-cdn/
should redirect traffic to bucketgs://roomle-configurator-dev
Can be managed in: https://console.cloud.google.com/net-services/loadbalancing/edit/http/roomle-loadbalancer-1-test?project=roomle-01
GitHub actions build artifact handover
Every GitHub Actions job starts with an empty container image. To use a build artifact from a previous job, we have to upload and download as a github artifact.
Exchange data between several build jobs within a workflow
There are several options as:
wildcard
,dir
,files
. For details see: https://github.com/actions/upload-artifact
Run GitHub actions locally
Create a new env file roomle-build.secrets
in the project's .act
directory and add the environment variables used in the github action.
Install act
to run the github action without pushing it to the repository.
Google Cloud Build (deactivated)
Create a new ssh key. For details see: https://cloud.google.com/build/docs/access-github-from-build
Add the
PUBLIC
key as deploy key to the repositoryhttps://github.com/roomle-dev/roomle-ui/settings/keys
. Name it i.e.SSH-Google-Cloud-Build-496816138068
.Add the
PRIVATE
ssh key as a secret to theGoogle Secret Manager
.Update the webhook trigger in GCP project
rml-ci-cd
. Run./cloudbuild/upsert-triggers.sh
using the correct gcloud user and configuration.Copy the webhook url from the trigger at
https://console.cloud.google.com/cloud-build/triggers;region=global/edit/03927c71-2af1-4bf7-bbd0-89ad1f9cb3cd?project=rml-ci-cd
and paste it to a webhook in GitHubhttps://github.com/roomle-dev/roomle-ui/settings/hooks
Alternatively we could use the Google Cloud Build GitHub App. For details see: https://cloud.google.com/blog/products/devops-sre/cloud-build-brings-advanced-cicd-capabilities-to-github
Unit tests trigger workflow
To optimize GitHub build times, we've made the unit test triggering in pull requests more dynamic:
Initial PR Testing: When a pull request is created, only tests relevant to the changes in the pull request are run. This is achieved using Jest's
--changedSince
flag.Pre-Merge the pull request: After the pull request is reviewed and ready to be merged, a different workflow is triggered to run all unit tests. This ensures the master branch remains stable after merging the pull request.
Skipping Tests for minor fixes: If the pull request contains a minor fix (e.g., a simple CSS adjustment) and the developer is confident it doesn't need to run the whole unit tests workflow, the approval workflow can be skipped. To do so, prefix your commit message with
fix(small): [YOUR FIX COMMIT MESSAGE]
. The workflow will recognize this prefix and won't trigger tests upon the pull request approval.
Playwright
For E2E tests we use https://playwright.dev/. Please bear in mind that an E2E test is expensive (computation resources etc.), therefore please use it only in cases where it makes sense. Also, make sure that requests to our backend are mocked. We do not want to DDoS our own service with our tests. To mock our backend we use HAR files. For a quick intro to HAR files see the docs here: https://playwright.dev/docs/network#replaying-from-har
The default way to run our tests is with fully mocked network connection. This is useful because:
we do not want to stress test our backend
we do not want to create backend costs (traffic, computation etc)
we want to be able to test several edge cases that won't happen with the real backend
~~Since we run our E2E tests before our app is deployed somewhere we have to do some shenanigans with the HAR file. First of all load the content in the browser and record the network activity inside the dev-tools. Then filter the network requests by our RAPI-Url. Currently only Firefox behaves correctly (in Chrome you have to save every request and in Safari OPTIONS calls are missing). You can find a screencast here: https://roomle.atlassian.net/wiki/spaces/DT/pages/2272722984/Playwright+HAR+file+generation ~~
The above explained behavior is not needed anymore. Please have a look at tests-e2e/specs/example.spec.ts
to see how it is used now. There is a fixture called pageInteractor
that can be used to navigate to a page and mock the network automatically.
To interact with the network mock there are currently 3 modes:
skip: means, do not mock the network and query the backend
update: means, record network activity and write it into HAR files
enforce: means, read requests from recoreded HAR files and report if one call was not mocked
Playwright is a very powerful tool and probably too powerful. There are several things that help creating tests. Like codegen and the --ui
mode. Nevertheless it's complex and some problems are not directly obvious. Therefore there is some kind of learning curve and I welcome everyone to start the learning journey before ditching Playwright immediatelly. There is plenty of learning material, the docs are awesome and there is a huge community so I think it should be possible to get quickly up to speed.
If you are trying to work with screenshots and snapshots it is important that you generate the screenshots also on Linux. Luckily Microsoft provides a Docker Image that can help us. This is what they wrote in the docs, for more details see: https://playwright.dev/docs/test-snapshots
Side note: to run just one test do something like:
This helps update snapshots and missing HAR stuff. Otherwise, we always update everything.
If you run into troubles that click
events do not come in correctly, probably you need to send the click
calls simultaneously. For more details see here: https://tally-b.medium.com/the-illustrated-guide-to-using-promise-all-in-playwright-tests-af7a98af3f32
In our case this happens when the "mode buttons" collapse on mobile. The following PR describes teh fix: https://github.com/roomle-dev/roomle-ui/pull/292
To debug the test runs on CI download the trace from Github and run:
A more sophisticated wrap-up can be found here: https://roomle.atlassian.net/wiki/spaces/DT/pages/2623143943/Playwright+reintroduction+2024
That's it, Happy E2E testing 🥳
If you are using VSCode you can also follow the tutorial on how to use the VSCode plugin: https://playwright.dev/docs/getting-started-vscode
Creating Translations
A guide for creating translations with localise.biz and DeepL.com is available on this confluence page here: https://roomle.atlassian.net/wiki/x/EQCIm, remember to obtain a localise.biz API key and set it to LOCO_KEY
in your .env.local
.
Important Note on Partlist Prices
When using usePriceService=true
, zero prices will no longer be shown in the partlist by default. However, you can enable the display of the price column for debugging purposes by using the debug=true
flag.
Last updated