@openmrs/esm-laboratory-app 1.1.2-pre.88 → 1.2.1-pre.590

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/.turbo/cache/99f2b2ec58ba1a5e-meta.json +1 -0
  2. package/.turbo/cache/99f2b2ec58ba1a5e.tar.zst +0 -0
  3. package/.turbo/turbo-build.log +27 -29
  4. package/dist/1026.js +2 -0
  5. package/dist/1026.js.map +1 -0
  6. package/dist/1083.js +1 -0
  7. package/dist/{7328.js.map → 1083.js.map} +1 -1
  8. package/dist/1119.js +1 -0
  9. package/dist/1120.js +1 -0
  10. package/dist/1120.js.map +1 -0
  11. package/dist/1197.js +1 -0
  12. package/dist/1788.js +1 -0
  13. package/dist/1788.js.map +1 -0
  14. package/dist/2058.js +1 -0
  15. package/dist/2058.js.map +1 -0
  16. package/dist/2146.js +1 -0
  17. package/dist/2246.js +1 -0
  18. package/dist/2246.js.map +1 -0
  19. package/dist/2690.js +1 -0
  20. package/dist/2934.js +2 -0
  21. package/dist/2934.js.map +1 -0
  22. package/dist/3099.js +1 -0
  23. package/dist/3495.js +2 -0
  24. package/dist/3495.js.map +1 -0
  25. package/dist/3584.js +1 -0
  26. package/dist/3656.js +1 -0
  27. package/dist/{8684.js.map → 3656.js.map} +1 -1
  28. package/dist/3671.js +1 -0
  29. package/dist/3671.js.map +1 -0
  30. package/dist/4055.js +1 -0
  31. package/dist/4069.js +1 -0
  32. package/dist/{7724.js.map → 4069.js.map} +1 -1
  33. package/dist/4132.js +1 -0
  34. package/dist/4239.js +1 -0
  35. package/dist/{6092.js.map → 4239.js.map} +1 -1
  36. package/dist/4300.js +1 -0
  37. package/dist/4335.js +1 -0
  38. package/dist/4369.js +1 -0
  39. package/dist/{2156.js.map → 4369.js.map} +1 -1
  40. package/dist/4618.js +1 -0
  41. package/dist/4652.js +1 -0
  42. package/dist/4944.js +1 -0
  43. package/dist/5085.js +1 -0
  44. package/dist/5085.js.map +1 -0
  45. package/dist/5173.js +1 -0
  46. package/dist/520.js +2 -0
  47. package/dist/{7936.js.LICENSE.txt → 520.js.LICENSE.txt} +5 -0
  48. package/dist/520.js.map +1 -0
  49. package/dist/5241.js +1 -0
  50. package/dist/5442.js +1 -0
  51. package/dist/5661.js +1 -0
  52. package/dist/6022.js +1 -0
  53. package/dist/6052.js +1 -0
  54. package/dist/6052.js.map +1 -0
  55. package/dist/6134.js +1 -0
  56. package/dist/6134.js.map +1 -0
  57. package/dist/6468.js +1 -0
  58. package/dist/6547.js +1 -0
  59. package/dist/6547.js.map +1 -0
  60. package/dist/6679.js +1 -0
  61. package/dist/6840.js +1 -0
  62. package/dist/6859.js +1 -0
  63. package/dist/7003.js +2 -0
  64. package/dist/7003.js.LICENSE.txt +9 -0
  65. package/dist/7003.js.map +1 -0
  66. package/dist/7097.js +1 -0
  67. package/dist/7159.js +1 -0
  68. package/dist/723.js +1 -0
  69. package/dist/7423.js +1 -0
  70. package/dist/{1708.js.map → 7423.js.map} +1 -1
  71. package/dist/7617.js +1 -0
  72. package/dist/795.js +1 -0
  73. package/dist/8163.js +1 -0
  74. package/dist/8349.js +1 -0
  75. package/dist/8554.js +1 -0
  76. package/dist/{3956.js.map → 8554.js.map} +1 -1
  77. package/dist/8618.js +1 -0
  78. package/dist/8667.js +1 -0
  79. package/dist/{1636.js.map → 8667.js.map} +1 -1
  80. package/dist/876.js +1 -0
  81. package/dist/876.js.map +1 -0
  82. package/dist/890.js +1 -0
  83. package/dist/9214.js +1 -0
  84. package/dist/9538.js +1 -0
  85. package/dist/9569.js +1 -0
  86. package/dist/9630.js +2 -0
  87. package/dist/9630.js.LICENSE.txt +29 -0
  88. package/dist/9630.js.map +1 -0
  89. package/dist/986.js +1 -0
  90. package/dist/9879.js +1 -0
  91. package/dist/9895.js +1 -0
  92. package/dist/9900.js +1 -0
  93. package/dist/9913.js +1 -0
  94. package/dist/main.js +1 -1
  95. package/dist/main.js.LICENSE.txt +5 -0
  96. package/dist/main.js.map +1 -1
  97. package/dist/openmrs-esm-laboratory-app.js +1 -1
  98. package/dist/openmrs-esm-laboratory-app.js.buildmanifest.json +429 -363
  99. package/dist/openmrs-esm-laboratory-app.js.map +1 -1
  100. package/dist/routes.json +1 -1
  101. package/e2e/README.md +117 -0
  102. package/e2e/commands/encounter-operations.ts +63 -0
  103. package/e2e/commands/index.ts +5 -0
  104. package/e2e/commands/patient-operations.ts +109 -0
  105. package/e2e/commands/provider-operations.ts +9 -0
  106. package/e2e/commands/test-order-operations.ts +46 -0
  107. package/e2e/commands/types/index.ts +157 -0
  108. package/e2e/commands/visit-operations.ts +38 -0
  109. package/e2e/core/global-setup.ts +32 -0
  110. package/e2e/core/index.ts +1 -0
  111. package/e2e/core/test.ts +31 -0
  112. package/e2e/fixtures/api.ts +27 -0
  113. package/e2e/fixtures/fhirApi.ts +28 -0
  114. package/e2e/fixtures/index.ts +2 -0
  115. package/e2e/pages/index.ts +1 -0
  116. package/e2e/pages/laboratory-page.ts +9 -0
  117. package/e2e/specs/add-lab-results.spec.ts +100 -0
  118. package/e2e/specs/test-orders.spec.ts +16 -0
  119. package/e2e/support/github/Dockerfile +34 -0
  120. package/e2e/support/github/docker-compose.yml +24 -0
  121. package/e2e/support/github/run-e2e-docker-env.sh +58 -0
  122. package/example.env +7 -0
  123. package/jest.config.js +10 -2
  124. package/package.json +9 -6
  125. package/playwright.config.ts +36 -0
  126. package/src/components/orders-table/list-order-details.component.tsx +91 -98
  127. package/src/components/orders-table/list-order-details.scss +77 -55
  128. package/src/components/orders-table/orders-data-table.component.tsx +19 -12
  129. package/src/components/orders-table/orders-data-table.scss +12 -6
  130. package/src/components/orders-table/orders-date-range-picker.component.tsx +2 -2
  131. package/src/components/overlay/overlay.component.tsx +8 -10
  132. package/src/index.ts +1 -0
  133. package/src/lab-tabs/actions/pickup-lab-request-action.component.tsx +4 -4
  134. package/src/lab-tabs/laboratory-tabs.component.tsx +48 -50
  135. package/src/lab-tabs/laboratory-tabs.scss +0 -9
  136. package/translations/am.json +6 -5
  137. package/translations/ar.json +6 -5
  138. package/translations/ar_SY.json +6 -5
  139. package/translations/bn.json +6 -5
  140. package/translations/de.json +6 -5
  141. package/translations/en.json +4 -4
  142. package/translations/en_US.json +68 -0
  143. package/translations/es.json +6 -5
  144. package/translations/es_MX.json +6 -5
  145. package/translations/fr.json +6 -5
  146. package/translations/he.json +6 -5
  147. package/translations/hi.json +6 -5
  148. package/translations/hi_IN.json +6 -5
  149. package/translations/id.json +64 -63
  150. package/translations/it.json +6 -5
  151. package/translations/ka.json +68 -0
  152. package/translations/km.json +6 -5
  153. package/translations/ku.json +6 -5
  154. package/translations/ky.json +6 -5
  155. package/translations/lg.json +6 -5
  156. package/translations/ne.json +6 -5
  157. package/translations/pl.json +6 -5
  158. package/translations/pt.json +6 -5
  159. package/translations/pt_BR.json +6 -5
  160. package/translations/qu.json +6 -5
  161. package/translations/ro_RO.json +65 -64
  162. package/translations/ru_RU.json +6 -5
  163. package/translations/si.json +6 -5
  164. package/translations/sw.json +6 -5
  165. package/translations/sw_KE.json +6 -5
  166. package/translations/tr.json +6 -5
  167. package/translations/tr_TR.json +6 -5
  168. package/translations/uk.json +6 -5
  169. package/translations/uz.json +6 -5
  170. package/translations/uz@Latn.json +6 -5
  171. package/translations/uz_UZ.json +6 -5
  172. package/translations/vi.json +6 -5
  173. package/translations/zh.json +6 -5
  174. package/translations/zh_CN.json +9 -8
  175. package/.turbo/cache/4de59823a6517c3e-meta.json +0 -1
  176. package/.turbo/cache/4de59823a6517c3e.tar.zst +0 -0
  177. package/dist/1448.js +0 -1
  178. package/dist/1636.js +0 -1
  179. package/dist/1708.js +0 -1
  180. package/dist/2156.js +0 -1
  181. package/dist/2326.js +0 -1
  182. package/dist/2328.js +0 -1
  183. package/dist/2636.js +0 -1
  184. package/dist/2636.js.map +0 -1
  185. package/dist/2664.js +0 -1
  186. package/dist/2816.js +0 -1
  187. package/dist/3240.js +0 -1
  188. package/dist/3264.js +0 -1
  189. package/dist/3264.js.map +0 -1
  190. package/dist/370.js +0 -1
  191. package/dist/3724.js +0 -1
  192. package/dist/3724.js.map +0 -1
  193. package/dist/3872.js +0 -1
  194. package/dist/3956.js +0 -1
  195. package/dist/400.js +0 -1
  196. package/dist/400.js.map +0 -1
  197. package/dist/436.js +0 -2
  198. package/dist/436.js.map +0 -1
  199. package/dist/4412.js +0 -1
  200. package/dist/4592.js +0 -1
  201. package/dist/464.js +0 -1
  202. package/dist/4656.js +0 -1
  203. package/dist/4744.js +0 -1
  204. package/dist/4868.js +0 -1
  205. package/dist/5028.js +0 -1
  206. package/dist/5086.js +0 -1
  207. package/dist/5136.js +0 -1
  208. package/dist/5144.js +0 -2
  209. package/dist/5144.js.map +0 -1
  210. package/dist/552.js +0 -1
  211. package/dist/5992.js +0 -1
  212. package/dist/6048.js +0 -1
  213. package/dist/6048.js.map +0 -1
  214. package/dist/6092.js +0 -1
  215. package/dist/6172.js +0 -1
  216. package/dist/6172.js.map +0 -1
  217. package/dist/6352.js +0 -1
  218. package/dist/6504.js +0 -1
  219. package/dist/6676.js +0 -1
  220. package/dist/6676.js.map +0 -1
  221. package/dist/6856.js +0 -1
  222. package/dist/6936.js +0 -1
  223. package/dist/7295.js +0 -1
  224. package/dist/7295.js.map +0 -1
  225. package/dist/7328.js +0 -1
  226. package/dist/7540.js +0 -1
  227. package/dist/7540.js.map +0 -1
  228. package/dist/7640.js +0 -1
  229. package/dist/7724.js +0 -1
  230. package/dist/7936.js +0 -2
  231. package/dist/7936.js.map +0 -1
  232. package/dist/8064.js +0 -1
  233. package/dist/8170.js +0 -1
  234. package/dist/8256.js +0 -1
  235. package/dist/8320.js +0 -1
  236. package/dist/8320.js.map +0 -1
  237. package/dist/8552.js +0 -1
  238. package/dist/8552.js.map +0 -1
  239. package/dist/8570.js +0 -1
  240. package/dist/8684.js +0 -1
  241. package/dist/8698.js +0 -1
  242. package/dist/8720.js +0 -1
  243. package/dist/8728.js +0 -2
  244. package/dist/8728.js.LICENSE.txt +0 -7
  245. package/dist/8728.js.map +0 -1
  246. package/dist/8936.js +0 -1
  247. package/dist/9064.js +0 -2
  248. package/dist/9064.js.map +0 -1
  249. package/dist/9372.js +0 -1
  250. package/dist/9464.js +0 -1
  251. package/dist/9656.js +0 -1
  252. package/dist/9832.js +0 -1
  253. package/dist/9864.js +0 -1
  254. package/dist/9960.js +0 -1
  255. /package/dist/{436.js.LICENSE.txt → 1026.js.LICENSE.txt} +0 -0
  256. /package/dist/{9064.js.LICENSE.txt → 2934.js.LICENSE.txt} +0 -0
  257. /package/dist/{5144.js.LICENSE.txt → 3495.js.LICENSE.txt} +0 -0
