diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 54e32022b..3b70a03dd 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -45,7 +45,7 @@ jobs:
- 'example.env'
- '.github/workflows/e2e.yml'
- main:
+ run-e2e-on-esm-core:
needs: changes
if: ${{ needs.changes.outputs.test-changes == 'true' }}
@@ -74,8 +74,65 @@ jobs:
- name: Install dependencies
run: yarn install --immutable
+ - name: Setup local cache server for Turborepo
+ uses: felixmosh/turborepo-gh-artifacts@v3
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ server-token: ${{ env.TURBO_TOKEN }}
+
+ - name: Build apps
+ run: yarn turbo run build --color --concurrency=5
+
+ - name: Run dev server
+ run: bash e2e/support/github/run-e2e-docker-env.sh
+
+ - name: Copy test environment variables
+ run: cp example.env .env
+
- name: Install Playwright Browsers
- run: yarn playwright install chromium --with-deps
+ run: npx playwright install chromium --with-deps
+
+ - name: Wait for the OpenMRS instance to start
+ run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done
+
+ - name: Run E2E tests
+ run: yarn playwright test
+
+ - name: Upload Report
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: report-esm-core
+ path: playwright-report/
+ retention-days: 30
+
+
+ run-e2e-on-other-repos:
+ needs: changes
+ if: ${{ needs.changes.outputs.test-changes == 'true' }}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ repo:
+ - openmrs-esm-patient-management
+ - openmrs-esm-patient-chart
+ - openmrs-esm-form-builder
+
+ steps:
+ - name: Checkout esm-core repo
+ uses: actions/checkout@v4
+
+ - name: Setup node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - name: Cache dependencies
+ id: cache
+ uses: actions/cache@v4
+ with:
+ path: '**/node_modules'
+ key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Setup local cache server for Turborepo
uses: felixmosh/turborepo-gh-artifacts@v3
@@ -83,26 +140,45 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
server-token: ${{ env.TURBO_TOKEN }}
+ - name: Install dependencies
+ run: yarn install --immutable
+
- name: Build apps
run: yarn turbo run build --color --concurrency=5
- name: Run dev server
run: bash e2e/support/github/run-e2e-docker-env.sh
- - name: Wait for OpenMRS instance to start
+ - name: Checkout to the ${{ matrix.repo }} main branch
+ uses: actions/checkout@v4
+ with:
+ repository: openmrs/${{ matrix.repo }}
+ ref: main
+ path: e2e_repo
+
+ - name: Copy test environment variables
+ run: cp example.env .env
+ working-directory: e2e_repo
+
+ - name: Install dependencies
+ run: yarn install --immutable
+ working-directory: e2e_repo
+
+ - name: Install Playwright Browsers
+ run: npx playwright install chromium --with-deps
+ working-directory: e2e_repo
+
+ - name: Wait for the OpenMRS instance to start
run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done
- name: Run E2E tests
run: yarn playwright test
+ working-directory: e2e_repo
- - name: Stop dev server
- if: '!cancelled()'
- run: docker stop $(docker ps -a -q)
-
- - name: Upload report
+ - name: Upload Report
uses: actions/upload-artifact@v4
if: always()
with:
- name: playwright-report
- path: playwright-report/
+ name: report-${{ matrix.repo }}
+ path: e2e_repo/playwright-report/
retention-days: 30
diff --git a/e2e/support/github/.dockerignore b/e2e/support/github/.dockerignore
new file mode 100644
index 000000000..3a80671ee
--- /dev/null
+++ b/e2e/support/github/.dockerignore
@@ -0,0 +1,2 @@
+# .dockerignore
+node_modules
diff --git a/e2e/support/github/Dockerfile b/e2e/support/github/Dockerfile
index 682360321..e5c37fd2c 100644
--- a/e2e/support/github/Dockerfile
+++ b/e2e/support/github/Dockerfile
@@ -8,9 +8,12 @@ WORKDIR /app
COPY . .
-RUN npm_config_legacy_peer_deps=true npm install -g openmrs@${APP_SHELL_VERSION:-next}
ARG CACHE_BUST
-RUN npm_config_legacy_peer_deps=true openmrs assemble --manifest --mode config --config spa-assemble-config.json --target ./spa
+
+RUN yarn install
+RUN npm_config_legacy_peer_deps=true node packages/tooling/openmrs/dist/cli.js assemble --manifest --mode config --config spa-assemble-config.json --target ./spa
+RUN npm_config_legacy_peer_deps=true node packages/tooling/openmrs/dist/cli.js build --target ./spa
+
FROM --platform=$BUILDPLATFORM openmrs/openmrs-reference-application-3-frontend:nightly as frontend
FROM nginx:1.23-alpine
diff --git a/e2e/support/github/commit_and_export_images.sh b/e2e/support/github/commit_and_export_images.sh
new file mode 100644
index 000000000..465459e37
--- /dev/null
+++ b/e2e/support/github/commit_and_export_images.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+backend_container_id=$(docker ps --filter "name=backend" --format "{{.ID}}")
+db_container_id=$(docker ps --filter "name=db" --format "{{.ID}}")
+frontend_container_id=$(docker ps --filter "name=frontend" --format "{{.ID}}")
+gateway_container_id=$(docker ps --filter "name=gateway" --format "{{.ID}}")
+
+docker commit $frontend_container_id frontend
+docker commit $gateway_container_id gateway
+docker commit $backend_container_id backend
+docker commit $db_container_id db
+
+docker save frontend > e2e_release_env_images.tar
+docker save frontend gateway backend db > e2e_release_env_images.tar
+
+# compress the file (to decrease the upload file size)
+gzip -c e2e_release_env_images.tar > e2e_release_env_images.tar.gz
diff --git a/e2e/support/github/docker-compose.yml b/e2e/support/github/docker-compose.yml
index f7fc4d70a..a882b3404 100644
--- a/e2e/support/github/docker-compose.yml
+++ b/e2e/support/github/docker-compose.yml
@@ -4,11 +4,13 @@ version: "3.7"
services:
gateway:
+ container_name: gateway
image: openmrs/openmrs-reference-application-3-gateway:${TAG:-nightly}
ports:
- "8080:80"
frontend:
+ container_name: frontend
build:
context: .
environment:
@@ -16,9 +18,11 @@ services:
API_URL: /openmrs
backend:
+ container_name: backend
image: openmrs/openmrs-reference-application-3-backend:nightly-with-data
depends_on:
- db
db:
+ container_name: db
image: openmrs/openmrs-reference-application-3-db:nightly-with-data
diff --git a/e2e/support/github/run-e2e-docker-env.sh b/e2e/support/github/run-e2e-docker-env.sh
index aea1c9f7c..858a3a0d4 100644
--- a/e2e/support/github/run-e2e-docker-env.sh
+++ b/e2e/support/github/run-e2e-docker-env.sh
@@ -2,8 +2,10 @@
# get the dir containing the script
script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+repository_root="$script_dir/../../../."
# create a temporary working directory
working_dir=$(mktemp -d "${TMPDIR:-/tmp/}openmrs-e2e-frontends.XXXXXXXXXX")
+echo $working_dir
# get a list of all the apps in this workspace
apps=$(yarn workspaces list --json | jq -r 'if .location | test("-app$") then .name else empty end')
# this array will hold all of the packed app names
@@ -20,7 +22,7 @@ do
# run yarn pack for our app and add it to the working directory
yarn workspace "$app" pack -o "$working_dir/$app_name.tgz" >/dev/null;
done;
-echo "Created packed app archives"
+echo "Created packed app archives"
echo "Creating dynamic spa-assemble-config.json..."
# dynamically assemble our list of frontend modules, prepending the
@@ -29,17 +31,64 @@ echo "Creating dynamic spa-assemble-config.json..."
jq -n \
--arg apps "$apps" \
--arg app_names "$(echo ${app_names[@]})" \
- '(
+ '{
+ "@openmrs/esm-active-visits-app": "next",
+ "@openmrs/esm-appointments-app": "next",
+ "@openmrs/esm-cohort-builder-app": "next",
+ "@openmrs/esm-devtools-app": "next",
+ "@openmrs/esm-dispensing-app": "next",
+ "@openmrs/esm-fast-data-entry-app": "next",
+ "@openmrs/esm-form-builder-app": "next",
+ "@openmrs/esm-form-engine-app": "next",
+ "@openmrs/esm-generic-patient-widgets-app": "next",
+ "@openmrs/esm-home-app": "next",
+ "@openmrs/esm-laboratory-app": "next",
+ "@openmrs/esm-openconceptlab-app": "next",
+ "@openmrs/esm-patient-allergies-app": "next",
+ "@openmrs/esm-patient-attachments-app": "next",
+ "@openmrs/esm-patient-banner-app": "next",
+ "@openmrs/esm-patient-chart-app": "next",
+ "@openmrs/esm-patient-conditions-app": "next",
+ "@openmrs/esm-patient-flags-app": "next",
+ "@openmrs/esm-patient-forms-app": "next",
+ "@openmrs/esm-patient-immunizations-app": "next",
+ "@openmrs/esm-patient-labs-app": "next",
+ "@openmrs/esm-patient-list-management-app": "next",
+ "@openmrs/esm-patient-lists-app": "next",
+ "@openmrs/esm-patient-medications-app": "next",
+ "@openmrs/esm-patient-notes-app": "next",
+ "@openmrs/esm-patient-orders-app": "next",
+ "@openmrs/esm-patient-programs-app": "next",
+ "@openmrs/esm-patient-registration-app": "next",
+ "@openmrs/esm-patient-search-app": "next",
+ "@openmrs/esm-patient-vitals-app": "next",
+ "@openmrs/esm-service-queues-app": "next",
+ "@openmrs/esm-system-admin-app": "next"
+ } + (
($apps | split("\n")) as $apps | ($app_names | split(" ") | map("/app/" + .)) as $app_files
| [$apps, $app_files]
| transpose
| map({"key": .[0], "value": .[1]})
| from_entries
)' | jq '{"frontendModules": .}' > "$working_dir/spa-assemble-config.json"
+
echo "Created dynamic spa-assemble-config.json"
+echo "Copying tooling, shell and framework..."
+mkdir -p "$working_dir/packages"
+mkdir -p "$working_dir/packages/tooling"
+
+cp -r "$repository_root/.yarn" "$working_dir/.yarn"
+cp -r "$repository_root/.yarnrc.yml" "$working_dir/.yarnrc.yml"
+cp -r "$repository_root/packages/tooling" "$working_dir/packages"
+cp -r "$repository_root/packages/shell" "$working_dir/packages"
+cp -r "$repository_root/packages/framework" "$working_dir/packages"
+cp "$repository_root/package.json" "$working_dir/package.json"
+cp "$repository_root/yarn.lock" "$working_dir/yarn.lock"
+
echo "Copying Docker configuration..."
cp "$script_dir/Dockerfile" "$working_dir/Dockerfile"
+cp "$script_dir/.dockerignore" "$working_dir/.dockerignore"
cp "$script_dir/docker-compose.yml" "$working_dir/docker-compose.yml"
cd $working_dir
diff --git a/packages/framework/esm-framework/docs/API.md b/packages/framework/esm-framework/docs/API.md
index 1d352b77d..8e989a4a3 100644
--- a/packages/framework/esm-framework/docs/API.md
+++ b/packages/framework/esm-framework/docs/API.md
@@ -5777,7 +5777,7 @@ This component also provides everything needed for workspace notifications to be
#### Defined in
-[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:67](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L67)
+[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:68](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L68)
___
@@ -7214,7 +7214,9 @@ with the key "webservices.rest.maxResultsDefault". See: https://openmrs.atlassia
This hook fetches data from a paginated rest endpoint, initially by fetching the first page of the results.
It provides a callback to load data from subsequent pages as needed. This hook is intended to serve UIs that
-provide infinite loading / scrolling of results.
+provide infinite loading / scrolling of results. Unlike `useOpenmrsPagination`, this hook does not allow random access
+(and lazy-loading) of any arbitrary page; rather, it fetches pages sequentially starting form the initial page, and the next page
+is fetched by calling `loadMore`. See: https://swr.vercel.app/docs/pagination#useswrinfinite
**`see`** `useOpenmrsPagination`
@@ -7243,7 +7245,7 @@ a UseServerInfiniteReturnObject object
#### Defined in
-[packages/framework/esm-react-utils/src/useOpenmrsInfinite.ts:97](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-react-utils/src/useOpenmrsInfinite.ts#L97)
+[packages/framework/esm-react-utils/src/useOpenmrsInfinite.ts:99](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-react-utils/src/useOpenmrsInfinite.ts#L99)
___
@@ -7385,7 +7387,7 @@ ___
### age
-▸ **age**(`birthDate`, `currentDate?`): `string`
+▸ **age**(`birthDate`, `currentDate?`): `string` \| ``null``
Gets a human readable and locale supported representation of a person's age, given their birthDate,
The representation logic follows the guideline here:
@@ -7396,12 +7398,12 @@ https://webarchive.nationalarchives.gov.uk/ukgwa/20160921162509mp_/http://system
| Name | Type | Description |
| :------ | :------ | :------ |
-| `birthDate` | `undefined` \| ``null`` \| `string` \| `number` \| `Date` \| `Dayjs` | The birthDate. |
+| `birthDate` | `undefined` \| ``null`` \| `string` \| `number` \| `Date` \| `Dayjs` | The birthDate. If birthDate is null, returns null. |
| `currentDate` | `undefined` \| ``null`` \| `string` \| `number` \| `Date` \| `Dayjs` | Optional. If provided, calculates the age of the person at the provided currentDate (instead of now). |
#### Returns
-`string`
+`string` \| ``null``
A human-readable string version of the age.
diff --git a/packages/framework/esm-framework/docs/interfaces/WorkspaceContainerProps.md b/packages/framework/esm-framework/docs/interfaces/WorkspaceContainerProps.md
index 9954cf2e2..07cd2b74e 100644
--- a/packages/framework/esm-framework/docs/interfaces/WorkspaceContainerProps.md
+++ b/packages/framework/esm-framework/docs/interfaces/WorkspaceContainerProps.md
@@ -19,7 +19,7 @@
#### Defined in
-[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L19)
+[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:20](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L20)
___
@@ -29,7 +29,7 @@ ___
#### Defined in
-[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:16](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L16)
+[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L17)
___
@@ -39,7 +39,7 @@ ___
#### Defined in
-[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L17)
+[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L18)
___
@@ -49,4 +49,4 @@ ___
#### Defined in
-[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L18)
+[packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx#L19)
diff --git a/packages/framework/esm-styleguide/package.json b/packages/framework/esm-styleguide/package.json
index 258fedcdd..6bac1a0db 100644
--- a/packages/framework/esm-styleguide/package.json
+++ b/packages/framework/esm-styleguide/package.json
@@ -59,6 +59,7 @@
"i18next": "21.x",
"react": "18.x",
"react-dom": "18.x",
+ "react-i18next": "11.x",
"rxjs": "6.x"
},
"devDependencies": {
diff --git a/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx b/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx
index 603542550..eb9f5b282 100644
--- a/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx
+++ b/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx
@@ -11,6 +11,7 @@ import ActionMenu from './action-menu.component';
import { type OpenWorkspace, updateWorkspaceWindowState, useWorkspaces } from '../workspaces';
import { WorkspaceRenderer } from './workspace-renderer.component';
import styles from './workspace.module.scss';
+import { useTranslation } from 'react-i18next';
export interface WorkspaceContainerProps {
contextKey: string;
@@ -125,6 +126,7 @@ interface WorkspaceProps {
}
function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspaceProps) {
+ const { t } = useTranslation(workspaceInstance.moduleName);
const layout = useLayoutType();
const { workspaceWindowState } = useWorkspaces();
const isMaximized = workspaceWindowState === 'maximized';
@@ -160,7 +162,7 @@ function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspacePro
{!isDesktop(layout) && !canHide && (