@openmrs/esm-openconceptlab-app 3.0.1-pre.10
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.
- package/.turbo/turbo-build.log +36 -0
- package/LICENSE +401 -0
- package/README.md +8 -0
- package/dist/316.js +2 -0
- package/dist/316.js.LICENSE.txt +19 -0
- package/dist/452.js +1 -0
- package/dist/574.js +1 -0
- package/dist/645.js +2 -0
- package/dist/645.js.LICENSE.txt +20 -0
- package/dist/709.js +1 -0
- package/dist/784.js +2 -0
- package/dist/784.js.LICENSE.txt +9 -0
- package/dist/858.js +2 -0
- package/dist/858.js.LICENSE.txt +1 -0
- package/dist/862.js +1 -0
- package/dist/main.js +1 -0
- package/dist/openmrs-esm-openconceptlab-app.js +1 -0
- package/dist/openmrs-esm-openconceptlab-app.js.buildmanifest.json +265 -0
- package/dist/openmrs-esm-openconceptlab-app.old +1 -0
- package/package.json +53 -0
- package/src/config-schema.ts +5 -0
- package/src/declarations.d.tsx +2 -0
- package/src/import/import.component.scss +27 -0
- package/src/import/import.component.tsx +190 -0
- package/src/import/import.resource.ts +40 -0
- package/src/import/import.test.tsx +103 -0
- package/src/index.ts +61 -0
- package/src/previous-imports/import-overview/import-items.component.scss +24 -0
- package/src/previous-imports/import-overview/import-items.component.tsx +124 -0
- package/src/previous-imports/import-overview/import-items.resource.ts +10 -0
- package/src/previous-imports/import-overview/import-items.test.tsx +93 -0
- package/src/previous-imports/import-overview/import-overview.component.scss +11 -0
- package/src/previous-imports/import-overview/import-overview.component.tsx +65 -0
- package/src/previous-imports/import-overview/import-overview.test.tsx +83 -0
- package/src/previous-imports/previous-imports.component.scss +39 -0
- package/src/previous-imports/previous-imports.component.tsx +136 -0
- package/src/previous-imports/previous-imports.resource.ts +17 -0
- package/src/previous-imports/previous-imports.test.tsx +77 -0
- package/src/root.component.scss +23 -0
- package/src/root.component.tsx +39 -0
- package/src/root.test.tsx +22 -0
- package/src/setup-tests.ts +1 -0
- package/src/subscription/subscription.component.scss +27 -0
- package/src/subscription/subscription.component.tsx +244 -0
- package/src/subscription/subscription.resource.ts +40 -0
- package/src/subscription/subscription.test.tsx +181 -0
- package/src/types.ts +34 -0
- package/translations/en.json +51 -0
- package/tsconfig.json +23 -0
- package/webpack.config.js +1 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import Subscription from './subscription.component';
|
|
5
|
+
import { openmrsFetch, showNotification } from '@openmrs/esm-framework';
|
|
6
|
+
import { renderWithSwr } from '../../../../tools/test-helpers';
|
|
7
|
+
import { deleteSubscription, updateSubscription } from './subscription.resource';
|
|
8
|
+
import { mockSubscription } from '../../../../__mocks__/openconceptlab.mock';
|
|
9
|
+
|
|
10
|
+
const mockOpenmrsFetch = openmrsFetch as jest.Mock;
|
|
11
|
+
const mockUpdateSubscription = updateSubscription as jest.Mock;
|
|
12
|
+
const mockDeleteSubscription = deleteSubscription as jest.Mock;
|
|
13
|
+
const mockShowNotification = showNotification as jest.Mock;
|
|
14
|
+
|
|
15
|
+
jest.mock('./subscription.resource', () => {
|
|
16
|
+
const originalModule = jest.requireActual('./subscription.resource');
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
...originalModule,
|
|
20
|
+
updateSubscription: jest.fn(),
|
|
21
|
+
deleteSubscription: jest.fn(),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
jest.mock('@openmrs/esm-framework', () => {
|
|
26
|
+
const originalModule = jest.requireActual('@openmrs/esm-framework');
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...originalModule,
|
|
30
|
+
showNotification: jest.fn(),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe(`Subscription component`, () => {
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
mockShowNotification.mockReset();
|
|
37
|
+
mockUpdateSubscription.mockReset();
|
|
38
|
+
mockOpenmrsFetch.mockReset();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it(`renders without dying`, () => {
|
|
42
|
+
renderSubscriptionComponent();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it(`renders the empty forms`, async () => {
|
|
46
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [] } });
|
|
47
|
+
renderSubscriptionComponent();
|
|
48
|
+
await waitForLoadingToFinish();
|
|
49
|
+
|
|
50
|
+
expect(screen.getByText('Setup Subscription')).toBeVisible();
|
|
51
|
+
expect(screen.getByRole('heading', { name: 'Unsubscribe' })).toBeVisible();
|
|
52
|
+
expect(screen.getByRole('button', { name: 'danger Unsubscribe' })).toBeDisabled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it(`renders the subscription if a subscription exists`, async () => {
|
|
56
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } });
|
|
57
|
+
renderSubscriptionComponent();
|
|
58
|
+
await waitForLoadingToFinish();
|
|
59
|
+
await waitForLoadingSubscription();
|
|
60
|
+
|
|
61
|
+
expect(screen.getByLabelText('Subscription URL')).toHaveValue(mockSubscription.url);
|
|
62
|
+
expect(screen.getByLabelText('Token')).toHaveValue(mockSubscription.token);
|
|
63
|
+
expect(screen.getByLabelText('Subscribe to SNAPSHOT versions (not recommended)')).not.toBeChecked();
|
|
64
|
+
expect(
|
|
65
|
+
screen.getByLabelText('Disable validation (should be used with care for well curated collections or sources)'),
|
|
66
|
+
).not.toBeChecked();
|
|
67
|
+
expect(screen.getByRole('button', { name: 'danger Unsubscribe' })).toBeEnabled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it(`allows adding a new subscription`, async () => {
|
|
71
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [] } });
|
|
72
|
+
renderSubscriptionComponent();
|
|
73
|
+
await waitForLoadingToFinish();
|
|
74
|
+
|
|
75
|
+
const urlInputField = screen.getByLabelText('Subscription URL');
|
|
76
|
+
const tokenInputField = screen.getByLabelText('Token');
|
|
77
|
+
const saveButton = screen.getByRole('button', { name: 'Save changes' });
|
|
78
|
+
|
|
79
|
+
mockUpdateSubscription.mockReturnValueOnce({ status: 201, ok: true });
|
|
80
|
+
|
|
81
|
+
await waitFor(() => userEvent.type(urlInputField, mockSubscription.url));
|
|
82
|
+
await waitFor(() => userEvent.type(tokenInputField, mockSubscription.token));
|
|
83
|
+
await waitFor(() => userEvent.click(saveButton));
|
|
84
|
+
|
|
85
|
+
expect(mockUpdateSubscription).toHaveBeenCalledWith(
|
|
86
|
+
expect.objectContaining(mockSubscription),
|
|
87
|
+
new AbortController(),
|
|
88
|
+
);
|
|
89
|
+
expect(mockUpdateSubscription).toHaveBeenCalledTimes(1);
|
|
90
|
+
|
|
91
|
+
expect(mockShowNotification).toHaveBeenCalledWith(
|
|
92
|
+
expect.objectContaining({
|
|
93
|
+
description: 'Subscription created successfully',
|
|
94
|
+
kind: 'success',
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
expect(mockShowNotification).toHaveBeenCalledTimes(1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it(`allows changing the saved subscription`, async () => {
|
|
101
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } });
|
|
102
|
+
renderSubscriptionComponent();
|
|
103
|
+
await waitForLoadingToFinish();
|
|
104
|
+
await waitForLoadingSubscription();
|
|
105
|
+
|
|
106
|
+
const urlInputField = screen.getByLabelText('Subscription URL');
|
|
107
|
+
const tokenInputField = screen.getByLabelText('Token');
|
|
108
|
+
const saveButton = screen.getByRole('button', { name: 'Save changes' });
|
|
109
|
+
|
|
110
|
+
mockUpdateSubscription.mockReturnValueOnce({ status: 200, ok: true });
|
|
111
|
+
|
|
112
|
+
await waitFor(() => userEvent.clear(urlInputField));
|
|
113
|
+
await waitFor(() => userEvent.clear(tokenInputField));
|
|
114
|
+
await waitFor(() =>
|
|
115
|
+
userEvent.type(urlInputField, 'https://api.openconceptlab.org/orgs/openmrs/collections/DemoQueueConcepts/2'),
|
|
116
|
+
);
|
|
117
|
+
await waitFor(() => userEvent.type(tokenInputField, 'token123'));
|
|
118
|
+
await waitFor(() => userEvent.click(saveButton));
|
|
119
|
+
|
|
120
|
+
expect(mockUpdateSubscription).toHaveBeenCalledWith(
|
|
121
|
+
expect.objectContaining({
|
|
122
|
+
...mockSubscription,
|
|
123
|
+
url: 'https://api.openconceptlab.org/orgs/openmrs/collections/DemoQueueConcepts/2',
|
|
124
|
+
token: 'token123',
|
|
125
|
+
}),
|
|
126
|
+
new AbortController(),
|
|
127
|
+
);
|
|
128
|
+
expect(mockUpdateSubscription).toHaveBeenCalledTimes(1);
|
|
129
|
+
|
|
130
|
+
expect(mockShowNotification).toHaveBeenCalledWith(
|
|
131
|
+
expect.objectContaining({
|
|
132
|
+
description: 'Subscription updated successfully',
|
|
133
|
+
kind: 'success',
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
expect(mockShowNotification).toHaveBeenCalledTimes(1);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it(`allows removing the saved subscription`, async () => {
|
|
140
|
+
mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } });
|
|
141
|
+
renderSubscriptionComponent();
|
|
142
|
+
await waitForLoadingToFinish();
|
|
143
|
+
await waitForLoadingSubscription();
|
|
144
|
+
|
|
145
|
+
const unsubscribeButton = screen.getByRole('button', { name: 'danger Unsubscribe' });
|
|
146
|
+
|
|
147
|
+
mockDeleteSubscription.mockReturnValueOnce({ status: 204 });
|
|
148
|
+
|
|
149
|
+
await waitFor(() => userEvent.click(unsubscribeButton));
|
|
150
|
+
|
|
151
|
+
expect(mockDeleteSubscription).toHaveBeenCalledWith(
|
|
152
|
+
expect.objectContaining(mockSubscription),
|
|
153
|
+
new AbortController(),
|
|
154
|
+
);
|
|
155
|
+
expect(mockDeleteSubscription).toHaveBeenCalledTimes(1);
|
|
156
|
+
|
|
157
|
+
expect(mockShowNotification).toHaveBeenCalledWith(
|
|
158
|
+
expect.objectContaining({
|
|
159
|
+
description: 'Successfully unsubscribed',
|
|
160
|
+
kind: 'success',
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
expect(mockShowNotification).toHaveBeenCalledTimes(1);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
function renderSubscriptionComponent() {
|
|
168
|
+
renderWithSwr(<Subscription />);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function waitForLoadingToFinish() {
|
|
172
|
+
return waitFor(() => {
|
|
173
|
+
expect(screen.getByText('Setup Subscription')).toBeVisible(), { timeout: 2000 };
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function waitForLoadingSubscription() {
|
|
178
|
+
return waitFor(() => {
|
|
179
|
+
expect(screen.getByLabelText('Subscription URL')).not.toHaveValue(''), { timeout: 2000 };
|
|
180
|
+
});
|
|
181
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface Subscription {
|
|
2
|
+
uuid: string;
|
|
3
|
+
url: string;
|
|
4
|
+
token: string;
|
|
5
|
+
subscribedToSnapshot?: boolean;
|
|
6
|
+
validationType?: 'NONE' | 'FULL';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Import {
|
|
10
|
+
uuid: string;
|
|
11
|
+
localDateStarted: Date;
|
|
12
|
+
localDateStopped: Date;
|
|
13
|
+
importTime: string;
|
|
14
|
+
errorMessage: string;
|
|
15
|
+
importProgress: number;
|
|
16
|
+
allItemsCount: number;
|
|
17
|
+
addedItemsCount: number;
|
|
18
|
+
errorItemsCount: number;
|
|
19
|
+
ignoredErrorsCount: number;
|
|
20
|
+
updatedItemsCount: number;
|
|
21
|
+
upToDateItemsCount: number;
|
|
22
|
+
retiredItemsCount: number;
|
|
23
|
+
unretiredItemsCount: number;
|
|
24
|
+
status: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ImportItem {
|
|
28
|
+
uuid: string;
|
|
29
|
+
errorMessage: string;
|
|
30
|
+
type: 'CONCEPT' | 'MAPPING';
|
|
31
|
+
versionUrl: string;
|
|
32
|
+
updatedOn: Date;
|
|
33
|
+
state: 'ADDED' | 'UPDATED' | 'RETIRED' | 'UNRETIRED' | 'ERROR' | 'IGNORED_ERROR' | 'UP_TO_DATE' | 'DUPLICATE';
|
|
34
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"advancedOptions": "Advanced Options",
|
|
3
|
+
"apiToken": "Token",
|
|
4
|
+
"cancelButton": "Cancel changes",
|
|
5
|
+
"cancelledChanges": "Cancelled changes successfully",
|
|
6
|
+
"completedOn": "Completed on",
|
|
7
|
+
"conceptOrMapping": "Concept/Mapping",
|
|
8
|
+
"conceptsAdded": "concepts added",
|
|
9
|
+
"conceptsRetired": "concepts retired",
|
|
10
|
+
"conceptsUpdated": "concepts updated",
|
|
11
|
+
"conceptsUpToDate": "concepts up to date",
|
|
12
|
+
"dateAndTime": "Date and Time",
|
|
13
|
+
"disableValidationText": "Disable validation (should be used with care for well curated collections or sources)",
|
|
14
|
+
"duration": "Duration",
|
|
15
|
+
"errorDeletingSubscription": "Error occured while deleting the subscription",
|
|
16
|
+
"errorSavingSubscription": "Error occured while saving the subscription",
|
|
17
|
+
"errorsFound": "errors found",
|
|
18
|
+
"errorsIgnored": "errors ignored",
|
|
19
|
+
"fileAdded": "File Added",
|
|
20
|
+
"fileFormatError": "Only .zip files are allowed",
|
|
21
|
+
"import": "Import",
|
|
22
|
+
"importConcepts": "Import Concepts",
|
|
23
|
+
"importFailed": "Import failed",
|
|
24
|
+
"importFromFile": "Import from file",
|
|
25
|
+
"importFromFileDragInfo": "Drag and drop file here or click to upload",
|
|
26
|
+
"importFromFileHeading": "Import from file (Offline)",
|
|
27
|
+
"importFromFileInfo": "You can import data without setting up a subscription by using this option.",
|
|
28
|
+
"importFromSubscription": "Import from Subscription",
|
|
29
|
+
"importInstructions": "Avoid importing during data entry hours, because the operation may significantly slow down the system. You can either use the subscription or a zip file to import concepts.",
|
|
30
|
+
"importItemsFetchError": "Error occured while fetching the import items",
|
|
31
|
+
"importSuccess": "Import started successfully",
|
|
32
|
+
"message": "Message",
|
|
33
|
+
"moduleTitle": "OCL Subscription Module",
|
|
34
|
+
"noFileSelected": "No file selected",
|
|
35
|
+
"noSubscriptionError": "No saved subscription",
|
|
36
|
+
"previousImports": "Previous Imports",
|
|
37
|
+
"previousImportsFetchError": "Error occured while fetching the imports",
|
|
38
|
+
"result": "Result:",
|
|
39
|
+
"setupSubscription": "Setup Subscription",
|
|
40
|
+
"startedOn": "Started on",
|
|
41
|
+
"status": "Status",
|
|
42
|
+
"subscribeButton": "Save changes",
|
|
43
|
+
"subscribeToSnapshotText": "Subscribe to SNAPSHOT versions (not recommended)",
|
|
44
|
+
"subscription": "Subscription",
|
|
45
|
+
"subscriptionDeleted": "Successfully unsubscribed",
|
|
46
|
+
"subscriptionError": "Error occured while fetching the subscription",
|
|
47
|
+
"subscriptionUrl": "Subscription URL",
|
|
48
|
+
"unsubscribe": "Unsubscribe",
|
|
49
|
+
"unsubscribeButton": "Unsubscribe",
|
|
50
|
+
"unsubscribeInfo": "If you unsubscribe, no concepts will be deleted nor changed. All information about subscription will be deleted from your system."
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"esModuleInterop": true,
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"allowSyntheticDefaultImports": true,
|
|
6
|
+
"jsx": "react",
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"lib": [
|
|
10
|
+
"dom",
|
|
11
|
+
"es5",
|
|
12
|
+
"scripthost",
|
|
13
|
+
"es2015",
|
|
14
|
+
"es2015.promise",
|
|
15
|
+
"es2016.array.include",
|
|
16
|
+
"es2018",
|
|
17
|
+
"es2020"
|
|
18
|
+
],
|
|
19
|
+
"noEmit": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*"],
|
|
22
|
+
"exclude": ["src/**/*.test.tsx", "src/**/*.outdated.tsx"]
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("openmrs/default-webpack-config");
|