diff --git a/backend/src/main/resources/static/favicon.ico b/backend/src/main/resources/static/favicon.ico
new file mode 100644
index 000000000..9c1f419d3
Binary files /dev/null and b/backend/src/main/resources/static/favicon.ico differ
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 77fd5a08a..ab86d547d 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -2205,9 +2205,9 @@
}
},
"@testing-library/dom": {
- "version": "7.30.4",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.4.tgz",
- "integrity": "sha512-GObDVMaI4ARrZEXaRy4moolNAxWPKvEYNV/fa6Uc2eAzR/t4otS6A7EhrntPBIQLeehL9DbVhscvvv7gd6hWqA==",
+ "version": "7.31.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
+ "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@@ -2215,7 +2215,7 @@
"@types/aria-query": "^4.2.0",
"aria-query": "^4.2.2",
"chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.4",
+ "dom-accessibility-api": "^0.5.6",
"lz-string": "^1.4.4",
"pretty-format": "^26.6.2"
},
@@ -2233,9 +2233,9 @@
}
},
"@testing-library/jest-dom": {
- "version": "5.12.0",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.12.0.tgz",
- "integrity": "sha512-N9Y82b2Z3j6wzIoAqajlKVF1Zt7sOH0pPee0sUHXHc5cv2Fdn23r+vpWm0MBBoGJtPOly5+Bdx1lnc3CD+A+ow==",
+ "version": "5.13.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.13.0.tgz",
+ "integrity": "sha512-+jXXTn8GjRnZkJfzG/tqK/2Q7dGlBInR412WE7Aml7CT3wdSpx5dMQC0HOwVQoZ3cNTmQUy8fCVGUV/Zhoyvcw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2",
@@ -2249,9 +2249,9 @@
}
},
"@testing-library/react": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
- "integrity": "sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==",
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.7.tgz",
+ "integrity": "sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.12.5",
@@ -2408,9 +2408,9 @@
}
},
"@types/jest": {
- "version": "26.0.22",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz",
- "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==",
+ "version": "26.0.23",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz",
+ "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==",
"dev": true,
"requires": {
"jest-diff": "^26.0.0",
@@ -2520,9 +2520,9 @@
"dev": true
},
"@types/testing-library__jest-dom": {
- "version": "5.9.5",
- "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz",
- "integrity": "sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==",
+ "version": "5.13.0",
+ "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.13.0.tgz",
+ "integrity": "sha512-tfjY4Fzzwg1wSU31MWaIH8rzJ2WPtQtUNnZ0wcZwzzhWWRa63Jb1fB7tl79fGX7PUL/4ZHjKs+tcY5BZ8nfNyg==",
"dev": true,
"requires": {
"@types/jest": "*"
@@ -5642,9 +5642,9 @@
}
},
"dom-accessibility-api": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz",
- "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==",
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz",
+ "integrity": "sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==",
"dev": true
},
"dom-converter": {
diff --git a/ui/package.json b/ui/package.json
index 45deba74d..1046146a3 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -31,12 +31,12 @@
"web-vitals": "^1.0.1"
},
"devDependencies": {
- "@testing-library/jest-dom": "^5.11.4",
- "@testing-library/react": "^11.1.0",
+ "@testing-library/jest-dom": "^5.13.0",
+ "@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.1.10",
+ "http-proxy-middleware": "^1.2.0",
"react-scripts": "4.0.3",
- "sass": "1.32.11",
- "http-proxy-middleware": "^1.2.0"
+ "sass": "1.32.11"
},
"scripts": {
"start": "react-scripts start",
@@ -48,6 +48,12 @@
"copy:static": "node ./build",
"copy": "npm run copy:static && npm run build:static"
},
+ "jest": {
+ "collectCoverageFrom": [
+ "src/**/*.{js,jsx}",
+ "!src/test/**/*.*"
+ ]
+ },
"eslintConfig": {
"extends": [
"react-app",
diff --git a/ui/src/app/core/components/FormattedDate.js b/ui/src/app/core/components/FormattedDate.js
index 5e84ba7df..e9298af73 100644
--- a/ui/src/app/core/components/FormattedDate.js
+++ b/ui/src/app/core/components/FormattedDate.js
@@ -1,8 +1,10 @@
import React from 'react';
import { format, parseISO } from 'date-fns';
-export default function FormattedDate ({ date, time = false }) {
+export function FormattedDate ({ date, time = false }) {
const formatted = React.useMemo(() => format(parseISO(date), `MMM d, Y${time ? ' HH:mm:ss' : ''}`), [date, time]);
return (<>{ formatted }>);
-}
\ No newline at end of file
+}
+
+export default FormattedDate;
\ No newline at end of file
diff --git a/ui/src/app/core/components/FormattedDate.test.js b/ui/src/app/core/components/FormattedDate.test.js
new file mode 100644
index 000000000..a013c93ea
--- /dev/null
+++ b/ui/src/app/core/components/FormattedDate.test.js
@@ -0,0 +1,9 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+
+import { FormattedDate } from './FormattedDate';
+
+it('should display a formatted date', () => {
+ render();
+ expect(screen.getByText('Jun 10, 2021')).toBeInTheDocument();
+});
\ No newline at end of file
diff --git a/ui/src/app/core/components/VersionInfo.js b/ui/src/app/core/components/VersionInfo.js
index f9079037a..ab1fff8d5 100644
--- a/ui/src/app/core/components/VersionInfo.js
+++ b/ui/src/app/core/components/VersionInfo.js
@@ -9,7 +9,7 @@ const formatter = v => v && v.build ? `${v.build.version}-${v.git.commit.id}` :
const year = new Date().getFullYear();
const params = { year };
-export default function VersionInfo () {
+export function VersionInfo () {
const { data = {} } = useFetch('/actuator/info', {}, []);
@@ -26,4 +26,6 @@ export default function VersionInfo () {
Copyright & copy; Internet2
);
-}
\ No newline at end of file
+}
+
+export default VersionInfo;
\ No newline at end of file
diff --git a/ui/src/app/core/components/VersionInfo.test.js b/ui/src/app/core/components/VersionInfo.test.js
new file mode 100644
index 000000000..28a8a6b47
--- /dev/null
+++ b/ui/src/app/core/components/VersionInfo.test.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import { VersionInfo } from './VersionInfo';
+
+const data = { "git": { "branch": "react", "commit": { "id": "634f1a4", "time": "2021-06-10T19:00:29Z" } }, "build": { "artifact": "shibui", "name": "backend", "time": "2021-06-10T19:26:14.478Z", "version": "2.0.0-SNAPSHOT", "group": "edu.internet2.tier.shibboleth.admin.ui" } };
+
+jest.mock('../../i18n/components/translate', () => {
+ return 'span';
+})
+
+jest.mock('use-http', () => {
+ return () => ({ data });
+});
+
+it('should display formatted version information', () => {
+
+ render();
+ expect(screen.getByText('2.0.0-SNAPSHOT-634f1a4', { exact: false })).toBeInTheDocument();
+});
\ No newline at end of file
diff --git a/ui/src/app/dashboard/view/Dashboard.js b/ui/src/app/dashboard/view/Dashboard.js
index 5294abaf5..74e1b6452 100644
--- a/ui/src/app/dashboard/view/Dashboard.js
+++ b/ui/src/app/dashboard/view/Dashboard.js
@@ -7,7 +7,6 @@ import { NavLink } from 'react-router-dom';
import Translate from '../../i18n/components/translate';
import { AdminRoute } from '../../core/components/AdminRoute';
-import './Dashboard.scss';
import { SourcesTab } from './SourcesTab';
import { ProvidersTab } from './ProvidersTab';
import { AdminTab } from './AdminTab';
diff --git a/ui/src/app/dashboard/view/Dashboard.scss b/ui/src/app/dashboard/view/Dashboard.scss
deleted file mode 100644
index 74201a46e..000000000
--- a/ui/src/app/dashboard/view/Dashboard.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import '../../../theme/variables.scss';
-
diff --git a/ui/src/app/i18n/hooks.js b/ui/src/app/i18n/hooks.js
index 35b867de8..1d447c687 100644
--- a/ui/src/app/i18n/hooks.js
+++ b/ui/src/app/i18n/hooks.js
@@ -12,14 +12,6 @@ export function getMessage(value, messages) {
return messages.hasOwnProperty(value) ? messages[value] : (Object.keys(messages).length ? value : '');
}
-export function useCurrentLanguage () {
-
-}
-
-export function useCurrentLocale () {
-
-}
-
export function useTranslation (value, interpolated = {}) {
const messages = useContext(I18nContext);
const val = getMessage(value, messages);
diff --git a/ui/src/app/i18n/hooks.test.js b/ui/src/app/i18n/hooks.test.js
new file mode 100644
index 000000000..23f41c145
--- /dev/null
+++ b/ui/src/app/i18n/hooks.test.js
@@ -0,0 +1,71 @@
+import React from 'react';
+
+import { translate, getMessage, useTranslation, useTranslator } from './hooks';
+
+const msgs = { foo: 'bar { baz }' };
+
+describe('getMessage', () => {
+ it('should return the expected message based on the key', () => {
+ expect(getMessage('foo', msgs)).toEqual('bar { baz }');
+ });
+})
+
+describe('translate', () => {
+ it('should translate the provided message', () => {
+ const msg = getMessage('foo', msgs);
+ expect(translate(msg, { baz: 'baz' })).toEqual('bar baz');
+ });
+
+ it('should return provided value if no interpolation strings are passed', () => {
+ expect(translate('foo', {})).toEqual('foo');
+ });
+
+ it('should return an empty string if messages are empty', () => {
+ expect(getMessage('foo', {})).toEqual('');
+ });
+});
+
+describe('useTranslation hook', () => {
+ let realUseContext;
+ let useContextMock;
+ beforeEach(() => {
+ realUseContext = React.useContext;
+ useContextMock = React.useContext = jest.fn();
+ });
+ // Cleanup mock
+ afterEach(() => {
+ React.useContext = realUseContext;
+ });
+
+ test("mock hook", () => {
+ useContextMock.mockReturnValue(msgs);
+
+ expect(useTranslation('foo', { baz: 'baz' })).toBe('bar baz');
+ });
+ //jest.mock('useContext')
+
+ it('should translate the provided message', () => {
+ const msg = getMessage('foo', { foo: 'bar { baz }' });
+ expect(translate(msg, { baz: 'baz' })).toEqual('bar baz');
+ });
+});
+
+describe('useTranslator hook', () => {
+ let realUseContext;
+ let useContextMock;
+ beforeEach(() => {
+ realUseContext = React.useContext;
+ useContextMock = React.useContext = jest.fn();
+ });
+ // Cleanup mock
+ afterEach(() => {
+ React.useContext = realUseContext;
+ });
+
+ test("mock hook", () => {
+ useContextMock.mockReturnValue(msgs);
+ const translator = useTranslator();
+
+ expect(translator('foo', { baz: 'baz' })).toBe('bar baz');
+ });
+});
\ No newline at end of file
diff --git a/ui/src/app/metadata/domain/index.test.js b/ui/src/app/metadata/domain/index.test.js
new file mode 100644
index 000000000..38438a14f
--- /dev/null
+++ b/ui/src/app/metadata/domain/index.test.js
@@ -0,0 +1,12 @@
+import { NameIDFilterEditor } from './filter/NameIdFilterDefinition';
+import { getDefinition } from './index';
+import { FileSystemMetadataProviderEditor } from './provider/FileSystemMetadataProviderDefinition';
+import { SourceEditor } from './source/SourceDefinition';
+
+describe('getDefinitions method', () => {
+ it('should retrieve the definition', () => {
+ expect(getDefinition('source')).toBe(SourceEditor);
+ expect(getDefinition('NameIDFormat')).toBe(NameIDFilterEditor);
+ expect(getDefinition('FilesystemMetadataResolver')).toBe(FileSystemMetadataProviderEditor);
+ });
+});
\ No newline at end of file
diff --git a/ui/src/app/metadata/editor/MetadataEditorForm.js b/ui/src/app/metadata/editor/MetadataEditorForm.js
index f8e9bd29c..f1288fd16 100644
--- a/ui/src/app/metadata/editor/MetadataEditorForm.js
+++ b/ui/src/app/metadata/editor/MetadataEditorForm.js
@@ -34,6 +34,8 @@ export function MetadataEditorForm({ metadata, definition, schema, current, onCh
onChange(definition.bindings ? { ...form, formData: definition.bindings(data, form.formData) }: form);
};
+ console.log(uiSchema);
+
return (
<>
{step.locked &&
diff --git a/ui/src/app/metadata/hoc/MetadataSchema.test.js b/ui/src/app/metadata/hoc/MetadataSchema.test.js
deleted file mode 100644
index e8164a2cc..000000000
--- a/ui/src/app/metadata/hoc/MetadataSchema.test.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from "react";
-import ReactDOM from 'react-dom';
-import { render, screen } from "@testing-library/react";
-import { act } from 'react-dom/test-utils';
-import { MetadataSchema, useMetadataDefinitionContext } from './MetadataSchema';
-
-let container;
-beforeEach(() => {
- container = document.createElement('div');
- document.body.appendChild(container);
-});
-
-afterEach(() => {
- document.body.removeChild(container);
- container = null;
-});
-
-const Tester = () => {
- const definition = useMetadataDefinitionContext();
-
- return (
- <>
- <>{definition.type}>
- >
- );
-}
-
-
-xdescribe("", () => {
- /*xact(() => {
- ReactDOM.render(
-
-
- ,
- container
- );
- });*/
-
- xdescribe("definition context", () => {
- it("should provide the type of the definition", () => {
- expect(screen.getByText('@MetadataProvider')).toBeTruthy();
- });
- });
-});
\ No newline at end of file
diff --git a/ui/src/app/metadata/hooks/api.test.js b/ui/src/app/metadata/hooks/api.test.js
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ui/src/app/metadata/hooks/schema.js b/ui/src/app/metadata/hooks/schema.js
index 2aeb62c4f..ecde47f05 100644
--- a/ui/src/app/metadata/hooks/schema.js
+++ b/ui/src/app/metadata/hooks/schema.js
@@ -1,14 +1,12 @@
import React from 'react';
import { useIsAdmin } from '../../core/user/UserContext';
-const fillInRootProperties = (keys, ui) => {
- return keys.reduce((sch, key, idx) => {
- if (!sch.hasOwnProperty(key)) {
- sch[key] = {};
- }
- return sch;
- }, ui);
-}
+export const fillInRootProperties = (keys, ui) => keys.reduce((sch, key, idx) => {
+ if (!sch.hasOwnProperty(key)) {
+ sch[key] = {};
+ }
+ return sch;
+}, ui);
export function useUiSchema(definition, schema, current, locked = true) {
@@ -17,23 +15,21 @@ export function useUiSchema(definition, schema, current, locked = true) {
const step = React.useMemo(() => definition ? definition.steps.find(step => step.id === current) : {fields: []}, [definition, current]);
const filled = React.useMemo(() => fillInRootProperties(schemaKeys, ui), [schemaKeys, ui]);
- const mapped = React.useMemo(() => {
- return Object.keys(filled).reduce((sch, key) => {
- const obj = { ...filled[key] };
- if (step.fields.indexOf(key) === -1) {
- obj["ui:widget"] = 'hidden';
- }
- sch[key] = obj;
- return sch;
- }, {})
- }, [filled, step]);
-
- const isLocked = React.useMemo(() => {
- return {
+ const mapped = React.useMemo(() => Object.keys(filled).reduce((sch, key) => {
+ const obj = { ...filled[key] };
+ if (step.fields.indexOf(key) === -1) {
+ obj["ui:widget"] = 'hidden';
+ }
+ sch[key] = obj;
+ return sch;
+ }, {}), [filled, step]);
+
+ const isLocked = React.useMemo(() => (
+ {
...mapped,
'ui:disabled': locked && step.locked ? true : false
- };
- }, [mapped, step.locked, locked]);
+ }
+ ), [mapped, step.locked, locked]);
const isAdmin = useIsAdmin();
diff --git a/ui/src/app/metadata/hooks/schema.test.js b/ui/src/app/metadata/hooks/schema.test.js
index e69de29bb..1679ba221 100644
--- a/ui/src/app/metadata/hooks/schema.test.js
+++ b/ui/src/app/metadata/hooks/schema.test.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import { useUiSchema } from './schema';
+
+import { SourceEditor } from '../domain/source/SourceDefinition';
+
+import jsonSchema from '../../../testing/sourceSchema';
+import uiSchemaResult from '../../../testing/uiSchema';
+
+import { useIsAdmin } from '../../core/user/UserContext';
+jest.mock('../../core/user/UserContext');
+
+describe('useUiSchema', () => {
+ let realUseMemo;
+ let useMemoMock;
+ beforeEach(() => {
+ realUseMemo = React.useMemo;
+ useMemoMock = React.useMemo = (cb) => cb();
+ });
+ // Cleanup mock
+ afterEach(() => {
+ React.useMemo = realUseMemo;
+ });
+
+ test('should return a parsed ui schema', () => {
+ useIsAdmin.mockResolvedValue(false);
+
+ const { uiSchema } = useUiSchema(SourceEditor, jsonSchema, 'common')
+ expect(uiSchema).toEqual(uiSchemaResult);
+ })
+
+});
\ No newline at end of file
diff --git a/ui/src/app/metadata/hooks/utility.js b/ui/src/app/metadata/hooks/utility.js
index d8279ec07..8b8ab5961 100644
--- a/ui/src/app/metadata/hooks/utility.js
+++ b/ui/src/app/metadata/hooks/utility.js
@@ -1,4 +1,4 @@
-function getDefinition(path, definitions) {
+export function getDefinition(path, definitions) {
let def = path.split('/').pop();
return definitions[def];
}
diff --git a/ui/src/app/metadata/hooks/utility.test.js b/ui/src/app/metadata/hooks/utility.test.js
index e69de29bb..9faaebec2 100644
--- a/ui/src/app/metadata/hooks/utility.test.js
+++ b/ui/src/app/metadata/hooks/utility.test.js
@@ -0,0 +1,61 @@
+import { getStepProperties, getDefinition, getPropertyItemSchema, getStepProperty } from './utility';
+import SCHEMA from '../../../testing/simpleSchema';
+
+describe('domain utility functions', () => {
+ describe('getStepProperties function', () => {
+ it('should return an empty array of schema or schema.properties is not defined', () => {
+ expect(getStepProperties(null, {})).toEqual([]);
+ expect(getStepProperties({}, {})).toEqual([]);
+ });
+
+ it('should return a formatted list of properties', () => {
+ expect(getStepProperties(SCHEMA, {}).length).toBe(4);
+ });
+ });
+
+ describe('getDefinitions method', () => {
+ it('should retrieve the definitions from the json schema', () => {
+ const definition = {
+ id: 'foo',
+ title: 'bar',
+ description: 'baz',
+ type: 'string'
+ };
+ expect(getDefinition('/foo/bar', { bar: definition })).toBe(definition);
+ });
+ });
+
+ describe('getPropertyItemSchema method', () => {
+ it('should return null if no items are provided', () => {
+ expect(getPropertyItemSchema(null, SCHEMA.definitions)).toBeNull();
+ });
+ it('should retrieve the definitions from the items schema', () => {
+ expect(getPropertyItemSchema({ $ref: 'description' }, SCHEMA.definitions)).toBe(SCHEMA.definitions.description);
+ });
+ it('should return the item itself if no $ref', () => {
+ let item = {};
+ expect(getPropertyItemSchema(item, SCHEMA.definitions)).toBe(item);
+ });
+ });
+
+ describe('getStepProperty method', () => {
+ const model = {
+ name: 'foo',
+ type: 'bar',
+ description: 'baz'
+ };
+ it('should return null if no items are provided', () => {
+ expect(getStepProperty(null, null, SCHEMA.definitions)).toBeNull();
+ });
+
+ it('should retrieve the property $ref definition if available', () => {
+ const property = getStepProperty(
+ { $ref: 'description' },
+ model,
+ SCHEMA.definitions
+ );
+ expect(property.type).toBe('string');
+ });
+ });
+});
+
diff --git a/ui/src/setupTests.js b/ui/src/setupTests.js
index f5f96e21d..8f2609b7b 100644
--- a/ui/src/setupTests.js
+++ b/ui/src/setupTests.js
@@ -3,39 +3,3 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
-
-const localStorageMock = {
- getItem: jest.fn(),
- setItem: jest.fn(),
- removeItem: jest.fn(),
- clear: jest.fn(),
-};
-global.localStorage = localStorageMock;
-/*
-const documentCookieMock = {
- cookies: '',
-
- get cookie() {
- return this.cookies;
- },
-
- set cookie(cookieValue) {
- const cookies = this.cookies.split(' ');
- const cookieName = cookieValue.split('=').shift();
- const cookieNameLength = cookieName.length;
- let cookieIndex = -1;
- cookies.forEach((value, index) => {
- if (`${value.substr(0, cookieNameLength)}=` === `${cookieName}=`) {
- cookieIndex = index;
- }
- });
- if (cookieIndex > -1) {
- cookies[cookieIndex] = `${cookieValue};`;
- } else {
- cookies.push(`${cookieValue};`);
- }
- this.cookies = cookies.join(' ').trim();
- },
-};
-
-global.document.cookie = documentCookieMock;*/
\ No newline at end of file
diff --git a/ui/src/testing/simpleSchema.js b/ui/src/testing/simpleSchema.js
new file mode 100644
index 000000000..43457ca7c
--- /dev/null
+++ b/ui/src/testing/simpleSchema.js
@@ -0,0 +1,131 @@
+export const SCHEMA = {
+ 'title': 'MetadataResolver',
+ 'type': 'object',
+ 'widget': {
+ 'id': 'fieldset'
+ },
+ 'properties': {
+ 'name': {
+ 'title': 'Metadata Provider Name (Dashboard Display Only)',
+ 'description': 'Metadata Provider Name (Dashboard Display Only)',
+ 'type': 'string',
+ 'widget': {
+ 'id': 'string',
+ 'help': 'Must be unique.'
+ }
+ },
+ '@type': {
+ 'title': 'Metadata Provider Type',
+ 'description': 'Metadata Provider Type',
+ 'ui:placeholder': 'Select a metadata provider type',
+ 'type': 'string',
+ 'widget': {
+ 'id': 'select'
+ },
+ 'oneOf': [
+ {
+ 'enum': [
+ 'FileBackedHttpMetadataResolver'
+ ],
+ 'description': 'FileBackedHttpMetadataProvider'
+ }
+ ]
+ },
+ 'list': {
+ 'title': 'label.retained-roles',
+ 'description': 'tooltip.retained-roles',
+ 'type': 'array',
+ 'items': {
+ 'widget': {
+ 'id': 'select'
+ },
+ 'type': 'string',
+ 'oneOf': [
+ {
+ 'enum': [
+ 'SPSSODescriptor'
+ ],
+ 'description': 'value.spdescriptor'
+ },
+ {
+ 'enum': [
+ 'AttributeAuthorityDescriptor'
+ ],
+ 'description': 'value.attr-auth-descriptor'
+ }
+ ]
+ }
+ },
+ 'formatFilterTarget': {
+ 'title': 'label.search-criteria',
+ 'description': 'tooltip.search-criteria',
+ 'type': 'object',
+ 'widget': {
+ 'id': 'filter-target',
+ 'target': 'formatFilterTargetType'
+ },
+ 'properties': {
+ 'formatFilterTargetType': {
+ 'title': '',
+ 'type': 'string',
+ 'default': 'ENTITY',
+ 'oneOf': [
+ {
+ 'enum': [
+ 'ENTITY'
+ ],
+ 'description': 'value.entity-id'
+ },
+ {
+ 'enum': [
+ 'REGEX'
+ ],
+ 'description': 'value.regex'
+ },
+ {
+ 'enum': [
+ 'CONDITION_SCRIPT'
+ ],
+ 'description': 'value.script'
+ }
+ ]
+ },
+ 'value': {
+ 'type': 'array',
+ 'minItems': 1,
+ 'uniqueItems': true,
+ 'items': {
+ 'type': 'string'
+ }
+ }
+ },
+ 'required': [
+ 'value',
+ 'nameIdFormatFilterTargetType'
+ ]
+ }
+ },
+ 'required': [
+ 'name',
+ '@type'
+ ],
+ 'fieldsets': [
+ {
+ 'type': 'section',
+ 'fields': [
+ 'name',
+ '@type'
+ ]
+ }
+ ],
+ 'definitions': {
+ 'description': {
+ 'title': 'Description',
+ 'description': 'A description of the object',
+ 'type': 'string',
+ 'widget': 'string'
+ }
+ }
+};
+
+export default SCHEMA;
\ No newline at end of file
diff --git a/ui/src/testing/sourceSchema.js b/ui/src/testing/sourceSchema.js
new file mode 100644
index 000000000..93262c5f4
--- /dev/null
+++ b/ui/src/testing/sourceSchema.js
@@ -0,0 +1,3 @@
+const SCHEMA = { "type": "object", "required": ["serviceProviderName", "entityId"], "properties": { "serviceProviderName": { "title": "label.service-provider-name", "description": "tooltip.service-provider-name", "type": "string", "minLength": 1, "maxLength": 255 }, "entityId": { "title": "label.entity-id", "description": "tooltip.entity-id", "type": "string", "minLength": 1, "maxLength": 255 }, "serviceEnabled": { "title": "label.enable-this-service", "description": "tooltip.enable-this-service-upon-saving", "type": "boolean", "default": false }, "organization": { "$ref": "#/definitions/Organization" }, "contacts": { "title": "label.contact-information", "description": "tooltip.contact-information", "type": "array", "items": { "$ref": "#/definitions/Contact" } }, "mdui": { "$ref": "#/definitions/MDUI" }, "securityInfo": { "type": "object", "widget": { "id": "fieldset" }, "dependencies": { "authenticationRequestsSigned": { "oneOf": [{ "properties": { "authenticationRequestsSigned": { "enum": [true] }, "x509Certificates": { "minItems": 1 } } }, { "properties": { "authenticationRequestsSigned": { "enum": [false] }, "x509Certificates": { "minItems": 0 } } }] } }, "properties": { "x509CertificateAvailable": { "type": "boolean", "default": true }, "authenticationRequestsSigned": { "title": "label.authentication-requests-signed", "description": "tooltip.authentication-requests-signed", "type": "boolean", "enumNames": ["value.true", "value.false"] }, "wantAssertionsSigned": { "title": "label.want-assertions-signed", "description": "tooltip.want-assertions-signed", "type": "boolean", "enumNames": ["value.true", "value.false"] }, "x509Certificates": { "title": "label.x509-certificates", "type": "array", "items": { "$ref": "#/definitions/Certificate" } } } }, "assertionConsumerServices": { "title": "label.assertion-consumer-service-endpoints", "description": "", "type": "array", "items": { "$ref": "#/definitions/AssertionConsumerService" } }, "serviceProviderSsoDescriptor": { "type": "object", "properties": { "protocolSupportEnum": { "title": "label.protocol-support-enumeration", "description": "tooltip.protocol-support-enumeration", "type": "string", "widget": { "id": "select" }, "oneOf": [{ "enum": ["SAML 2"], "description": "SAML 2" }, { "enum": ["SAML 1.1"], "description": "SAML 1.1" }] }, "nameIdFormats": { "$ref": "#/definitions/nameIdFormats" } }, "dependencies": { "nameIdFormats": ["protocolSupportEnum"] } }, "logoutEndpoints": { "title": "label.logout-endpoints", "description": "tooltip.logout-endpoints", "type": "array", "items": { "$ref": "#/definitions/LogoutEndpoint" } }, "relyingPartyOverrides": { "type": "object", "properties": { "signAssertion": { "title": "label.sign-the-assertion", "description": "tooltip.sign-assertion", "type": "boolean", "default": false }, "dontSignResponse": { "title": "label.dont-sign-the-response", "description": "tooltip.dont-sign-response", "type": "boolean", "default": false }, "turnOffEncryption": { "title": "label.turn-off-encryption-of-response", "description": "tooltip.turn-off-encryption", "type": "boolean", "default": false }, "useSha": { "title": "label.use-sha1-signing-algorithm", "description": "tooltip.usa-sha-algorithm", "type": "boolean", "default": false }, "ignoreAuthenticationMethod": { "title": "label.ignore-any-sp-requested-authentication-method", "description": "tooltip.ignore-auth-method", "type": "boolean", "default": false }, "omitNotBefore": { "title": "label.omit-not-before-condition", "description": "tooltip.omit-not-before-condition", "type": "boolean", "default": false }, "responderId": { "title": "label.responder-id", "description": "tooltip.responder-id", "type": "string", "default": "" }, "nameIdFormats": { "$ref": "#/definitions/nameIdFormats" }, "authenticationMethods": { "$ref": "#/definitions/authenticationMethods" }, "forceAuthn": { "title": "label.force-authn", "description": "tooltip.force-authn", "type": "boolean", "default": false } } }, "attributeRelease": { "type": "array", "title": "label.attribute-release", "description": "Attribute release table - select the attributes you want to release (default unchecked)", "items": { "type": "string", "enum": ["eduPersonPrincipalName", "uid", "mail", "surname", "givenName", "eduPersonAffiliation", "eduPersonScopedAffiliation", "eduPersonPrimaryAffiliation", "eduPersonEntitlement", "eduPersonAssurance", "eduPersonUniqueId", "employeeNumber"] }, "uniqueItems": true } }, "definitions": { "Contact": { "type": "object", "required": ["name", "type", "emailAddress"], "properties": { "name": { "title": "label.contact-name", "description": "tooltip.contact-name", "type": "string", "minLength": 1, "maxLength": 255 }, "type": { "title": "label.contact-type", "description": "tooltip.contact-type", "type": "string", "widget": "select", "minLength": 1, "oneOf": [{ "enum": ["support"], "description": "value.support" }, { "enum": ["technical"], "description": "value.technical" }, { "enum": ["administrative"], "description": "value.administrative" }, { "enum": ["other"], "description": "value.other" }] }, "emailAddress": { "title": "label.contact-email-address", "description": "tooltip.contact-email", "type": "string", "pattern": "^(mailto:)?(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$", "minLength": 1, "maxLength": 255 } } }, "Certificate": { "type": "object", "required": ["type", "value"], "properties": { "name": { "title": "label.certificate-name-display-only", "description": "tooltip.certificate-name", "type": "string", "maxLength": 255 }, "type": { "title": "label.certificate-type", "type": "string", "widget": { "id": "radio", "class": "form-check-inline" }, "oneOf": [{ "enum": ["signing"], "description": "value.signing" }, { "enum": ["encryption"], "description": "value.encryption" }, { "enum": ["both"], "description": "value.both" }] }, "value": { "title": "label.certificate", "description": "tooltip.certificate", "type": "string", "widget": "textarea", "minLength": 1 } } }, "AssertionConsumerService": { "type": "object", "required": ["locationUrl", "binding"], "properties": { "locationUrl": { "title": "label.assertion-consumer-service-location", "description": "tooltip.assertion-consumer-service-location", "type": "string", "widget": { "id": "string", "help": "message.valid-url" }, "minLength": 1, "maxLength": 255 }, "binding": { "title": "label.assertion-consumer-service-location-binding", "description": "tooltip.assertion-consumer-service-location-binding", "type": "string", "widget": "select", "oneOf": [{ "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:PAOS"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:PAOS" }, { "enum": ["urn:oasis:names:tc:SAML:1.0:profiles:browser-post"], "description": "urn:oasis:names:tc:SAML:1.0:profiles:browser-post" }, { "enum": ["urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"], "description": "urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" }] }, "makeDefault": { "title": "label.mark-as-default", "description": "tooltip.mark-as-default", "type": "boolean" } } }, "LogoutEndpoint": { "description": "tooltip.new-endpoint", "type": "object", "fieldsets": [{ "fields": ["url", "bindingType"] }], "required": ["url", "bindingType"], "properties": { "url": { "title": "label.url", "description": "tooltip.url", "type": "string", "minLength": 1, "maxLength": 255 }, "bindingType": { "title": "label.binding-type", "description": "tooltip.binding-type", "type": "string", "widget": "select", "oneOf": [{ "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:SOAP"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" }, { "enum": ["urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"], "description": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" }] } } }, "MDUI": { "type": "object", "widget": { "id": "fieldset" }, "fieldsets": [{ "type": "group", "fields": ["displayName", "informationUrl", "description"] }, { "type": "group", "fields": ["privacyStatementUrl", "logoUrl", "logoWidth", "logoHeight"] }], "properties": { "displayName": { "title": "label.display-name", "description": "tooltip.mdui-display-name", "type": "string", "minLength": 1, "maxLength": 255 }, "informationUrl": { "title": "label.information-url", "description": "tooltip.mdui-information-url", "type": "string", "minLength": 1, "maxLength": 255 }, "privacyStatementUrl": { "title": "label.privacy-statement-url", "description": "tooltip.mdui-privacy-statement-url", "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "title": "label.description", "description": "tooltip.mdui-description", "type": "string", "widget": { "id": "textarea" }, "minLength": 1, "maxLength": 255 }, "logoUrl": { "title": "label.logo-url", "description": "tooltip.mdui-logo-url", "type": "string", "minLength": 1, "maxLength": 255 }, "logoHeight": { "title": "label.logo-height", "description": "tooltip.mdui-logo-height", "minimum": 0, "type": "integer" }, "logoWidth": { "title": "label.logo-width", "description": "tooltip.mdui-logo-width", "minimum": 0, "type": "integer" } } }, "Organization": { "type": "object", "properties": { "name": { "title": "label.organization-name", "description": "tooltip.organization-name", "type": "string", "minLength": 1, "maxLength": 255 }, "displayName": { "title": "label.organization-display-name", "description": "tooltip.organization-display-name", "type": "string", "minLength": 1, "maxLength": 255 }, "url": { "title": "label.organization-url", "description": "tooltip.organization-url", "type": "string", "minLength": 1, "maxLength": 255 } }, "dependencies": { "name": { "required": ["displayName", "url"] }, "displayName": { "required": ["name", "url"] }, "url": { "required": ["name", "displayName"] } } }, "nameIdFormats": { "title": "label.nameid-format-to-send", "description": "tooltip.nameid-format", "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1, "maxLength": 255, "examples": ["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"] } }, "authenticationMethods": { "title": "label.authentication-methods-to-use", "description": "tooltip.authentication-methods-to-use", "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1, "maxLength": 255, "examples": ["https://refeds.org/profile/mfa", "urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken", "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"] } } } };
+
+export default SCHEMA;
\ No newline at end of file
diff --git a/ui/src/testing/uiSchema.js b/ui/src/testing/uiSchema.js
new file mode 100644
index 000000000..eb557bcf2
--- /dev/null
+++ b/ui/src/testing/uiSchema.js
@@ -0,0 +1,216 @@
+const schema = {
+ "ui:order": {
+ "0": "serviceProviderName",
+ "1": "*",
+ "ui:widget": "hidden"
+ },
+ "layout": {
+ "groups": [
+ {
+ "size": 6,
+ "fields": [
+ "serviceProviderName",
+ "entityId",
+ "serviceEnabled",
+ "organization"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "contacts"
+ ]
+ },
+ {
+ "size": 12,
+ "fields": [
+ "mdui"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "serviceProviderSsoDescriptor"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "logoutEndpoints"
+ ]
+ },
+ {
+ "size": 12,
+ "fields": [
+ "securityInfo"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "assertionConsumerServices"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "relyingPartyOverrides"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "attributeRelease"
+ ]
+ }
+ ],
+ "ui:widget": "hidden"
+ },
+ "contacts": {
+ "ui:options": {
+ "orderable": false
+ },
+ "type": "contact",
+ "ui:title": false
+ },
+ "attributeRelease": {
+ "ui:widget": "hidden"
+ },
+ "logoutEndpoints": {
+ "type": "endpoint",
+ "ui:options": {
+ "orderable": false
+ },
+ "ui:title": false,
+ "ui:widget": "hidden"
+ },
+ "assertionConsumerServices": {
+ "type": "service",
+ "ui:options": {
+ "orderable": false
+ },
+ "ui:title": false,
+ "ui:widget": "hidden"
+ },
+ "relyingPartyOverrides": {
+ "nameIdFormats": {
+ "ui:options": {
+ "orderable": false
+ },
+ "items": {
+ "ui:widget": "OptionWidget"
+ }
+ },
+ "authenticationMethods": {
+ "ui:options": {
+ "orderable": false
+ },
+ "items": {
+ "ui:widget": "OptionWidget"
+ }
+ },
+ "ui:widget": "hidden"
+ },
+ "serviceProviderSsoDescriptor": {
+ "protocolSupportEnum": {
+ "ui:placeholder": "label.select-protocol"
+ },
+ "nameIdFormats": {
+ "ui:options": {
+ "orderable": false
+ },
+ "items": {
+ "ui:widget": "OptionWidget"
+ }
+ },
+ "ui:widget": "hidden"
+ },
+ "securityInfo": {
+ "layout": {
+ "groups": [
+ {
+ "size": 6,
+ "fields": [
+ "authenticationRequestsSigned",
+ "wantAssertionsSigned",
+ "x509Certificates"
+ ]
+ }
+ ]
+ },
+ "x509CertificateAvailable": {
+ "ui:widget": "hidden"
+ },
+ "authenticationRequestsSigned": {
+ "ui:widget": "radio",
+ "ui:options": {
+ "inline": true
+ }
+ },
+ "wantAssertionsSigned": {
+ "ui:widget": "radio",
+ "ui:options": {
+ "inline": true
+ }
+ },
+ "x509Certificates": {
+ "type": "certificate",
+ "ui:options": {
+ "orderable": false
+ },
+ "items": {
+ "type": {
+ "ui:widget": "radio",
+ "ui:description": false,
+ "ui:options": {
+ "inline": true
+ }
+ },
+ "value": {
+ "ui:widget": "textarea"
+ }
+ }
+ },
+ "ui:widget": "hidden"
+ },
+ "mdui": {
+ "layout": {
+ "groups": [
+ {
+ "size": 6,
+ "fields": [
+ "displayName",
+ "informationUrl",
+ "description"
+ ]
+ },
+ {
+ "size": 6,
+ "fields": [
+ "privacyStatementUrl",
+ "logoUrl",
+ "logoWidth",
+ "logoHeight"
+ ]
+ }
+ ]
+ },
+ "description": {
+ "ui:widget": "textarea"
+ },
+ "logoHeight": {
+ "ui:widget": "updown"
+ },
+ "logoWidth": {
+ "ui:widget": "updown"
+ },
+ "ui:widget": "hidden"
+ },
+ "serviceProviderName": {},
+ "entityId": {},
+ "serviceEnabled": {},
+ "organization": {},
+ "ui:disabled": false
+};
+
+export default schema;
\ No newline at end of file
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 94dfe0128..dcbc3d1c9 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -1197,6 +1197,11 @@
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9"
integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw==
+"@fortawesome/fontawesome-free@^5.15.3":
+ version "5.15.3"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz#c36ffa64a2a239bf948541a97b6ae8d729e09a9a"
+ integrity sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==
+
"@fortawesome/fontawesome-svg-core@^1.2.35":
version "1.2.35"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff"
@@ -1702,10 +1707,10 @@
lz-string "^1.4.4"
pretty-format "^26.6.2"
-"@testing-library/jest-dom@^5.11.4":
- version "5.12.0"
- resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.12.0.tgz#6a5d340b092c44b7bce17a4791b47d9bc2c61443"
- integrity sha512-N9Y82b2Z3j6wzIoAqajlKVF1Zt7sOH0pPee0sUHXHc5cv2Fdn23r+vpWm0MBBoGJtPOly5+Bdx1lnc3CD+A+ow==
+"@testing-library/jest-dom@^5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.13.0.tgz#0a365684e2c1159f857f5915be50089fc5657df0"
+ integrity sha512-+jXXTn8GjRnZkJfzG/tqK/2Q7dGlBInR412WE7Aml7CT3wdSpx5dMQC0HOwVQoZ3cNTmQUy8fCVGUV/Zhoyvcw==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
@@ -1716,10 +1721,10 @@
lodash "^4.17.15"
redent "^3.0.0"
-"@testing-library/react@^11.1.0":
- version "11.2.6"
- resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b"
- integrity sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==
+"@testing-library/react@^11.2.7":
+ version "11.2.7"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818"
+ integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==
dependencies:
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^7.28.1"