diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3df4f9..328fb36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## 7.2.0 IN PROGRESS
+* Create Jest test environment and setup initial mocks. Refs UIPFU-78.
+
## [7.1.1](https://github.com/folio-org/ui-plugin-find-user/tree/v7.1.1) (2024-05-03)
[Full Changelog](https://github.com/folio-org/ui-plugin-find-user/compare/v7.1.0...v7.1.1)
diff --git a/test/jest/__mock__/index.js b/test/jest/__mock__/index.js
new file mode 100644
index 0000000..1596784
--- /dev/null
+++ b/test/jest/__mock__/index.js
@@ -0,0 +1,6 @@
+import './stripesConfig.mock';
+import './stripesCore.mock';
+import './stripes.mock';
+import './intl.mock';
+import './stripesComponents.mock';
+import './stripesSmartComponents.mock';
diff --git a/test/jest/__mock__/intl.mock.js b/test/jest/__mock__/intl.mock.js
new file mode 100644
index 0000000..e4d3692
--- /dev/null
+++ b/test/jest/__mock__/intl.mock.js
@@ -0,0 +1,36 @@
+import React from 'react';
+
+jest.mock('react-intl', () => {
+ const intl = {
+ formatMessage: ({ id }) => id,
+ formatNumber: (value) => value,
+ formatDisplayName: (value) => value,
+ };
+
+ return {
+ ...jest.requireActual('react-intl'),
+ FormattedMessage: jest.fn(({ id, children }) => {
+ if (children) {
+ return children([id]);
+ }
+
+ return id;
+ }),
+ FormattedTime: jest.fn(({ value, children }) => {
+ if (children) {
+ return children([value]);
+ }
+
+ return value;
+ }),
+ FormattedDate: jest.fn(({ value, children }) => {
+ if (children) {
+ return children([value]);
+ }
+
+ return value;
+ }),
+ useIntl: () => intl,
+ injectIntl: (Component) => (props) => ,
+ };
+});
diff --git a/test/jest/__mock__/stripes.mock.js b/test/jest/__mock__/stripes.mock.js
new file mode 100644
index 0000000..5163003
--- /dev/null
+++ b/test/jest/__mock__/stripes.mock.js
@@ -0,0 +1,26 @@
+import { noop } from 'lodash';
+
+const buildStripes = (otherProperties = {}) => ({
+ hasPerm: noop,
+ hasInterface: noop,
+ clone: noop,
+ logger: { log: noop },
+ config: {},
+ okapi: {
+ url: '',
+ tenant: '',
+ },
+ locale: 'en-US',
+ withOkapi: true,
+ setToken: noop,
+ actionNames: [],
+ setLocale: noop,
+ setTimezone: noop,
+ setCurrency: noop,
+ setSinglePlugin: noop,
+ setBindings: noop,
+ connect: noop,
+ ...otherProperties,
+});
+
+export default buildStripes;
diff --git a/test/jest/__mock__/stripesComponents.mock.js b/test/jest/__mock__/stripesComponents.mock.js
new file mode 100644
index 0000000..fd86742
--- /dev/null
+++ b/test/jest/__mock__/stripesComponents.mock.js
@@ -0,0 +1,68 @@
+import React from 'react';
+
+jest.mock('@folio/stripes/components', () => ({
+ Button: jest.fn(({
+ children,
+ onClick = jest.fn(),
+ ...rest
+ }) => (
+
+ )),
+ Checkbox: jest.fn((props) => (
+
+ )),
+ FilterGroups: jest.fn(({ config, filters, onChangeFilter, onClearFilter }) => (
+
+ {JSON.stringify(config)}
+ {JSON.stringify(filters)}
+ {JSON.stringify(onChangeFilter)}
+ {JSON.stringify(onClearFilter)}
+
+ )),
+ Icon: jest.fn((props) => (props && props.children ? props.children : )),
+ Modal: jest.fn(({ children, label, dismissible, footer, ...rest }) => {
+ return (
+
+
{label}
+ {children}
+ {footer}
+
+ );
+ }),
+ MultiColumnList: jest.fn((props) => (
+
+ )),
+ Pane: jest.fn(({ children, className, defaultWidth, paneTitle, firstMenu, ...rest }) => {
+ return (
+
+
+ {firstMenu ?? null}
+ {paneTitle}
+
+ {children}
+
+ );
+ }),
+ PaneMenu: jest.fn((props) => {props.children}
),
+ Paneset: jest.fn((props) => {props.children}
),
+ SearchField: jest.fn((props) => (
+
+ )),
+}));
diff --git a/test/jest/__mock__/stripesConfig.mock.js b/test/jest/__mock__/stripesConfig.mock.js
new file mode 100644
index 0000000..40a3417
--- /dev/null
+++ b/test/jest/__mock__/stripesConfig.mock.js
@@ -0,0 +1 @@
+jest.mock('stripes-config', () => ({ modules: [] }), { virtual: true });
diff --git a/test/jest/__mock__/stripesCore.mock.js b/test/jest/__mock__/stripesCore.mock.js
new file mode 100644
index 0000000..ef898f6
--- /dev/null
+++ b/test/jest/__mock__/stripesCore.mock.js
@@ -0,0 +1,139 @@
+import React from 'react';
+
+jest.mock('@folio/stripes/core', () => {
+ const STRIPES = {
+ actionNames: [],
+ bindings: {},
+ clone: jest.fn(),
+ connect: (Component) => Component,
+ config: {
+ logCategories: '',
+ logPrefix: '',
+ logTimestamp: true,
+ showPerms: true,
+ showHomeLink: true,
+ listInvisiblePerms: true,
+ disableAuth: false,
+ hasAllPerms: false,
+ },
+ currency: 'USD',
+ discovery: {
+ interfaces: {},
+ isFinished: true,
+ modules: {},
+ okapi: '',
+ },
+ epics: {
+ add: jest.fn(),
+ middleware: jest.fn(),
+ },
+ hasInterface: () => true,
+ hasPerm: jest.fn().mockReturnValue(true),
+ icons: {},
+ locale: 'en-US',
+ logger: {
+ log: jest.fn(),
+ },
+ metadata: {},
+ okapi: {
+ authFailure: false,
+ okapiReady: true,
+ tenant: 'diku',
+ token: 'c0ffee',
+ translations: {
+ 'stripes-components.Datepicker.calendar': 'calendar',
+ 'stripes-components.Datepicker.calendarDaysList': 'calendar days list.',
+ 'stripes-core.button.cancel': [{ type: 0, value: 'Cancel' }],
+ 'ui-users.permission.modal.list.pane.header': 'Permissions',
+ 'ui-users.permission.modal.list.pane.header.array': [{ type: 0, value: 'Permissions' }],
+ default: false,
+ },
+ url: 'https://folio-testing-okapi.dev.folio.org',
+ withoutOkapi: false,
+ },
+ plugins: {},
+ setBindings: jest.fn(),
+ setCurrency: jest.fn(),
+ setLocale: jest.fn(),
+ setSinglePlugin: jest.fn(),
+ setTimezone: jest.fn(),
+ setToken: jest.fn(),
+ store: {
+ getState: () => ({
+ okapi: {
+ token: 'abc',
+ },
+ }),
+ dispatch: jest.fn(),
+ subscribe: jest.fn(),
+ replaceReducer: jest.fn(),
+ },
+ timezone: 'UTC',
+ user: {
+ perms: {},
+ user: {
+ addresses: [],
+ firstName: 'Testy',
+ lastName: 'McTesterson',
+ email: 'test@folio.org',
+ id: 'b1add99d-530b-5912-94f3-4091b4d87e2c',
+ username: 'diku_admin',
+ },
+ },
+ withOkapi: true,
+ };
+
+ // eslint-disable-next-line react/prop-types
+ const stripesConnect = Component => ({ mutator, resources, stripes, ...rest }) => {
+ const fakeMutator = mutator || Object.keys(Component.manifest || {}).reduce((acc, mutatorName) => {
+ const returnValue = Component.manifest[mutatorName].records ? [] : {};
+
+ acc[mutatorName] = {
+ GET: jest.fn().mockReturnValue(Promise.resolve(returnValue)),
+ PUT: jest.fn().mockReturnValue(Promise.resolve()),
+ POST: jest.fn().mockReturnValue(Promise.resolve()),
+ DELETE: jest.fn().mockReturnValue(Promise.resolve()),
+ reset: jest.fn(),
+ update: jest.fn(),
+ replace: jest.fn(),
+ };
+
+ return acc;
+ }, {});
+
+ const fakeResources = resources || Object.keys(Component.manifest || {}).reduce((acc, resourceName) => {
+ acc[resourceName] = {
+ records: [],
+ };
+
+ return acc;
+ }, {});
+
+ const fakeStripes = stripes || STRIPES;
+
+ return ();
+ };
+
+ return {
+ ...jest.requireActual('@folio/stripes/core'),
+ AppIcon: jest.fn(({ ariaLabel }) => {ariaLabel}),
+ connect: stripesConnect,
+ IntlConsumer: jest.fn(({ children }) => {
+ const intl = {
+ formatMessage: jest.fn(({ id }) => id),
+ };
+ return (
+
+ {children(intl)}
+
+ );
+ }),
+ stripesConnect,
+ };
+});
diff --git a/test/jest/__mock__/stripesSmartComponents.mock.js b/test/jest/__mock__/stripesSmartComponents.mock.js
new file mode 100644
index 0000000..a06c898
--- /dev/null
+++ b/test/jest/__mock__/stripesSmartComponents.mock.js
@@ -0,0 +1,51 @@
+import React from 'react';
+
+jest.mock('@folio/stripes/smart-components', () => ({
+ makeQueryFunction: jest.fn((value) => value),
+ SearchAndSortQuery: jest.fn(({ children, ...rest }) => {children}
),
+ SearchAndSortSearchButton: jest.fn(({
+ label,
+ id,
+ onClick = jest.fn(),
+ disabled,
+ visible,
+ ...restProps
+ }) => (
+
+
+
+ )),
+ SearchAndSortNoResultsMessage: jest.fn(({
+ label,
+ filterPaneIsVisible = true,
+ toggleFilterPane = jest.fn(),
+ ...rest
+ }) => (
+
+
+ {label}
+
+ {!filterPaneIsVisible &&
+
+ }
+
+ )),
+ StripesConnectedSource: jest.fn(),
+}));
diff --git a/test/jest/setupFiles.js b/test/jest/setupFiles.js
index b9e1a54..e8a3851 100644
--- a/test/jest/setupFiles.js
+++ b/test/jest/setupFiles.js
@@ -1,4 +1,4 @@
// See https://github.com/facebook/jest/issues/335#issuecomment-703691592
-// import './__mock__';
+import './__mock__';
import 'regenerator-runtime/runtime';