diff --git a/imports/_test/png.ts b/imports/_test/png.ts
index 4501c40d2..b374286f1 100644
--- a/imports/_test/png.ts
+++ b/imports/_test/png.ts
@@ -3,6 +3,8 @@ import pngDataURL from '../lib/png/dataURL';
const base64Encoded =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
+export const randomPNGBase64 = () => base64Encoded;
+
export const randomPNGBuffer = async () => {
const {Buffer} = await import('buffer');
return Buffer.from(base64Encoded, 'base64');
diff --git a/imports/api/_dev/populate/eids.ts b/imports/api/_dev/populate/eids.ts
index b454bf130..93a244e1d 100644
--- a/imports/api/_dev/populate/eids.ts
+++ b/imports/api/_dev/populate/eids.ts
@@ -3,6 +3,7 @@ import {faker} from '@faker-js/faker';
import format from 'date-fns/format';
import {makeTemplate} from '../../../_test/fixtures';
+import {randomPNGBase64} from '../../../_test/png';
const DATE_FORMAT = 'yyyyMMdd';
const AGE_MAX = 130;
@@ -51,3 +52,59 @@ export const newEidData = makeTemplate({
zip: () => faker.location.zipCode(),
},
});
+
+export const exampleEidXML = (options?: {
+ name?: string;
+ firstname?: string;
+ middlenames?: string;
+ nationality?: string;
+ placeofbirth?: string;
+}) => {
+ const {name, firstname, middlenames, nationality, placeofbirth} = {
+ name: 'Name',
+ firstname: 'First Name',
+ middlenames: 'M',
+ nationality: 'Belge',
+ placeofbirth: 'Bruxelles',
+ ...options,
+ };
+ // TODO: Generalizes and escape values.
+ return `
+
+
+ ${name}
+ ${firstname}
+ ${middlenames}
+ ${nationality}
+ ${placeofbirth}
+ ${randomPNGBase64()}
+
+
+ Bruxelles
+
+
+ Rue de la montagne 58
+ 1000
+ Bruxelles
+
+
+ ROOT
+ CITIZENCA
+ AUTHENTICATION
+ SIGNING
+ RRN
+
+`;
+};
diff --git a/imports/ui/patients/SelectablePatientCard.tsx b/imports/ui/patients/SelectablePatientCard.tsx
index d85743b42..17277a9e5 100644
--- a/imports/ui/patients/SelectablePatientCard.tsx
+++ b/imports/ui/patients/SelectablePatientCard.tsx
@@ -8,6 +8,8 @@ import RadioButtonUncheckedOutlinedIcon from '@mui/icons-material/RadioButtonUnc
import type PropsOf from '../../lib/types/PropsOf';
+import useUniqueId from '../hooks/useUniqueId';
+
import GenericStaticPatientCard from './GenericStaticPatientCard';
type Props = {
@@ -75,12 +77,14 @@ const SelectablePatientCard = ({
patient,
...rest
}: Props) => {
+ const labelId = useUniqueId('selectable-patient-card');
return (
-
+
{
onClick(patient);
diff --git a/test/app/client/fixtures.ts b/test/app/client/fixtures.ts
index 015626b70..38314d170 100644
--- a/test/app/client/fixtures.ts
+++ b/test/app/client/fixtures.ts
@@ -204,21 +204,27 @@ export const createNewPatient = async (app: App, {firstname, lastname}) => {
};
export const searchResultsForQuery = async (
- {userWithoutPointerEventsCheck, findByRole, getByRole}: App,
+ {userWithoutPointerEventsCheck, findByRole}: App,
query,
) => {
await userWithoutPointerEventsCheck.type(
- getByRole('searchbox', {name: 'Patient search'}),
+ await findByRole('searchbox', {name: 'Patient search'}),
query,
);
await findByRole('heading', {name: /^Results for query/}, {timeout: 5000});
};
-export const searchForPatient = async (app: App, query, {name, id}) => {
+export const searchForPatient = async (
+ app: App,
+ query: string,
+ {name, id}: {name: string; id?: string},
+) => {
const {findByRole, user} = app;
await searchResultsForQuery(app, query);
await user.click(await findByRole('link', {name}, {timeout: 5000}));
- await findByRole('heading', {name: `/patient/${id}`}, {timeout: 10_000});
+ if (id !== undefined) {
+ await findByRole('heading', {name: `/patient/${id}`}, {timeout: 10_000});
+ }
};
type EditConsultationOptions = {
diff --git a/test/app/client/patient/eids.app-tests.ts b/test/app/client/patient/eids.app-tests.ts
new file mode 100644
index 000000000..0cde8dd45
--- /dev/null
+++ b/test/app/client/patient/eids.app-tests.ts
@@ -0,0 +1,139 @@
+import {fireEvent} from '@testing-library/dom';
+
+import {exampleEidXML} from '../../../../imports/api/_dev/populate/eids';
+
+import {
+ client,
+ randomPassword,
+ randomUserId,
+} from '../../../../imports/_test/fixtures';
+import {
+ setupApp,
+ createUserWithPasswordAndLogin,
+ searchForPatient,
+} from '../fixtures';
+
+type Item = string | File;
+
+const createFileList = (files: File[]): FileList => {
+ const list: FileList & Iterable = {
+ ...files,
+ length: files.length,
+ item: (index: number) => list[index]!,
+ [Symbol.iterator]: () => files[Symbol.iterator](),
+ };
+ list.constructor = FileList;
+ Object.setPrototypeOf(list, FileList.prototype);
+ Object.freeze(list);
+
+ return list;
+};
+
+const itemToDataTransferItem = (item: Item) =>
+ item instanceof File
+ ? {
+ kind: 'file',
+ type: item.type,
+ getAsFile: () => item,
+ }
+ : {
+ kind: 'string',
+ type: 'text/plain',
+ getAsString(resolve: (value: string) => void) {
+ resolve(item);
+ },
+ };
+
+const createItemList = (items: Item[]): DataTransferItemList => {
+ const list: DataTransferItemList & Iterable = {
+ ...items.map(itemToDataTransferItem),
+ length: items.length,
+ add() {
+ throw new Error('not-implemented');
+ },
+ remove() {
+ throw new Error('not-implemented');
+ },
+ clear() {
+ throw new Error('not-implemented');
+ },
+ *[Symbol.iterator]() {
+ // eslint-disable-next-line unicorn/no-for-loop,@typescript-eslint/prefer-for-of
+ for (let i = 0; i < list.length; ++i) {
+ yield list[i]!;
+ }
+ },
+ };
+
+ list.constructor = DataTransferItemList;
+ Object.setPrototypeOf(list, DataTransferItemList.prototype);
+ Object.freeze(list);
+
+ return list;
+};
+
+const createDataTransferFromFiles = (items: Item[] = []): DataTransfer => {
+ const dt = new DataTransfer();
+ Object.defineProperty(dt, 'files', {
+ get: () =>
+ createFileList(
+ items.filter((item: Item): item is File => item instanceof File),
+ ),
+ });
+ Object.defineProperty(dt, 'items', {get: () => createItemList(items)});
+ return dt;
+};
+
+const dropFiles = async ({findByLabelText}, item: Item) => {
+ fireEvent.drop(await findByLabelText(/drop contents here/i), {
+ dataTransfer: createDataTransferFromFiles([item]),
+ });
+};
+
+client(__filename, () => {
+ it('should allow to open and close eid dialog', async () => {
+ const username = randomUserId();
+ const password = randomPassword();
+ const app = setupApp();
+ await createUserWithPasswordAndLogin(app, username, password);
+
+ const {findByRole, findByText, user} = app;
+
+ await dropFiles(app, exampleEidXML());
+
+ await findByRole('heading', {name: 'Select record to work with.'});
+
+ await user.click(await findByRole('button', {name: 'Cancel'}));
+
+ await findByText('Closed eid dialog.');
+ });
+
+ it('should allow to create a patient from eid', async () => {
+ const username = randomUserId();
+ const password = randomPassword();
+ const app = setupApp();
+ await createUserWithPasswordAndLogin(app, username, password);
+
+ const {findByRole, user} = app;
+
+ const eidXML = exampleEidXML({name: 'Doe', firstname: 'Jane'});
+
+ await dropFiles(app, eidXML);
+
+ await findByRole('heading', {name: 'Select record to work with.'});
+
+ await user.click(await findByRole('button', {name: 'Add a new patient'}));
+
+ await user.click(await findByRole('button', {name: 'Next (1)'}));
+
+ await user.click(await findByRole('button', {name: 'Next'}));
+
+ await user.click(
+ await findByRole('button', {name: 'Create a new patient'}),
+ );
+
+ await searchForPatient(app, `Jane Doe`, {
+ name: `Jane Doe`,
+ });
+ });
+});