@@ -0,0 +1,32 @@
1
+ import { request } from '@playwright/test';
2
+ import * as dotenv from 'dotenv';
3
+
4
+ dotenv.config();
5
+
6
+ /**
7
+ * This configuration is to reuse the signed-in state in the tests
8
+ * by log in only once using the API and then skip the log in step for all the tests.
9
+ *
10
+ * https://playwright.dev/docs/auth#reuse-signed-in-state
11
+ */
12
+
13
+ async function globalSetup() {
14
+ const requestContext = await request.newContext();
15
+ const token = Buffer.from(`${process.env.E2E_USER_ADMIN_USERNAME}:${process.env.E2E_USER_ADMIN_PASSWORD}`).toString(
16
+ 'base64',
17
+ );
18
+ await requestContext.post(`${process.env.E2E_BASE_URL}/ws/rest/v1/session`, {
19
+ data: {
20
+ sessionLocation: process.env.E2E_LOGIN_DEFAULT_LOCATION_UUID,
21
+ locale: 'en',
22
+ },
23
+ headers: {
24
+ 'Content-Type': 'application/json',
25
+ Authorization: `Basic ${token}`,
26
+ },
27
+ });
28
+ await requestContext.storageState({ path: 'e2e/storageState.json' });
29
+ await requestContext.dispose();
30
+ }
31
+
32
+ export default globalSetup;
@@ -0,0 +1 @@
1
+ export * from './test';
@@ -0,0 +1,31 @@
1
+ import { type APIRequestContext, type Page, test as base } from '@playwright/test';
2
+ import { api } from '../fixtures';
3
+ import { type Patient } from '../commands/types';
4
+ import { generateRandomPatient, deletePatient } from '../commands';
5
+
6
+ // This file sets up our custom test harness using the custom fixtures.
7
+ // See https://playwright.dev/docs/test-fixtures#creating-a-fixture for details.
8
+ // If a spec intends to use one of the custom fixtures, the special `test` function
9
+ // exported from this file must be used instead of the default `test` function
10
+ // provided by playwright.
11
+
12
+ export interface CustomTestFixtures {
13
+ loginAsAdmin: Page;
14
+ patient: Patient;
15
+ }
16
+
17
+ export interface CustomWorkerFixtures {
18
+ api: APIRequestContext;
19
+ }
20
+
21
+ export const test = base.extend<CustomTestFixtures, CustomWorkerFixtures>({
22
+ api: [api, { scope: 'worker' }],
23
+ patient: [
24
+ async ({ api }, use) => {
25
+ const patient = await generateRandomPatient(api);
26
+ await use(patient);
27
+ await deletePatient(api, patient.uuid);
28
+ },
29
+ { scope: 'test', auto: true },
30
+ ],
31
+ });
@@ -0,0 +1,27 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import { type APIRequestContext, type PlaywrightWorkerArgs, type WorkerFixture } from '@playwright/test';
3
+
4
+ /**
5
+ * A fixture which initializes an [`APIRequestContext`](https://playwright.dev/docs/api/class-apirequestcontext)
6
+ * that is bound to the configured OpenMRS API server. The context is automatically authenticated
7
+ * using the configured admin account.
8
+ *
9
+ * Use the request context like this:
10
+ * ```ts
11
+ * test('your test', async ({ api }) => {
12
+ * const res = await api.get('patient/1234');
13
+ * await expect(res.ok()).toBeTruthy();
14
+ * });
15
+ * ```
16
+ */
17
+ export const api: WorkerFixture<APIRequestContext, PlaywrightWorkerArgs> = async ({ playwright }, use) => {
18
+ const ctx = await playwright.request.newContext({
19
+ baseURL: `${process.env.E2E_BASE_URL}/ws/rest/v1/`,
20
+ httpCredentials: {
21
+ username: process.env.E2E_USER_ADMIN_USERNAME,
22
+ password: process.env.E2E_USER_ADMIN_PASSWORD,
23
+ },
24
+ });
25
+
26
+ await use(ctx);
27
+ };
@@ -0,0 +1,28 @@
1
+ import { type APIRequestContext, type PlaywrightWorkerArgs, type WorkerFixture } from '@playwright/test';
2
+
3
+ /**
4
+ * A fixture which initializes an [`APIRequestContext`](https://playwright.dev/docs/api/class-apirequestcontext)
5
+ * that is bound to the configured OpenMRS API server. The context is automatically authenticated
6
+ * using the configured admin account.
7
+ *
8
+ * Use the request context like this:
9
+ * ```ts
10
+ * test('your test', async ({ api }) => {
11
+ * const res = await api.get('patient/1234');
12
+ * await expect(res.ok()).toBeTruthy();
13
+ * });
14
+ * ```
15
+ */
16
+
17
+ export const fhirApi: WorkerFixture<APIRequestContext, PlaywrightWorkerArgs> = async ({ playwright }, use) => {
18
+ const fhirctx = await playwright.request.newContext({
19
+ baseURL: `${process.env.E2E_BASE_URL}/ws/fhir2/R4/`,
20
+ httpCredentials: {
21
+ username: process.env.E2E_USER_ADMIN_USERNAME,
22
+ password: process.env.E2E_USER_ADMIN_PASSWORD,
23
+ },
24
+ });
25
+
26
+ // eslint-disable-next-line react-hooks/rules-of-hooks
27
+ await use(fhirctx);
28
+ };
@@ -0,0 +1,2 @@
1
+ export * from './api';
2
+ export * from './fhirApi';
@@ -0,0 +1 @@
1
+ export * from './laboratory-page';
@@ -0,0 +1,9 @@
1
+ import { type Page } from '@playwright/test';
2
+
3
+ export class LaboratoryPage {
4
+ constructor(readonly page: Page) {}
5
+
6
+ async goTo() {
7
+ await this.page.goto('/openmrs/spa/home/laboratory');
8
+ }
9
+ }
@@ -0,0 +1,100 @@
1
+ import { expect } from '@playwright/test';
2
+ import {
3
+ generateRandomTestOrder,
4
+ deleteTestOrder,
5
+ createEncounter,
6
+ deleteEncounter,
7
+ getProvider,
8
+ startVisit,
9
+ endVisit,
10
+ } from '../commands';
11
+ import { test } from '../core';
12
+ import { type Visit } from '@openmrs/esm-framework';
13
+ import { type Encounter, type Provider } from '../commands/types';
14
+ import { type Order } from '@openmrs/esm-patient-common-lib';
15
+ import { LaboratoryPage } from '../pages';
16
+
17
+ let testOrder: Order;
18
+ let encounter: Encounter;
19
+ let orderer: Provider;
20
+ let fullName: string;
21
+ let visit: Visit;
22
+
23
+ test.beforeEach(async ({ api, patient }) => {
24
+ orderer = await getProvider(api);
25
+ visit = await startVisit(api, patient.uuid);
26
+ encounter = await createEncounter(api, patient.uuid, orderer.uuid, visit);
27
+ testOrder = await generateRandomTestOrder(api, patient.uuid, encounter, orderer.uuid);
28
+ fullName = patient.person?.display;
29
+ });
30
+
31
+ test.describe('Running laboratory order tests sequentially', () => {
32
+ test('Add lab results via the lab app', async ({ page }) => {
33
+ const laboratoryPage = new LaboratoryPage(page);
34
+ await test.step('When I visit the Laboratory section', async () => {
35
+ await laboratoryPage.goTo();
36
+ await expect(page).toHaveURL(process.env.E2E_BASE_URL + `/spa/home/laboratory`);
37
+ });
38
+
39
+ await test.step('And I select the patient and the order for which the results need to be added', async () => {
40
+ await expect(page.getByRole('tab', { name: 'Tests ordered' })).toBeVisible();
41
+ await page
42
+ .getByRole('row', { name: new RegExp(`Expand current row ${fullName}`) })
43
+ .getByLabel('Expand current row')
44
+ .click();
45
+ await expect(page.getByText(/Status:Order not picked/i)).toBeVisible();
46
+ await expect(page.getByRole('cell', { name: 'serum glucose' })).toBeVisible();
47
+ });
48
+
49
+ await test.step('Then I click the Pick up lab request button', async () => {
50
+ await page.getByRole('button', { name: 'Pick Lab Request' }).first().click();
51
+ await page.getByRole('button', { name: 'Pick up lab request' }).click();
52
+ await expect(page.getByText(/You have successfully picked an order/i)).toBeVisible();
53
+ });
54
+
55
+ await test.step('Then I click In progress tab', async () => {
56
+ await page.getByRole('tab', { name: 'In progress' }).click();
57
+ });
58
+
59
+ await test.step('And I select the patient and the order for which the results need to be added', async () => {
60
+ await page
61
+ .getByRole('row', { name: new RegExp(`Expand current row ${fullName}`) })
62
+ .getByRole('button', { name: 'Expand current row' })
63
+ .click();
64
+ await expect(page.getByText('Inprogress')).toBeVisible();
65
+ await expect(page.getByRole('cell', { name: 'serum glucose' })).toBeVisible();
66
+ });
67
+
68
+ await test.step('Then I click Add Lab results form action and enters the result value', async () => {
69
+ await page.getByRole('button', { name: 'Add lab results' }).click();
70
+ await page.getByRole('spinbutton', { name: 'serum glucose (>= 0' }).fill('35');
71
+ });
72
+
73
+ await test.step('And I click on the `Save and close` button', async () => {
74
+ await page.getByRole('button', { name: 'Save and close' }).click();
75
+ });
76
+
77
+ await test.step('Then I should see a success notification', async () => {
78
+ await expect(page.getByText(/Lab results for .* have been successfully updated/i)).toBeVisible();
79
+ });
80
+
81
+ await test.step('Then I click the completed tab', async () => {
82
+ await page.getByRole('tab', { name: 'Completed' }).click();
83
+ });
84
+
85
+ await test.step('And I select the patient and confirm the result was added with completed status', async () => {
86
+ await page
87
+ .getByRole('row', { name: new RegExp(`Expand current row ${fullName}`) })
88
+ .getByRole('button', { name: 'Expand current row' })
89
+ .click();
90
+ await expect(page.getByLabel('Structured list section').getByText('Completed')).toBeVisible();
91
+ await expect(page.getByRole('cell', { name: 'serum glucose' })).toBeVisible();
92
+ });
93
+ });
94
+ });
95
+
96
+ test.afterEach(async ({ api }) => {
97
+ await endVisit(api, visit);
98
+ await deleteEncounter(api, encounter.uuid);
99
+ await deleteTestOrder(api, testOrder.uuid);
100
+ });
@@ -0,0 +1,16 @@
1
+ import { expect } from '@playwright/test';
2
+ import { test } from '../core';
3
+ import { LaboratoryPage } from '../pages';
4
+
5
+ const url = process.env.E2E_BASE_URL;
6
+
7
+ test('View test orders', async ({ page }) => {
8
+ const laboratoryPage = new LaboratoryPage(page);
9
+
10
+ await test.step('Given I am on the laboratory page', async () => {
11
+ await laboratoryPage.goTo();
12
+ });
13
+ await test.step('Then I should see the test orders', async () => {
14
+ await expect(page).toHaveURL(url + `/spa/home/laboratory`);
15
+ });
16
+ });
@@ -0,0 +1,34 @@
1
+ # syntax=docker/dockerfile:1.3
2
+ FROM --platform=$BUILDPLATFORM node:18-alpine as dev
3
+
4
+ ARG APP_SHELL_VERSION=next
5
+
6
+ RUN mkdir -p /app
7
+ WORKDIR /app
8
+
9
+ COPY . .
10
+
11
+ RUN npm_config_legacy_peer_deps=true npm install -g openmrs@${APP_SHELL_VERSION:-next}
12
+ ARG CACHE_BUST
13
+ RUN npm_config_legacy_peer_deps=true openmrs assemble --manifest --mode config --config spa-assemble-config.json --target ./spa
14
+
15
+ FROM --platform=$BUILDPLATFORM openmrs/openmrs-reference-application-3-frontend:nightly as frontend
16
+ FROM nginx:1.23-alpine
17
+
18
+ RUN apk update && \
19
+ apk upgrade && \
20
+ # add more utils for sponge to support our startup script
21
+ apk add --no-cache moreutils
22
+
23
+ # clear any default files installed by nginx
24
+ RUN rm -rf /usr/share/nginx/html/*
25
+
26
+ COPY --from=frontend /etc/nginx/nginx.conf /etc/nginx/nginx.conf
27
+ # this assumes that NOTHING in the framework is in a subdirectory
28
+ COPY --from=frontend /usr/share/nginx/html/* /usr/share/nginx/html/
29
+ COPY --from=frontend /usr/local/bin/startup.sh /usr/local/bin/startup.sh
30
+ RUN chmod +x /usr/local/bin/startup.sh
31
+
32
+ COPY --from=dev /app/spa/ /usr/share/nginx/html/
33
+
34
+ CMD ["/usr/local/bin/startup.sh"]
@@ -0,0 +1,24 @@
1
+ # This docker compose file is used to create a backend environment for the e2e.yml workflow.
2
+ # The images are pre-filled with data so that the backend environment can be started within a short time.
3
+ version: "3.7"
4
+
5
+ services:
6
+ gateway:
7
+ image: openmrs/openmrs-reference-application-3-gateway:${TAG:-nightly}
8
+ ports:
9
+ - "8080:80"
10
+
11
+ frontend:
12
+ build:
13
+ context: .
14
+ environment:
15
+ SPA_PATH: /openmrs/spa
16
+ API_URL: /openmrs
17
+
18
+ backend:
19
+ image: openmrs/openmrs-reference-application-3-backend:nightly-with-data
20
+ depends_on:
21
+ - db
22
+
23
+ db:
24
+ image: openmrs/openmrs-reference-application-3-db:nightly-with-data
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash -eu
2
+
3
+ # get the dir containing the script
4
+ script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
5
+ # create a temporary working directory
6
+ working_dir=$(mktemp -d "${TMPDIR:-/tmp/}openmrs-e2e-frontends.XXXXXXXXXX")
7
+ # get a list of all the apps in this workspace
8
+ apps=$(yarn workspaces list --json | jq -r 'select(.name | test("-app")) | .name')
9
+ # this array will hold all of the packed app names
10
+ app_names=()
11
+
12
+ echo "Creating packed archives of apps..."
13
+ # for each app
14
+ for app in $apps
15
+ do
16
+ # @openmrs/esm-whatever -> _openmrs_esm_whatever
17
+ app_name=$(echo "$app" | tr '[:punct:]' '_');
18
+ # add to our array
19
+ app_names+=("$app_name.tgz");
20
+ # run yarn pack for our app and add it to the working directory
21
+ # yarn workspace "$app" pack -o "$working_dir/$app_name.tgz" >/dev/null;
22
+ yarn pack -o "$working_dir/$app_name.tgz" >/dev/null
23
+ done;
24
+ echo "Created packed app archives"
25
+
26
+ echo "Creating dynamic spa-assemble-config.json..."
27
+ # dynamically assemble our list of frontend modules, prepending the login app and
28
+ # primary navigation apps; apps will all be in the /app directory of the Docker
29
+ # container
30
+ jq -n \
31
+ --arg apps "$apps" \
32
+ --arg app_names "$(echo ${app_names[@]})" \
33
+ '{
34
+ "@openmrs/esm-primary-navigation-app": "next",
35
+ "@openmrs/esm-home-app": "next",
36
+ "@openmrs/esm-patient-chart-app": "next",
37
+ "@openmrs/esm-patient-banner-app": "next",
38
+ "@openmrs/esm-patient-orders-app": "next",
39
+ "@openmrs/esm-patient-tests-app": "next"
40
+ } + (
41
+ ($apps | split("\n")) as $apps | ($app_names | split(" ") | map("/app/" + .)) as $app_files
42
+ | [$apps, $app_files]
43
+ | transpose
44
+ | map({"key": .[0], "value": .[1]})
45
+ | from_entries
46
+ )' | jq '{"frontendModules": .}' > "$working_dir/spa-assemble-config.json"
47
+ echo "Created dynamic spa-assemble-config.json"
48
+
49
+ echo "Copying Docker configuration..."
50
+ cp "$script_dir/Dockerfile" "$working_dir/Dockerfile"
51
+ cp "$script_dir/docker-compose.yml" "$working_dir/docker-compose.yml"
52
+
53
+ cd $working_dir
54
+ echo "Starting Docker containers..."
55
+ # CACHE_BUST to ensure the assemble step is always run
56
+ docker compose build --build-arg CACHE_BUST=$(date +%s) frontend
57
+ docker compose up -d
58
+
package/example.env ADDED
@@ -0,0 +1,7 @@
1
+ # This is an example environment file for configuring dynamic values.
2
+ E2E_BASE_URL=http://localhost:8080/openmrs
3
+ E2E_USER_ADMIN_USERNAME=admin
4
+ E2E_USER_ADMIN_PASSWORD=Admin123
5
+ E2E_LOGIN_DEFAULT_LOCATION_UUID=44c3efb0-2583-4c80-a79e-1f756a03c0a1
6
+ # The above location UUID is for the "Outpatient Clinic" location in the reference application
7
+
package/jest.config.js CHANGED
@@ -5,9 +5,9 @@ const path = require('path');
5
5
  module.exports = {
6
6
  clearMocks: true,
7
7
  transform: {
8
- '^.+\\.tsx?$': ['@swc/jest'],
8
+ '^.+\\.[jt]sx?$': ['@swc/jest'],
9
9
  },
10
- transformIgnorePatterns: ['/node_modules/(?!@openmrs)'],
10
+ transformIgnorePatterns: ['/node_modules/(?!@openmrs|.+\\.pnp\\.[^\\/]+$)'],
11
11
  moduleNameMapper: {
12
12
  '^dexie$': require.resolve('dexie'),
13
13
  '@openmrs/esm-framework': '@openmrs/esm-framework/mock',
@@ -18,6 +18,14 @@ module.exports = {
18
18
  '^@tools/(.*)$': path.resolve(__dirname, 'tools', '$1'),
19
19
  '^@mocks/(.*)$': path.resolve(__dirname, '__mocks__', '$1'),
20
20
  },
21
+ collectCoverageFrom: [
22
+ '!**/node_modules/**',
23
+ '!**/e2e/**',
24
+ ],
25
+ testPathIgnorePatterns: [
26
+ "/node_modules/",
27
+ "/e2e/" // Ignore the e2e directory containing Playwright tests
28
+ ],
21
29
  setupFilesAfterEnv: ['<rootDir>/tools/setup-tests.ts'],
22
30
  testEnvironment: 'jsdom',
23
31
  testEnvironmentOptions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-laboratory-app",
3
- "version": "1.1.2-pre.88",
3
+ "version": "1.2.1-pre.590",
4
4
  "license": "MPL-2.0",
5
5
  "description": "An O3 frontend module for managing laboratory requests and queues",
6
6
  "browser": "dist/openmrs-esm-laboratory-app.js",
@@ -18,7 +18,8 @@
18
18
  "verify": "turbo lint typescript test --color",
19
19
  "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts' --config ./tools/i18next-parser.config.js",
20
20
  "coverage": "yarn test -- --coverage",
21
- "postinstall": "husky install"
21
+ "postinstall": "husky install",
22
+ "test-e2e": "playwright test"
22
23
  },
23
24
  "browserslist": [
24
25
  "extends browserslist-config-openmrs"
@@ -44,7 +45,7 @@
44
45
  "*.{css,scss,ts,tsx}": "prettier --write --list-different"
45
46
  },
46
47
  "dependencies": {
47
- "@carbon/react": "^1.76.0",
48
+ "@carbon/react": "^1.83.0",
48
49
  "lodash-es": "^4.17.21",
49
50
  "react-hook-form": "^7.52.1"
50
51
  },
@@ -59,6 +60,7 @@
59
60
  "devDependencies": {
60
61
  "@openmrs/esm-framework": "next",
61
62
  "@openmrs/esm-patient-common-lib": "next",
63
+ "@playwright/test": "^1.52.0",
62
64
  "@swc/cli": "^0.1.62",
63
65
  "@swc/core": "^1.3.62",
64
66
  "@swc/jest": "^0.2.26",
@@ -66,6 +68,7 @@
66
68
  "@testing-library/jest-dom": "^5.16.5",
67
69
  "@testing-library/react": "^13.4.0",
68
70
  "@testing-library/user-event": "^14.4.3",
71
+ "@types/dotenv": "^8.2.3",
69
72
  "@types/jest": "^28.1.8",
70
73
  "@types/react": "^18.2.8",
71
74
  "@types/react-dom": "^18.2.4",
@@ -98,9 +101,9 @@
98
101
  "swc-loader": "^0.2.3",
99
102
  "turbo": "^2.5.2",
100
103
  "typescript": "^4.9.5",
101
- "webpack": "^5.88.1",
102
- "webpack-cli": "^5.1.3"
104
+ "webpack": "^5.99.9",
105
+ "webpack-cli": "^6.0.1"
103
106
  },
104
107
  "packageManager": "yarn@4.9.1",
105
- "stableVersion": "1.1.1"
108
+ "stableVersion": "1.2.0"
106
109
  }
@@ -0,0 +1,36 @@
1
+ import { devices, type PlaywrightTestConfig } from '@playwright/test';
2
+ import { config as dotenvConfig } from 'dotenv';
3
+ import { resolve } from 'node:path';
4
+ dotenvConfig({ path: resolve(process.cwd(), '.env') });
5
+ dotenvConfig();
6
+
7
+ // See https://playwright.dev/docs/test-configuration.
8
+ const config: PlaywrightTestConfig = {
9
+ testDir: './e2e/specs',
10
+ timeout: 3 * 60 * 1000,
11
+ expect: {
12
+ timeout: 40 * 1000,
13
+ },
14
+ fullyParallel: true,
15
+ forbidOnly: !!process.env.CI,
16
+ retries: 0,
17
+ reporter: process.env.CI ? [['junit', { outputFile: 'results.xml' }], ['html']] : [['html']],
18
+ globalSetup: require.resolve('./e2e/core/global-setup'),
19
+ use: {
20
+ baseURL: `${process.env.E2E_BASE_URL}/spa/`,
21
+ storageState: 'e2e/storageState.json',
22
+ video: 'retain-on-failure',
23
+ trace: 'retain-on-failure',
24
+ },
25
+ projects: [
26
+ {
27
+ name: 'chromium',
28
+ use: {
29
+ ...devices['Desktop Chrome'],
30
+ channel: 'chromium',
31
+ },
32
+ },
33
+ ],
34
+ };
35
+
36
+ export default config;