diff --git a/packages/components/src/internal/components/ontology/ConceptInformationTabs.spec.tsx b/packages/components/src/internal/components/ontology/ConceptInformationTabs.spec.tsx
deleted file mode 100644
index 7a3891b37b..0000000000
--- a/packages/components/src/internal/components/ontology/ConceptInformationTabs.spec.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { ConceptInformationTabs, ConceptInfoTabs } from './ConceptInformationTabs';
-import { ConceptOverviewPanelImpl } from './ConceptOverviewPanel';
-import { ConceptModel } from './models';
-import { ConceptPathInfo } from './ConceptPathInfo';
-
-const DEFAULT_PROPS = {
- concept: undefined,
-};
-
-describe('ConceptInformationTabs', () => {
- function validate(wrapper: ReactWrapper) {
- expect(wrapper.find('li[role="presentation"]')).toHaveLength(2);
- expect(wrapper.find('.ontology-concept-overview-container')).toHaveLength(2);
- expect(wrapper.find(ConceptOverviewPanelImpl)).toHaveLength(1);
- expect(wrapper.find(ConceptPathInfo)).toHaveLength(1);
- }
-
- test('no concept', () => {
- const wrapper = mount();
- validate(wrapper);
- expect(wrapper.find(ConceptOverviewPanelImpl).prop('concept')).toBe(undefined);
- wrapper.unmount();
- });
-
- test('with concept', () => {
- const concept = new ConceptModel({ code: 'a', label: 'b' });
- const wrapper = mount(
-
- );
- validate(wrapper);
- expect(wrapper.find(ConceptOverviewPanelImpl).prop('concept')).toBe(concept);
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/ConceptInformationTabs.test.tsx b/packages/components/src/internal/components/ontology/ConceptInformationTabs.test.tsx
new file mode 100644
index 0000000000..62233d9f7d
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/ConceptInformationTabs.test.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import { ConceptInformationTabs } from './ConceptInformationTabs';
+import { ConceptModel } from './models';
+import { waitFor } from '@testing-library/dom';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+}));
+
+const DEFAULT_PROPS = {
+ concept: undefined,
+ alternatePathClickHandler: jest.fn(),
+};
+
+describe('ConceptInformationTabs', () => {
+ test('no concept', () => {
+ const { container } = render();
+ expect(container.querySelectorAll('li[role="presentation"]')).toHaveLength(2);
+ expect(container.querySelectorAll('.ontology-concept-overview-container')).toHaveLength(1);
+ expect(container.querySelectorAll('.ontology-concept-pathinfo-container')).toHaveLength(1);
+ expect(container.querySelectorAll('.none-selected')).toHaveLength(2);
+ });
+
+ test('with concept', async () => {
+ const concept = new ConceptModel({ code: 'a', label: 'b' });
+ const { container } = render();
+ await waitFor(() => {
+ expect(container.querySelectorAll('li[role="presentation"]')).toHaveLength(2);
+ });
+ expect(container.querySelectorAll('.ontology-concept-overview-container')).toHaveLength(1);
+ expect(container.querySelectorAll('.ontology-concept-pathinfo-container')).toHaveLength(1);
+ expect(container.querySelector('.title').textContent).toBe('b');
+ expect(container.querySelector('.code').textContent).toBe('a');
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/ConceptOverviewPanel.spec.tsx b/packages/components/src/internal/components/ontology/ConceptOverviewPanel.spec.tsx
deleted file mode 100644
index 7b7cc911bd..0000000000
--- a/packages/components/src/internal/components/ontology/ConceptOverviewPanel.spec.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-
-import { LabelHelpTip } from '../base/LabelHelpTip';
-
-import {
- ConceptOverviewTooltip,
- ConceptOverviewPanelImpl,
- ConceptSynonyms,
- OntologyConceptOverviewPanel,
-} from './ConceptOverviewPanel';
-import { ConceptPathDisplay } from './ConceptPathDisplay';
-import { ConceptModel, PathModel } from './models';
-import { waitForLifecycle } from '../../test/enzymeTestHelpers';
-
-const TEST_CONCEPT = new ConceptModel({ code: 'a', label: 'b', description: 'c' });
-const TEST_PATH = new PathModel({});
-
-describe('OntologyConceptOverviewPanel', () => {
- test('without code prop', () => {
- const wrapper = mount();
- expect(wrapper.find(Alert)).toHaveLength(1);
- expect(wrapper.find(Alert).text()).toBe('');
- expect(wrapper.find(ConceptOverviewPanelImpl)).toHaveLength(0);
- wrapper.unmount();
- });
-});
-
-describe('ConceptSynonyms', () => {
- test('without synonyms', () => {
- let wrapper = mount();
- expect(wrapper.find('.synonyms-title')).toHaveLength(0);
- expect(wrapper.find('.synonyms-text')).toHaveLength(0);
- wrapper.unmount();
-
- wrapper = mount();
- expect(wrapper.find('.synonyms-title')).toHaveLength(0);
- expect(wrapper.find('.synonyms-text')).toHaveLength(0);
- wrapper.unmount();
- });
-
- test('with sorted synonyms', () => {
- const wrapper = mount();
- expect(wrapper.find('.synonyms-title')).toHaveLength(1);
- expect(wrapper.find('.synonyms-text')).toHaveLength(1);
- expect(wrapper.find('li')).toHaveLength(3);
- expect(wrapper.find('li').at(0).text()).toBe('a');
- expect(wrapper.find('li').at(1).text()).toBe('b');
- expect(wrapper.find('li').at(2).text()).toBe('c');
- wrapper.unmount();
- });
-});
-
-describe('ConceptOverviewPanelImpl', () => {
- function validate(
- wrapper: ReactWrapper,
- concept: ConceptModel,
- divCount = 1,
- hasSelectedPath = false,
- showPath = false
- ): void {
- expect(wrapper.find('.none-selected')).toHaveLength(concept === undefined ? 1 : 0);
- expect(wrapper.find('div')).toHaveLength(divCount);
- expect(wrapper.find(ConceptPathDisplay)).toHaveLength(showPath ? 1 : 0);
-
- expect(wrapper.find('button')).toHaveLength(hasSelectedPath ? 1 : 0);
- if (hasSelectedPath) {
- expect(wrapper.find('button').text()).toBe(showPath ? 'Hide Path' : 'Show Path');
- }
-
- if (concept) {
- expect(wrapper.find('.title').first().text()).toBe(concept.label);
- expect(wrapper.find('.code').first().text()).toBe(concept.code);
- expect(wrapper.find('.description-title').text()).toBe('Description');
- expect(wrapper.find('.description-text').text()).toBe(concept.description);
- }
- }
-
- test('no concept', () => {
- const wrapper = mount();
- validate(wrapper, undefined);
- wrapper.unmount();
- });
-
- test('with concept', () => {
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT, 3);
- wrapper.unmount();
- });
-
- test('with selected path, not shown', () => {
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT, 4, true);
- wrapper.unmount();
- });
-
- test('with selected path, shown', () => {
- const wrapper = mount();
- wrapper.find('button').simulate('click');
- validate(wrapper, TEST_CONCEPT, 8, true, true);
- expect(wrapper.find(ConceptPathDisplay).prop('title')).toBe('Current Path');
- expect(wrapper.find(ConceptPathDisplay).prop('path')).toBe(TEST_PATH);
- expect(wrapper.find(ConceptPathDisplay).prop('isSelected')).toBe(true);
- wrapper.unmount();
- });
-});
-
-describe('ConceptOverviewToolTip', () => {
- async function validate(wrapper: ReactWrapper, concept?: ConceptModel, errorTxt?: string): void {
- expect(wrapper.find(LabelHelpTip)).toHaveLength(errorTxt ? 0 : 1);
- expect(wrapper.find(Alert)).toHaveLength(1);
- expect(wrapper.find(Alert).text()).toBe(errorTxt ?? '');
- const infoIcon = wrapper.find('.fa-info-circle');
- expect(infoIcon).toHaveLength(errorTxt ? 0 : !concept ? 0 : 1);
-
- if (infoIcon.length > 0) {
- wrapper.find('.overlay-trigger').simulate('mouseenter');
- await waitForLifecycle(wrapper);
- const over = wrapper.find('.ontology-concept-overview-container');
- expect(over).toHaveLength(errorTxt ? 0 : 1);
- expect(over.find(ConceptOverviewPanelImpl)).toHaveLength(errorTxt ? 0 : 1);
- }
- }
-
- test('no concept', async () => {
- const wrapper = mount();
- await validate(wrapper);
- wrapper.unmount();
- });
-
- test('with concept', async () => {
- const wrapper = mount();
- await validate(wrapper, TEST_CONCEPT);
- expect(wrapper.find(ConceptOverviewPanelImpl).prop('concept')).toBe(TEST_CONCEPT);
- wrapper.unmount();
- });
-
- test('with path', async () => {
- const wrapper = mount();
- await validate(wrapper, TEST_CONCEPT);
- expect(wrapper.find(ConceptOverviewPanelImpl).prop('selectedPath')).toBe(TEST_PATH);
- wrapper.unmount();
- });
-
- test('error', async () => {
- const wrapper = mount();
- await validate(wrapper, TEST_CONCEPT, 'test error');
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/ConceptOverviewPanel.test.tsx b/packages/components/src/internal/components/ontology/ConceptOverviewPanel.test.tsx
new file mode 100644
index 0000000000..d5268fd73f
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/ConceptOverviewPanel.test.tsx
@@ -0,0 +1,119 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+
+import {
+ ConceptOverviewPanelImpl,
+ ConceptOverviewTooltip,
+ ConceptSynonyms,
+ OntologyConceptOverviewPanel,
+} from './ConceptOverviewPanel';
+import { ConceptModel, PathModel } from './models';
+import { waitFor } from '@testing-library/dom';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+}));
+
+const TEST_CONCEPT = new ConceptModel({ code: 'a', label: 'b', description: 'c' });
+const TEST_PATH = new PathModel({});
+
+describe('OntologyConceptOverviewPanel', () => {
+ test('without code prop', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.none-selected')).toBeNull();
+ expect(container.querySelector('.title')).toBeNull();
+ });
+});
+
+describe('ConceptSynonyms', () => {
+ test('without synonyms', () => {
+ const { container, rerender } = render();
+ expect(container.querySelector('.synonyms-title')).toBeNull();
+ expect(container.querySelector('.synonyms-text')).toBeNull();
+
+ rerender();
+ expect(container.querySelector('.synonyms-title')).toBeNull();
+ expect(container.querySelector('.synonyms-text')).toBeNull();
+ });
+
+ test('with sorted synonyms', () => {
+ const { container } = render();
+ expect(container.querySelector('.synonyms-title')).not.toBeNull();
+ expect(container.querySelector('.synonyms-text')).not.toBeNull();
+ const items = container.querySelectorAll('li');
+ expect(items).toHaveLength(3);
+ expect(items[0].textContent).toBe('a');
+ expect(items[1].textContent).toBe('b');
+ expect(items[2].textContent).toBe('c');
+ });
+});
+
+describe('ConceptOverviewPanelImpl', () => {
+ test('no concept', () => {
+ const { container } = render();
+ expect(container.querySelector('.none-selected').textContent).toBe('No concept selected');
+ expect(container.querySelector('.title')).toBeNull();
+ expect(container.querySelector('.code')).toBeNull();
+ });
+
+ test('with concept', () => {
+ const { container } = render();
+ expect(container.querySelector('.none-selected')).toBeNull();
+ expect(container.querySelector('button')).toBeNull();
+ expect(container.querySelector('.title').textContent).toBe('b');
+ expect(container.querySelector('.code').textContent).toBe('a');
+ expect(container.querySelector('.description-title').textContent).toBe('Description');
+ expect(container.querySelector('.description-text').textContent).toBe('c');
+ });
+
+ test('with selected path, not shown', () => {
+ const { container } = render();
+ const button = container.querySelector('button');
+ expect(button.textContent).toBe('Show Path');
+ expect(container.querySelector('.concept-overview-selected-path')).toBeNull();
+ });
+
+ test('with selected path, shown', async () => {
+ const { container } = render();
+ fireEvent.click(container.querySelector('button'));
+ await waitFor(() => {
+ expect(container.querySelector('button').textContent).toBe('Hide Path');
+ });
+ expect(container.querySelector('.concept-overview-selected-path')).not.toBeNull();
+ expect(container.querySelector('.concept-path-container.selected')).not.toBeNull();
+ });
+});
+
+describe('ConceptOverviewTooltip', () => {
+ test('no concept', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.fa-info-circle')).toBeNull();
+ expect(container.querySelector('.overlay-trigger')).not.toBeNull();
+ });
+
+ test('with concept', async () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.fa-info-circle')).not.toBeNull();
+ fireEvent.mouseEnter(container.querySelector('.overlay-trigger'));
+ await waitFor(() => {
+ expect(document.querySelector('.ontology-concept-overview-container')).not.toBeNull();
+ });
+ });
+
+ test('with path', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.fa-info-circle')).not.toBeNull();
+ });
+
+ test('error', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]').textContent).toBe('test error');
+ expect(container.querySelector('.overlay-trigger')).toBeNull();
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/ConceptPathDisplay.spec.tsx b/packages/components/src/internal/components/ontology/ConceptPathDisplay.spec.tsx
deleted file mode 100644
index 8db79bcd99..0000000000
--- a/packages/components/src/internal/components/ontology/ConceptPathDisplay.spec.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-
-import { ConceptPathDisplay, ConceptPathDisplayImpl } from './ConceptPathDisplay';
-
-import { PathModel } from './models';
-
-const TEST_CONCEPT_PATH = new PathModel();
-
-describe('ConceptPathDisplay', () => {
- test('Path not set', () => {
- const wrapper = mount();
- expect(wrapper.find(Alert)).toHaveLength(1);
- expect(wrapper.find(Alert).text()).toBe('');
- expect(wrapper.find(ConceptPathDisplayImpl)).toHaveLength(0);
- wrapper.unmount();
- });
-
- test('Path set', () => {
- const wrapper = mount();
- expect(wrapper.find(ConceptPathDisplayImpl)).toHaveLength(1);
- expect(wrapper.find(ConceptPathDisplayImpl).prop('path')).toBe(TEST_CONCEPT_PATH);
- expect(wrapper.find(ConceptPathDisplayImpl).prop('title')).toBe('test title');
- expect(wrapper.find(ConceptPathDisplayImpl).prop('isSelected')).toBe(true);
- expect(wrapper.find(ConceptPathDisplayImpl).prop('parentPaths')).toBe(undefined);
- wrapper.unmount();
- });
-});
-
-describe('ConceptPathDisplayImpl', () => {
- function validate(
- wrapper: ReactWrapper,
- path: PathModel = undefined,
- parentCount = 0,
- title: string = undefined,
- isSelected = false,
- isLoading = false
- ): void {
- expect(wrapper.find('.concept-path-container')).toHaveLength(path ? 1 : 0);
- expect(wrapper.find('.concept-path')).toHaveLength(path ? 1 : 0);
- expect(wrapper.find('.selected')).toHaveLength(isSelected ? 1 : 0);
- expect(wrapper.find('.concept-path-label')).toHaveLength(parentCount);
- expect(wrapper.find('i')).toHaveLength(parentCount === 0 ? (isLoading ? 1 : 0) : parentCount - 1);
- expect(wrapper.find('.concept-path-spacer')).toHaveLength(parentCount > 0 ? parentCount - 1 : 0);
- expect(wrapper.find('.title')).toHaveLength(title ? 1 : 0);
-
- if (title) {
- expect(wrapper.find('.title').text()).toBe(title);
- }
- }
-
- test('No path loaded', () => {
- const wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
- });
-
- test('Parent path not loaded yet', () => {
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT_PATH, 0, undefined, false, true);
- wrapper.unmount();
- });
-
- test('Parent path empty', () => {
- const parentPaths = [];
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT_PATH, parentPaths.length);
- wrapper.unmount();
- });
-
- test('Parent path set', () => {
- const pathModel1 = new PathModel({
- label: 'first',
- });
-
- const parentPaths = [pathModel1];
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT_PATH, parentPaths.length);
-
- expect(wrapper.find('.concept-path-label').at(0).text()).toBe('first');
- wrapper.unmount();
- });
-
- test('Parent path set', () => {
- const pathModel1 = new PathModel({
- label: 'first',
- });
-
- const pathModel2 = new PathModel({
- label: 'second',
- });
-
- const pathModel3 = new PathModel({
- label: 'third',
- });
-
- const parentPaths = [pathModel1, pathModel2, pathModel3];
- const wrapper = mount();
- validate(wrapper, TEST_CONCEPT_PATH, parentPaths.length);
-
- expect(wrapper.find('.concept-path-label').at(0).text()).toBe('first');
- expect(wrapper.find('.concept-path-label').at(1).text()).toBe('second');
- expect(wrapper.find('.concept-path-label').at(2).text()).toBe('third');
- wrapper.unmount();
- });
-
- test('Title set', () => {
- const parentPaths = [];
- const title = 'Long title to show';
- const wrapper = mount(
-
- );
- validate(wrapper, TEST_CONCEPT_PATH, parentPaths.length, title);
- wrapper.unmount();
- });
-
- test('Selected set', () => {
- const parentPaths = [];
- const title = 'Long title to show';
- const selected = true;
- const wrapper = mount(
-
- );
- validate(wrapper, TEST_CONCEPT_PATH, parentPaths.length, title, selected);
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/ConceptPathDisplay.test.tsx b/packages/components/src/internal/components/ontology/ConceptPathDisplay.test.tsx
new file mode 100644
index 0000000000..ad4b7f73fe
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/ConceptPathDisplay.test.tsx
@@ -0,0 +1,98 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import { ConceptPathDisplay, ConceptPathDisplayImpl } from './ConceptPathDisplay';
+import { PathModel } from './models';
+import { waitFor } from '@testing-library/dom';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+}));
+
+const TEST_CONCEPT_PATH = new PathModel();
+
+describe('ConceptPathDisplay', () => {
+ test('Path not set', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.concept-path-container')).toBeNull();
+ });
+
+ test('Path set', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.concept-path-container.selected')).not.toBeNull();
+ });
+ expect(container.querySelector('.title').textContent).toBe('test title');
+ expect(container.querySelector('.fa-spinner')).toBeNull();
+ });
+});
+
+describe('ConceptPathDisplayImpl', () => {
+ test('No path loaded', () => {
+ const { container } = render();
+ expect(container.querySelector('.concept-path-container')).toBeNull();
+ expect(container.querySelector('.concept-path')).toBeNull();
+ });
+
+ test('Parent path not loaded yet', () => {
+ const { container } = render();
+ expect(container.querySelector('.concept-path-container')).not.toBeNull();
+ expect(container.querySelector('.concept-path')).not.toBeNull();
+ expect(container.querySelector('.selected')).toBeNull();
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ expect(container.querySelectorAll('.concept-path-label')).toHaveLength(0);
+ });
+
+ test('Parent path empty', () => {
+ const { container } = render();
+ expect(container.querySelector('.concept-path-container')).not.toBeNull();
+ expect(container.querySelector('.fa-spinner')).toBeNull();
+ expect(container.querySelectorAll('.concept-path-label')).toHaveLength(0);
+ expect(container.querySelectorAll('.concept-path-spacer')).toHaveLength(0);
+ });
+
+ test('Parent path set with one path', () => {
+ const parentPaths = [new PathModel({ label: 'first' })];
+ const { container } = render();
+ const labels = container.querySelectorAll('.concept-path-label');
+ expect(labels).toHaveLength(1);
+ expect(labels[0].textContent).toBe('first');
+ expect(container.querySelectorAll('.concept-path-spacer')).toHaveLength(0);
+ });
+
+ test('Parent path set with multiple paths', () => {
+ const parentPaths = [
+ new PathModel({ label: 'first' }),
+ new PathModel({ label: 'second' }),
+ new PathModel({ label: 'third' }),
+ ];
+ const { container } = render();
+ const labels = container.querySelectorAll('.concept-path-label');
+ expect(labels).toHaveLength(3);
+ expect(labels[0].textContent).toBe('first');
+ expect(labels[1].textContent).toBe('second');
+ expect(labels[2].textContent).toBe('third');
+ expect(container.querySelectorAll('.concept-path-spacer')).toHaveLength(2);
+ });
+
+ test('Title set', () => {
+ const title = 'Long title to show';
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.title').textContent).toBe(title);
+ });
+
+ test('Selected set', () => {
+ const title = 'Long title to show';
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.concept-path-container.selected')).not.toBeNull();
+ expect(container.querySelector('.title').textContent).toBe(title);
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/ConceptPathDisplay.tsx b/packages/components/src/internal/components/ontology/ConceptPathDisplay.tsx
index 3dea97ae1e..b3c07c0d0f 100644
--- a/packages/components/src/internal/components/ontology/ConceptPathDisplay.tsx
+++ b/packages/components/src/internal/components/ontology/ConceptPathDisplay.tsx
@@ -70,12 +70,12 @@ export const ConceptPathDisplayImpl: FC = memo(prop
{!parentPaths && }
{parentPaths?.map((parent, idx) => {
return (
- <>
+
{parent.label}
{idx !== parentPaths.length - 1 && (
)}
- >
+
);
})}
diff --git a/packages/components/src/internal/components/ontology/ConceptPathInfo.spec.tsx b/packages/components/src/internal/components/ontology/ConceptPathInfo.spec.tsx
deleted file mode 100644
index 2ab58b9691..0000000000
--- a/packages/components/src/internal/components/ontology/ConceptPathInfo.spec.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-
-import { LoadingSpinner } from '../base/LoadingSpinner';
-
-import { PathModel } from './models';
-import { AlternatePathPanel, ConceptPathInfo, ConceptPathInfoImpl } from './ConceptPathInfo';
-
-import { ConceptPathDisplay } from './ConceptPathDisplay';
-
-describe('ConceptPathInfo', () => {
- test('Nothing set', () => {
- const wrapper = mount();
- expect(wrapper.find(Alert)).toHaveLength(1);
- expect(wrapper.find(Alert).text()).toBe('');
- expect(wrapper.find(ConceptPathInfoImpl)).toHaveLength(1);
- expect(wrapper.find(ConceptPathInfoImpl).prop('selectedCode')).toBe(undefined);
- expect(wrapper.find(ConceptPathInfoImpl).prop('alternatePaths')).toBe(undefined);
- wrapper.unmount();
- });
-});
-
-describe('ConceptPathInfoImpl', () => {
- function validate(
- wrapper: ReactWrapper,
- code: string = undefined,
- loadingCount = 0,
- alternatePaths: PathModel[] = undefined,
- selectedPath: PathModel = undefined
- ): void {
- expect(wrapper.find('.none-selected')).toHaveLength(code ? 0 : 1);
- expect(wrapper.find(LoadingSpinner)).toHaveLength(loadingCount);
- expect(wrapper.find(ConceptPathDisplay)).toHaveLength(alternatePaths?.length ? alternatePaths.length : 0);
- expect(wrapper.find('.current-path-container')).toHaveLength(
- alternatePaths?.length > 0 && selectedPath ? 1 : 0
- );
- expect(wrapper.find('.current-path-container')?.find(ConceptPathDisplay)).toHaveLength(
- alternatePaths?.length > 0 && selectedPath ? 1 : 0
- );
- expect(wrapper.find('.current-path-container')?.find('.selected')).toHaveLength(
- alternatePaths?.length > 0 && selectedPath ? 1 : 0
- );
- expect(wrapper.find('.alternate-paths-container')).toHaveLength(alternatePaths?.length > 0 ? 1 : 0);
- expect(wrapper.find('.alternate-paths-container')?.find(ConceptPathDisplay)).toHaveLength(
- alternatePaths?.length > 0 ? alternatePaths.length - 1 : 0
- );
- expect(wrapper.find('.no-path-info')).toHaveLength(alternatePaths?.length <= 1 ? 1 : 0);
- }
-
- test('Nothing set', () => {
- const wrapper = mount();
- expect(wrapper.find('.none-selected')).toHaveLength(1);
- expect(wrapper.find('.none-selected').text()).toBe('No concept selected');
- expect(wrapper.find('.concept-pathinfo-container')).toHaveLength(0);
- expect(wrapper.find(ConceptPathDisplay)).toHaveLength(0);
- wrapper.unmount();
- });
-
- test('Code set, aka Loading', () => {
- const code = 'MagicCode';
- const wrapper = mount();
- validate(wrapper, code, 1);
- wrapper.unmount();
- });
-
- test('Loading, Selected path set, alternate paths undefined', () => {
- const code = 'MagicCode';
- const path = new PathModel({
- path: 'abcd/efg/',
- label: 'first',
- });
- const alternatePaths = undefined;
- const wrapper = mount(
-
- );
- validate(wrapper, code, 1, alternatePaths, path);
- wrapper.unmount();
- });
-
- /**
- * NOTE: this scenario should be impossible in reality as selectedPath should always be included in the alternatePaths set
- */
- test('Selected path set, alternate paths empty', () => {
- const code = 'MagicCode';
- const path = new PathModel({
- path: 'abcd/efg/',
- label: 'first',
- });
- const alternatePaths = [];
- const wrapper = mount(
-
- );
- validate(wrapper, code, 0, alternatePaths, path);
- wrapper.unmount();
- });
-
- test('Only selected path', () => {
- const code = 'MagicCode';
- const path = new PathModel({
- path: 'abcd/efg/',
- label: 'first',
- });
- const alternatePaths = [path];
- const wrapper = mount(
-
- );
- validate(wrapper, code, 1, alternatePaths, path);
- wrapper.unmount();
- });
-
- test('Selected path, and alternate paths', () => {
- const code = 'MagicCode';
- const path1 = new PathModel({
- path: 'abcd/efg/',
- label: 'first',
- });
-
- const path2 = new PathModel({
- path: '1234/efg/',
- label: 'second',
- });
- const path3 = new PathModel({
- path: 'abcd/efg/123',
- label: 'third',
- });
- const path4 = new PathModel({
- path: 'abcd/efg/4',
- label: 'fourth',
- });
- const selected = path3;
- const alternatePaths = [path1, path2, path3, path4];
- const wrapper = mount(
-
- );
- validate(wrapper, code, 4, alternatePaths, selected);
- wrapper.unmount();
- });
-});
-
-describe('AlternatePathPanel', () => {
- const TEST_SELECTED_PATH = new PathModel({ code: 'a', label: 'A', path: 'a/a' });
- const TEST_ALTERNATE_PATH = new PathModel({ code: 'a', label: 'A', path: 'b/a' });
-
- function validate(wrapper: ReactWrapper, hasSelectedPath = false, alternatePathCount = 0): void {
- expect(wrapper.find('.current-path-container')).toHaveLength(hasSelectedPath ? 1 : 0);
- expect(wrapper.find('.alternate-paths-container')).toHaveLength(1);
- expect(wrapper.find('.no-path-info')).toHaveLength(alternatePathCount === 0 ? 1 : 0);
- expect(wrapper.find('.title')).toHaveLength(1 + (hasSelectedPath ? 1 : 0));
- expect(wrapper.find(ConceptPathDisplay)).toHaveLength(alternatePathCount + (hasSelectedPath ? 1 : 0));
- }
-
- test('no paths', () => {
- const wrapper = mount(
-
- );
- validate(wrapper);
- wrapper.unmount();
- });
-
- test('with selected path but no alternates', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, true);
- expect(wrapper.find(ConceptPathDisplay).prop('path')).toBe(TEST_SELECTED_PATH);
- expect(wrapper.find(ConceptPathDisplay).prop('isSelected')).toBe(true);
- wrapper.unmount();
- });
-
- test('with selected path and alternate', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, true, 1);
- expect(wrapper.find(ConceptPathDisplay).first().prop('path')).toBe(TEST_SELECTED_PATH);
- expect(wrapper.find(ConceptPathDisplay).first().prop('isSelected')).toBe(true);
- expect(wrapper.find(ConceptPathDisplay).first().prop('onClick')).toBeUndefined();
- expect(wrapper.find(ConceptPathDisplay).last().prop('path')).toBe(TEST_ALTERNATE_PATH);
- expect(wrapper.find(ConceptPathDisplay).last().prop('isSelected')).toBeUndefined();
- expect(wrapper.find(ConceptPathDisplay).last().prop('onClick')).toBeDefined();
- wrapper.unmount();
- });
-
- test('with no selected path and alternates', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, false, 2);
- expect(wrapper.find(ConceptPathDisplay).first().prop('path')).toBe(TEST_SELECTED_PATH);
- expect(wrapper.find(ConceptPathDisplay).first().prop('isSelected')).toBeUndefined();
- expect(wrapper.find(ConceptPathDisplay).first().prop('onClick')).toBeDefined();
- expect(wrapper.find(ConceptPathDisplay).last().prop('path')).toBe(TEST_ALTERNATE_PATH);
- expect(wrapper.find(ConceptPathDisplay).last().prop('isSelected')).toBeUndefined();
- expect(wrapper.find(ConceptPathDisplay).last().prop('onClick')).toBeDefined();
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/ConceptPathInfo.test.tsx b/packages/components/src/internal/components/ontology/ConceptPathInfo.test.tsx
new file mode 100644
index 0000000000..df8c4f0db7
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/ConceptPathInfo.test.tsx
@@ -0,0 +1,173 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import { AlternatePathPanel, ConceptPathInfo, ConceptPathInfoImpl } from './ConceptPathInfo';
+import { PathModel } from './models';
+import { waitFor } from '@testing-library/dom';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+}));
+
+const TEST_SELECTED_PATH = new PathModel({ code: 'a', label: 'A', path: 'a/a' });
+const TEST_ALTERNATE_PATH = new PathModel({ code: 'a', label: 'A', path: 'b/a' });
+
+describe('ConceptPathInfo', () => {
+ test('Nothing set', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.none-selected').textContent).toBe('No concept selected');
+ });
+});
+
+describe('ConceptPathInfoImpl', () => {
+ test('Nothing set', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.none-selected').textContent).toBe('No concept selected');
+ expect(container.querySelector('.concept-pathinfo-container')).toBeNull();
+ });
+
+ test('Code set, aka Loading', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.concept-pathinfo-container')).not.toBeNull();
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container')).toBeNull();
+ });
+
+ test('Loading, Selected path set, alternate paths undefined', () => {
+ const path = new PathModel({ path: 'abcd/efg/', label: 'first' });
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.concept-pathinfo-container')).not.toBeNull();
+ expect(container.querySelector('.title').textContent).toBe('first');
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ });
+
+ test('Selected path set, alternate paths empty', () => {
+ const path = new PathModel({ path: 'abcd/efg/', label: 'first' });
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.concept-pathinfo-container')).not.toBeNull();
+ expect(container.querySelector('.fa-spinner')).toBeNull();
+ expect(container.querySelector('.no-path-info').textContent).toBe('No path information available');
+ });
+
+ test('Only selected path', async () => {
+ const path = new PathModel({ path: 'abcd/efg/', label: 'first' });
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.current-path-container')).not.toBeNull();
+ });
+ expect(container.querySelector('.current-path-container .concept-path-container.selected')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container .no-path-info').textContent).toBe(
+ 'No alternate paths'
+ );
+ });
+
+ test('Selected path and alternate paths', async () => {
+ const path1 = new PathModel({ path: 'abcd/efg/', label: 'first' });
+ const path2 = new PathModel({ path: '1234/efg/', label: 'second' });
+ const path3 = new PathModel({ path: 'abcd/efg/123', label: 'third' });
+ const path4 = new PathModel({ path: 'abcd/efg/4', label: 'fourth' });
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.current-path-container .concept-path-container.selected')).not.toBeNull();
+ });
+ expect(container.querySelector('.alternate-paths-container')).not.toBeNull();
+ expect(container.querySelectorAll('.alternate-paths-container .concept-path-container')).toHaveLength(3);
+ expect(container.querySelector('.alternate-paths-container .no-path-info')).toBeNull();
+ });
+});
+
+describe('AlternatePathPanel', () => {
+ test('no paths', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.current-path-container')).toBeNull();
+ expect(container.querySelector('.alternate-paths-container')).not.toBeNull();
+ expect(container.querySelector('.no-path-info').textContent).toBe('No alternate paths');
+ });
+
+ test('with selected path but no alternates', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.current-path-container')).not.toBeNull();
+ });
+ expect(container.querySelector('.current-path-container .concept-path-container.selected')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container .no-path-info').textContent).toBe(
+ 'No alternate paths'
+ );
+ });
+
+ test('with selected path and alternate', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.current-path-container .concept-path-container.selected')).not.toBeNull();
+ });
+ expect(container.querySelector('.alternate-paths-container .no-path-info')).toBeNull();
+ expect(container.querySelectorAll('.alternate-paths-container .concept-path-container')).toHaveLength(1);
+ });
+
+ test('with no selected path and alternates', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.current-path-container')).toBeNull();
+ });
+ expect(container.querySelector('.alternate-paths-container')).not.toBeNull();
+ expect(container.querySelector('.alternate-paths-container .no-path-info')).toBeNull();
+ expect(container.querySelectorAll('.alternate-paths-container .concept-path-container')).toHaveLength(2);
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.spec.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.spec.tsx
deleted file mode 100644
index 6e4a05a48e..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.spec.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-import React from 'react';
-
-import { Filter } from '@labkey/api';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-import { waitForLifecycle } from '../../test/enzymeTestHelpers';
-
-import { OntologyBrowserPanel } from './OntologyBrowserPanel';
-import { OntologyBrowserFilterPanel } from './OntologyBrowserFilterPanel';
-import { PathModel } from './models';
-
-const DEFAULT_PROPS = {
- ontologyId: 'TestOntology',
- conceptSubtree: undefined,
- filterValue: undefined,
- filterType: undefined,
- onFilterChange: jest.fn,
-};
-
-jest.mock('./actions.ts', () => {
- // Require the original module to not be mocked...
- const originalModule = jest.requireActual('./actions.ts');
- return {
- ...originalModule,
- fetchPathModel: jest.fn().mockReturnValue({
- path: 'root',
- code: 'testroot',
- label: 'test root',
- hasChildren: false,
- children: undefined,
- } as PathModel),
- };
-});
-
-const EqStub = { getURLSuffix: () => 'eq' } as Filter.IFilterType;
-const InSubtreeStub = { getURLSuffix: () => 'concept:insubtree' } as Filter.IFilterType;
-const NotInSubtreeStub = { getURLSuffix: () => 'concept:notinsubtree' } as Filter.IFilterType;
-
-describe('OntologyBrowserFilterPanel', () => {
- const validate = (wrapper: ReactWrapper): void => {
- expect(wrapper.find(Alert)).toHaveLength(2);
- expect(wrapper.find(Alert).first().text()).toBe('');
- expect(wrapper.find(OntologyBrowserPanel)).toHaveLength(1);
- expect(wrapper.find(OntologyBrowserPanel).prop('hideConceptInfo')).toBeTruthy();
- };
-
- test('default props', () => {
- const wrapper = mount();
- validate(wrapper);
-
- wrapper.unmount();
- });
-
- // change filter value
- test('Concept filter value changed', async () => {
- const changeHandler = jest.fn();
- const props = {
- ...DEFAULT_PROPS,
- filterValue: 'Test:Code',
- filterType: EqStub,
- onFilterChange: changeHandler,
- };
-
- const wrapper = mount();
- validate(wrapper);
- expect(changeHandler).toHaveBeenCalledTimes(0);
-
- await waitForLifecycle(wrapper.setProps({ filterValue: 'Mock:Code2' }));
- // Shouldn't call out to handler unless change is from the panel
- expect(changeHandler).toHaveBeenCalledTimes(0);
- expect(wrapper.find(OntologyBrowserPanel).prop('filters')).toHaveProperty('size', 1);
-
- // Multi valued filter
- await waitForLifecycle(wrapper.setProps({ filterValue: 'Mock:Code2;Mock:Code3;Mock:Code1' }));
- expect(changeHandler).toHaveBeenCalledTimes(0);
- expect(wrapper.find(OntologyBrowserPanel).prop('filters')).toHaveProperty('size', 3);
- expect(wrapper.find(OntologyBrowserPanel).prop('filters').get('Mock:Code3')).toHaveProperty(
- 'code',
- 'Mock:Code3'
- );
-
- // Path valued filter
- await waitForLifecycle(wrapper.setProps({ filterValue: 'Mock:Code2/Mock:Code3/Mock:Code1' }));
- expect(changeHandler).toHaveBeenCalledTimes(0);
- expect(wrapper.find(OntologyBrowserPanel).prop('filters')).toHaveProperty('size', 1);
-
- wrapper.unmount();
- });
-
- // change filter type
- test('Concept filter type changed', async () => {
- const changeHandler = jest.fn();
- const props = {
- ...DEFAULT_PROPS,
- filterValue: 'Test:Code',
- filterType: EqStub,
- onFilterChange: changeHandler,
- };
-
- const wrapper = mount();
- validate(wrapper);
- expect(changeHandler).toHaveBeenCalledTimes(0);
-
- await waitForLifecycle(wrapper.setProps({ filterType: InSubtreeStub }));
- // Shouldn't call out to handler unless change is from the panel
- expect(changeHandler).toHaveBeenCalledTimes(0);
-
- // Multi valued filter
- await waitForLifecycle(wrapper.setProps({ filterType: NotInSubtreeStub }));
- expect(changeHandler).toHaveBeenCalledTimes(0);
-
- // Path valued filter
- await waitForLifecycle(wrapper.setProps({ filterType: EqStub }));
- expect(changeHandler).toHaveBeenCalledTimes(0);
-
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.test.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.test.tsx
new file mode 100644
index 0000000000..5e59d1cb87
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyBrowserFilterPanel.test.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import { OntologyBrowserFilterPanel } from './OntologyBrowserFilterPanel';
+import { PathModel } from './models';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchChildPaths: jest.fn().mockResolvedValue({ children: [] }),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+ fetchPathModel: jest.fn().mockResolvedValue({
+ path: 'root',
+ code: 'testroot',
+ label: 'test root',
+ hasChildren: false,
+ children: undefined,
+ } as PathModel),
+ getOntologyDetails: jest.fn().mockResolvedValue(undefined),
+}));
+
+jest.mock('../files/FileTree', () => ({
+ DEFAULT_ROOT_PREFIX: '|root',
+ FileTree: () => ,
+}));
+
+const DEFAULT_PROPS = {
+ ontologyId: 'TestOntology',
+ conceptSubtree: undefined,
+ filterValue: undefined,
+ filterType: undefined,
+ onFilterChange: jest.fn(),
+};
+
+describe('OntologyBrowserFilterPanel', () => {
+ test('default props', () => {
+ const { container } = render();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserModal.spec.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserModal.spec.tsx
deleted file mode 100644
index db524dd802..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyBrowserModal.spec.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper, shallow } from 'enzyme';
-
-import { OntologyBrowserModal } from './OntologyBrowserModal';
-import { OntologyBrowserPanel } from './OntologyBrowserPanel';
-
-const DEFAULT_PROPS = {
- title: 'Test title',
- onCancel: jest.fn(),
- onApply: jest.fn(),
-};
-
-describe('OntologyBrowserModal', () => {
- function validate(wrapper: ReactWrapper): void {
- expect(wrapper.find('Modal').prop('bsSize')).toBe('lg');
- expect(wrapper.find('Modal').prop('onCancel')).toBe(DEFAULT_PROPS.onCancel);
- expect(wrapper.find('.modal-title').text()).toBe(DEFAULT_PROPS.title);
- expect(wrapper.find(OntologyBrowserPanel)).toHaveLength(1);
- expect(wrapper.find('button')).toHaveLength(3);
- }
-
- test('default props', () => {
- const wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
- });
-
- test('OntologyBrowserPanel props', () => {
- const wrapper = shallow();
- const panel = wrapper.find(OntologyBrowserPanel);
- expect(panel.prop('asPanel')).toBe(false);
- expect(panel.prop('initOntologyId')).toBe('testOntId');
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserModal.test.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserModal.test.tsx
new file mode 100644
index 0000000000..78b0037e03
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyBrowserModal.test.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { waitFor } from '@testing-library/dom';
+
+import { OntologyBrowserModal } from './OntologyBrowserModal';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchChildPaths: jest.fn().mockResolvedValue({ children: [] }),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ getOntologyDetails: jest.fn().mockResolvedValue(undefined),
+}));
+
+const DEFAULT_PROPS = {
+ title: 'Test title',
+ onCancel: jest.fn(),
+ onApply: jest.fn(),
+};
+
+describe('OntologyBrowserModal', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('default props', () => {
+ render();
+ expect(document.querySelector('.modal-title').textContent).toBe('Test title');
+ expect(document.querySelectorAll('button')).toHaveLength(3);
+ });
+
+ test('OntologyBrowserPanel props', async () => {
+ render();
+ const { getOntologyDetails } = jest.requireMock('./actions');
+ await waitFor(() => {
+ expect(getOntologyDetails).toHaveBeenCalledWith('testOntId');
+ });
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserPanel.spec.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserPanel.spec.tsx
deleted file mode 100644
index 1dfed966ca..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyBrowserPanel.spec.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-
-import { LabelHelpTip } from '../base/LabelHelpTip';
-
-import { LoadingSpinner } from '../base/LoadingSpinner';
-
-import { OntologyBrowserPanel, OntologyBrowserPanelImpl } from './OntologyBrowserPanel';
-import { OntologySelectionPanel } from './OntologySelectionPanel';
-import { OntologyTreeSearchContainer } from './OntologyTreeSearchContainer';
-import { OntologyTreePanel } from './OntologyTreePanel';
-import { ConceptInformationTabs } from './ConceptInformationTabs';
-import { ConceptModel, OntologyModel } from './models';
-
-describe('OntologyBrowserPanel', () => {
- function validate(wrapper: ReactWrapper, hasOntologyId: boolean): void {
- expect(wrapper.find(Alert)).toHaveLength(hasOntologyId ? 1 : 2);
- expect(wrapper.find(Alert).first().text()).toBe('');
- expect(wrapper.find(OntologySelectionPanel)).toHaveLength(!hasOntologyId ? 1 : 0);
- expect(wrapper.find(OntologyBrowserPanelImpl)).toHaveLength(hasOntologyId ? 1 : 0);
- }
-
- test('no initOntologyId', () => {
- const wrapper = mount();
- validate(wrapper, false);
- expect(wrapper.find(OntologySelectionPanel).prop('asPanel')).toBeTruthy();
- wrapper.unmount();
- });
-
- test('with initOntologyId', () => {
- const wrapper = mount();
- validate(wrapper, true);
- expect(wrapper.find(OntologyBrowserPanelImpl).prop('asPanel')).toBeTruthy();
- wrapper.unmount();
- });
-
- test('asPanel false', () => {
- const wrapper = mount();
- validate(wrapper, false);
- expect(wrapper.find(OntologySelectionPanel).prop('asPanel')).toBeFalsy();
- wrapper.unmount();
- });
-});
-
-const DEFAULT_PROPS = {
- ontology: undefined,
- selectedConcept: undefined,
- setSelectedConcept: jest.fn,
- setSelectedPath: jest.fn,
- asPanel: false,
-};
-
-const TEST_ONTOLOGY = new OntologyModel({
- abbreviation: 't',
- name: 'test name',
- conceptCount: 100,
- description: 'test desc',
-});
-const TEST_CONCEPT = new ConceptModel({ code: 'a', label: 'b' });
-
-describe('OntologyBrowserPanelImpl', () => {
- function validate(wrapper: ReactWrapper, loading: boolean, asPanel = false): void {
- expect(wrapper.find('.ontology-browser-container')).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find('.left-panel')).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find('.right-panel')).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find(OntologyTreeSearchContainer)).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find(OntologyTreePanel)).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find(ConceptInformationTabs)).toHaveLength(!loading ? 1 : 0);
- expect(wrapper.find('.panel-body')).toHaveLength(asPanel ? 1 : 0);
- expect(wrapper.find(LabelHelpTip)).toHaveLength(asPanel ? 1 : 0);
- }
-
- test('loading', () => {
- const wrapper = mount();
- validate(wrapper, true);
- expect(wrapper.find(LoadingSpinner)).toHaveLength(1);
- wrapper.unmount();
- });
-
- test('ontology', () => {
- const wrapper = mount();
- validate(wrapper, false);
- expect(wrapper.find(OntologyTreeSearchContainer).prop('ontology')).toBe(TEST_ONTOLOGY);
- expect(wrapper.find(OntologyTreePanel).prop('root').label).toBe(TEST_ONTOLOGY.name);
- wrapper.unmount();
- });
-
- test('selectedConcept', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, false);
- expect(wrapper.find(ConceptInformationTabs).prop('concept')).toBe(TEST_CONCEPT);
- wrapper.unmount();
- });
-
- test('asPanel', () => {
- const wrapper = mount();
- validate(wrapper, false, true);
- expect(wrapper.find('.panel-heading').text()).toContain('Browse test name (t)');
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyBrowserPanel.test.tsx b/packages/components/src/internal/components/ontology/OntologyBrowserPanel.test.tsx
new file mode 100644
index 0000000000..4adaa771b8
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyBrowserPanel.test.tsx
@@ -0,0 +1,104 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { waitFor } from '@testing-library/dom';
+
+import { OntologyBrowserPanel, OntologyBrowserPanelImpl } from './OntologyBrowserPanel';
+import { ConceptModel, OntologyModel } from './models';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchChildPaths: jest.fn().mockResolvedValue({ children: [] }),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+ getOntologyDetails: jest.fn().mockResolvedValue(undefined),
+}));
+
+jest.mock('../files/FileTree', () => ({
+ DEFAULT_ROOT_PREFIX: '|root',
+ FileTree: () => ,
+}));
+
+const TEST_ONTOLOGY = new OntologyModel({
+ abbreviation: 't',
+ name: 'test name',
+ conceptCount: 100,
+ description: 'test desc',
+});
+const TEST_CONCEPT = new ConceptModel({ code: 'a', label: 'b' });
+
+describe('OntologyBrowserPanel', () => {
+ test('no initOntologyId', async () => {
+ const { container } = render();
+ // Default asPanel=true, OntologySelectionPanel shown with asPanel=true
+ await waitFor(() => {
+ // After fetchChildPaths resolves with empty children, shows no-ontologies warning
+ expect(container.querySelector('.alert-warning')).not.toBeNull();
+ });
+ expect(container.querySelector('.ontology-browser-container')).not.toBeNull();
+ });
+
+ test('with initOntologyId', () => {
+ const { container } = render();
+ // OntologyBrowserPanelImpl renders, getOntologyDetails resolves with undefined => spinner stays
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ });
+
+ test('asPanel false', async () => {
+ const { container } = render();
+ await waitFor(() => {
+ expect(container.querySelector('.alert-warning')).not.toBeNull();
+ });
+ // OntologySelectionPanel rendered with asPanel=false => no panel container
+ expect(container.querySelector('.ontology-browser-container')).toBeNull();
+ });
+});
+
+const DEFAULT_IMPL_PROPS = {
+ ontology: undefined,
+ selectedConcept: undefined,
+ setSelectedPath: jest.fn(),
+ asPanel: false,
+};
+
+describe('OntologyBrowserPanelImpl', () => {
+ test('loading', () => {
+ const { container } = render();
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ expect(container.querySelector('.ontology-browser-container')).toBeNull();
+ });
+
+ test('ontology', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.ontology-browser-container')).not.toBeNull();
+ expect(container.querySelector('.left-panel')).not.toBeNull();
+ expect(container.querySelector('.right-panel')).not.toBeNull();
+ expect(container.querySelector('.concept-search-container')).not.toBeNull();
+ expect(container.querySelector('.mock-file-tree')).not.toBeNull();
+ expect(container.querySelector('.panel-body')).toBeNull();
+ });
+
+ test('selectedConcept', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.ontology-browser-container')).not.toBeNull();
+ expect(container.querySelector('.left-panel')).not.toBeNull();
+ expect(container.querySelector('.right-panel')).not.toBeNull();
+ });
+
+ test('asPanel', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.ontology-browser-container')).not.toBeNull();
+ expect(container.querySelector('.panel-body')).not.toBeNull();
+ expect(container.querySelector('.panel-heading').textContent).toContain('Browse test name (t)');
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.spec.tsx b/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.spec.tsx
deleted file mode 100644
index 1c3e9a935f..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.spec.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
-
-import { DomainField } from '../domainproperties/models';
-
-import { DomainFieldLabel } from '../domainproperties/DomainFieldLabel';
-
-import { OntologyConceptAnnotation } from './OntologyConceptAnnotation';
-import { OntologyBrowserModal } from './OntologyBrowserModal';
-
-const DEFAULT_PROPS = {
- id: 'testId',
- field: new DomainField(),
- onChange: jest.fn,
- error: undefined,
-};
-
-const TEST_FIELD = new DomainField({ principalConceptCode: 'code:123' });
-
-describe('OntologyConceptAnnotation', () => {
- function validate(wrapper: ReactWrapper, hasCode: boolean, canRemove = true): void {
- expect(wrapper.find(DomainFieldLabel)).toHaveLength(1);
- expect(wrapper.find('.domain-annotation-table')).toHaveLength(1);
- expect(wrapper.find('.domain-validation-button')).toHaveLength(1);
- expect(getSelectButton(wrapper).text()).toBe('Select Concept');
- expect(wrapper.find('.domain-text-label')).toHaveLength(!hasCode || !canRemove ? 1 : 0);
- if (!hasCode) {
- expect(wrapper.find('.domain-text-label').text()).toBe('None Set');
- }
-
- expect(wrapper.find('.content')).toHaveLength(hasCode && canRemove ? 3 : 2);
- expect(wrapper.find('.domain-annotation-item')).toHaveLength(hasCode ? 1 : 0);
- }
-
- function getSelectButton(wrapper: ReactWrapper): ReactWrapper {
- return wrapper.find('.domain-validation-button').first();
- }
-
- test('no principalConceptCode', () => {
- const wrapper = mount();
- validate(wrapper, false);
- expect(wrapper.find('.domain-text-label').text()).toBe('None Set');
- wrapper.unmount();
- });
-
- test('principalConceptCode', () => {
- const wrapper = mount();
- validate(wrapper, true);
- expect(wrapper.find('.domain-annotation-item').text()).toBe(TEST_FIELD.principalConceptCode);
- expect(wrapper.find('.fa-remove')).toHaveLength(1);
- expect(getSelectButton(wrapper).prop('disabled')).toBeFalsy();
- wrapper.unmount();
- });
-
- test('isFieldLocked and select button props', () => {
- const field = TEST_FIELD.merge({ lockType: DOMAIN_FIELD_FULLY_LOCKED }) as DomainField;
- const wrapper = mount();
- validate(wrapper, true, false);
- expect(wrapper.find('.fa-remove')).toHaveLength(0);
- expect(getSelectButton(wrapper).prop('disabled')).toBeTruthy();
- expect(getSelectButton(wrapper).prop('id')).toBe(DEFAULT_PROPS.id);
- expect(getSelectButton(wrapper).prop('name')).toBe('domainpropertiesrow-principalConceptCode');
- wrapper.unmount();
- });
-
- test('showSelectModal', () => {
- const wrapper = mount();
- validate(wrapper, true);
- expect(wrapper.find(OntologyBrowserModal)).toHaveLength(0);
- getSelectButton(wrapper).simulate('click');
- expect(wrapper.find(OntologyBrowserModal)).toHaveLength(1);
- expect(wrapper.find(OntologyBrowserModal).prop('title')).toBe('Select Concept');
- expect(wrapper.find(OntologyBrowserModal).prop('initOntologyId')).toBe(TEST_FIELD.sourceOntology);
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.test.tsx b/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.test.tsx
new file mode 100644
index 0000000000..73a113ccab
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyConceptAnnotation.test.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+import { waitFor } from '@testing-library/dom';
+
+import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
+import { DomainField } from '../domainproperties/models';
+
+import { OntologyConceptAnnotation } from './OntologyConceptAnnotation';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ fetchPathModel: jest.fn().mockResolvedValue(undefined),
+ getOntologyDetails: jest.fn().mockResolvedValue(undefined),
+}));
+
+const DEFAULT_PROPS = {
+ id: 'testId',
+ field: new DomainField(),
+ onChange: jest.fn(),
+};
+
+const TEST_FIELD = new DomainField({ principalConceptCode: 'code:123' });
+
+describe('OntologyConceptAnnotation', () => {
+ test('no principalConceptCode', () => {
+ const { container } = render();
+ expect(container.querySelector('.domain-annotation-table')).not.toBeNull();
+ expect(container.querySelector('.domain-validation-button').textContent).toBe('Select Concept');
+ expect(container.querySelector('.domain-text-label').textContent).toBe('None Set');
+ expect(container.querySelector('.domain-annotation-item')).toBeNull();
+ });
+
+ test('principalConceptCode', async () => {
+ const { container } = render();
+ await waitFor(() => {
+ expect(container.querySelector('.domain-annotation-item').textContent).toBe(
+ TEST_FIELD.principalConceptCode
+ );
+ });
+ expect(container.querySelector('.domain-text-label')).toBeNull();
+ expect(container.querySelector('.fa-remove')).not.toBeNull();
+ expect((container.querySelector('.domain-validation-button') as HTMLButtonElement).disabled).toBe(false);
+ });
+
+ test('isFieldLocked', async () => {
+ const field = TEST_FIELD.merge({ lockType: DOMAIN_FIELD_FULLY_LOCKED }) as DomainField;
+ const { container } = render();
+ await waitFor(() => {
+ expect(container.querySelector('.domain-annotation-item.domain-text-label')).not.toBeNull();
+ });
+ const button = container.querySelector('.domain-validation-button') as HTMLButtonElement;
+ expect(button.disabled).toBe(true);
+ expect(button.id).toBe(DEFAULT_PROPS.id);
+ expect(button.name).toBe('domainpropertiesrow-principalConceptCode');
+ expect(container.querySelector('.fa-remove')).toBeNull();
+ });
+
+ test('showSelectModal', async () => {
+ const { container } = render();
+ expect(document.querySelector('.modal-title')).toBeNull();
+ fireEvent.click(container.querySelector('.domain-validation-button'));
+ await waitFor(() => {
+ expect(document.querySelector('.modal-title').textContent).toBe('Select Concept');
+ });
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.spec.tsx b/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.spec.tsx
deleted file mode 100644
index 67be8d71e0..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.spec.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { DomainField } from '../domainproperties/models';
-
-import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
-
-import { OntologyConceptSelectButton } from './OntologyConceptSelectButton';
-import { ConceptOverviewTooltip } from './ConceptOverviewPanel';
-import { OntologyBrowserModal } from './OntologyBrowserModal';
-
-const DEFAULT_PROPS = {
- id: 'test-id',
- title: 'Button Title',
- field: new DomainField({}),
- valueProp: 'principalConceptCode',
- valueIsPath: false,
- onChange: jest.fn,
-};
-
-describe('OntologyConceptSelectButton', () => {
- function validate(wrapper: ReactWrapper, value = 'None Set', isFieldLocked = false, showModal = false): void {
- const hasValue = value !== 'None Set';
-
- expect(wrapper.find('.domain-annotation-table')).toHaveLength(1);
- expect(wrapper.find(ConceptOverviewTooltip)).toHaveLength(1);
- expect(wrapper.find(OntologyBrowserModal)).toHaveLength(showModal ? 1 : 0);
-
- expect(wrapper.find('button')).toHaveLength(showModal ? 4 : 1);
- expect(wrapper.find('button').first().prop('disabled')).toBe(isFieldLocked);
- expect(wrapper.find('button').first().text()).toBe('Button Title');
-
- expect(wrapper.find('.fa-remove')).toHaveLength(hasValue && !isFieldLocked ? 1 : 0);
- expect(wrapper.find('.domain-text-label')).toHaveLength(!hasValue || isFieldLocked ? 1 : 0);
- expect(wrapper.find('.domain-annotation-item')).toHaveLength(hasValue ? 1 : 0);
- if (hasValue) {
- expect(wrapper.find('.domain-annotation-item').text()).toBe(value);
- const itemOnClick = wrapper.find('.domain-annotation-item').prop('onClick');
- if (!isFieldLocked) expect(itemOnClick).toBeDefined();
- if (isFieldLocked) expect(itemOnClick).toBeUndefined();
- } else {
- expect(wrapper.find('.domain-text-label').text()).toBe(value);
- }
- }
-
- test('no value set', () => {
- const wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
- });
-
- test('showSelectModal', () => {
- const wrapper = mount();
- validate(wrapper);
- wrapper.find('button').simulate('click');
- validate(wrapper, 'None Set', false, true);
- wrapper.unmount();
- });
-
- test('with value set', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, 'TEST VALUE');
- wrapper.unmount();
- });
-
- test('isFieldLocked', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, 'TEST VALUE', true);
- wrapper.unmount();
- });
-
- test('OntologyBrowserModal props', () => {
- const wrapper = mount(
-
- );
- wrapper.find('button').simulate('click');
- validate(wrapper, 'None Set', false, true);
- const modal = wrapper.find(OntologyBrowserModal);
- expect(modal.prop('title')).toBe('Button Title');
- expect(modal.prop('initOntologyId')).toBe(undefined);
- wrapper.unmount();
- });
-
- test('useFieldSourceOntology', () => {
- const wrapper = mount(
-
- );
- wrapper.find('button').simulate('click');
- validate(wrapper, 'None Set', false, true);
- const modal = wrapper.find(OntologyBrowserModal);
- expect(modal.prop('initOntologyId')).toBe('Test Source');
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.test.tsx b/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.test.tsx
new file mode 100644
index 0000000000..ce073f711b
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyConceptSelectButton.test.tsx
@@ -0,0 +1,111 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+import { waitFor } from '@testing-library/dom';
+
+import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
+import { DomainField } from '../domainproperties/models';
+
+import { OntologyConceptSelectButton } from './OntologyConceptSelectButton';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchAlternatePaths: jest.fn().mockResolvedValue([]),
+ fetchChildPaths: jest.fn().mockResolvedValue(undefined),
+ fetchConceptForCode: jest.fn().mockResolvedValue(undefined),
+ fetchPathModel: jest.fn().mockResolvedValue(undefined),
+ getOntologyDetails: jest.fn().mockResolvedValue(undefined),
+}));
+
+const DEFAULT_PROPS = {
+ id: 'test-id',
+ title: 'Button Title',
+ field: new DomainField({}),
+ valueProp: 'principalConceptCode',
+ valueIsPath: false,
+ onChange: jest.fn(),
+};
+
+describe('OntologyConceptSelectButton', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('no value set', () => {
+ const { container } = render();
+ expect(container.querySelector('.domain-annotation-table')).not.toBeNull();
+ const button = container.querySelector('button') as HTMLButtonElement;
+ expect(button.textContent).toBe('Button Title');
+ expect(button.disabled).toBe(false);
+ expect(container.querySelector('.fa-remove')).toBeNull();
+ expect(container.querySelector('.domain-text-label').textContent).toBe('None Set');
+ expect(container.querySelector('.domain-annotation-item')).toBeNull();
+ });
+
+ test('showSelectModal', async () => {
+ const { container } = render();
+ expect(document.querySelector('.modal-title')).toBeNull();
+ fireEvent.click(container.querySelector('button'));
+ await waitFor(() => {
+ expect(document.querySelector('.modal-title').textContent).toBe('Button Title');
+ });
+ });
+
+ test('with value set', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.domain-annotation-item').textContent).toBe('TEST VALUE');
+ });
+ expect(container.querySelector('.domain-text-label')).toBeNull();
+ expect(container.querySelector('.fa-remove')).not.toBeNull();
+ });
+
+ test('isFieldLocked', async () => {
+ const { container } = render(
+
+ );
+ await waitFor(() => {
+ expect(container.querySelector('.domain-annotation-item.domain-text-label')).not.toBeNull();
+ });
+ expect((container.querySelector('button') as HTMLButtonElement).disabled).toBe(true);
+ expect(container.querySelector('.domain-annotation-item').textContent).toBe('TEST VALUE');
+ expect(container.querySelector('.fa-remove')).toBeNull();
+ });
+
+ test('OntologyBrowserModal props', async () => {
+ const { container } = render(
+
+ );
+ fireEvent.click(container.querySelector('button'));
+ await waitFor(() => {
+ expect(document.querySelector('.modal-title')).not.toBeNull();
+ });
+ const { getOntologyDetails } = jest.requireMock('./actions');
+ expect(getOntologyDetails).not.toHaveBeenCalled();
+ });
+
+ test('useFieldSourceOntology', async () => {
+ const { container } = render(
+
+ );
+ fireEvent.click(container.querySelector('button'));
+ const { getOntologyDetails } = jest.requireMock('./actions');
+ await waitFor(() => {
+ expect(getOntologyDetails).toHaveBeenCalledWith('Test Source');
+ });
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyLookupOptions.spec.tsx b/packages/components/src/internal/components/ontology/OntologyLookupOptions.spec.tsx
deleted file mode 100644
index aeb3fadf52..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyLookupOptions.spec.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-import { List } from 'immutable';
-
-import { SectionHeading } from '../domainproperties/SectionHeading';
-import { INTEGER_TYPE, ONTOLOGY_LOOKUP_TYPE, TEXT_TYPE } from '../domainproperties/PropDescType';
-import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
-
-import { DomainField } from '../domainproperties/models';
-
-import { getDomainPropertiesTestAPIWrapper } from '../domainproperties/APIWrapper';
-
-import { waitForLifecycle } from '../../test/enzymeTestHelpers';
-
-import { OntologyLookupOptions } from './OntologyLookupOptions';
-import { OntologyConceptSelectButton } from './OntologyConceptSelectButton';
-import { OntologyModel } from './models';
-
-const field1 = DomainField.create({
- name: 'field1',
- conceptURI: ONTOLOGY_LOOKUP_TYPE.conceptURI,
- rangeURI: ONTOLOGY_LOOKUP_TYPE.rangeURI,
- sourceOntology: 'NCIT',
- conceptImportColumn: 'field2',
- conceptLabelColumn: 'field3',
-});
-const field2 = DomainField.create({
- name: 'field2', // conceptImportColumn
- rangeURI: TEXT_TYPE.rangeURI,
-});
-const field3 = DomainField.create({
- name: 'field3', // conceptLabelColumn
- rangeURI: TEXT_TYPE.rangeURI,
-});
-const field4 = DomainField.create({
- name: 'field4', // other text field that should show as option
- rangeURI: TEXT_TYPE.rangeURI,
-});
-const field5 = DomainField.create({
- name: 'field5', // int field should not show as option
- rangeURI: INTEGER_TYPE.rangeURI,
-});
-const field6 = DomainField.create({
- name: '', // invalid name field should not show as option
- rangeURI: TEXT_TYPE.rangeURI,
-});
-
-describe('OntologyLookupOptions', () => {
- function getDefaultProps() {
- return {
- domainContainerPath: '/Where/The/Domain/Lives',
- index: 0,
- domainIndex: 0,
- label: 'Test',
- lockType: undefined,
- onChange: jest.fn(),
- onMultiChange: jest.fn(),
- api: getDomainPropertiesTestAPIWrapper(jest.fn, {
- fetchOntologies: jest.fn().mockResolvedValue([
- new OntologyModel({
- rowId: 2,
- name: "Test HOM-UCARE-->\">'>'\"",
- abbreviation: '45887',
- }),
- new OntologyModel({
- rowId: 1,
- name: 'Test National Cancer Institute Thesaurus',
- abbreviation: 'NCIT',
- }),
- ]),
- }),
- };
- }
-
- function validate(
- wrapper: ReactWrapper,
- disabled: boolean,
- selectedSource: string,
- importOptions: string[],
- labelOptions: string[]
- ): void {
- expect(wrapper.find(SectionHeading)).toHaveLength(1);
- expect(wrapper.find('.domain-field-label')).toHaveLength(4);
-
- const selectInputs = wrapper.find('select');
- expect(selectInputs).toHaveLength(3);
-
- // source ontology select
- let selectInput = selectInputs.at(0);
- expect(selectInput.prop('disabled')).toBe(disabled);
- expect(selectInput.prop('value')).toBe(selectedSource);
- let options = selectInput.children();
- expect(options).toHaveLength(2);
- expect(options.find({ value: '45887' })).toHaveLength(1);
- expect(options.find({ value: 'NCIT' })).toHaveLength(1);
-
- // import field select
- selectInput = selectInputs.at(1);
- expect(selectInput.prop('disabled')).toBe(disabled);
- options = selectInput.children();
- expect(options).toHaveLength(importOptions.length);
- importOptions.forEach(value => {
- expect(options.find({ value })).toHaveLength(1);
- });
-
- // label field select
- selectInput = selectInputs.at(2);
- expect(selectInput.prop('disabled')).toBe(disabled);
- options = selectInput.children();
- expect(options).toHaveLength(labelOptions.length);
- labelOptions.forEach(value => {
- expect(options.find({ value })).toHaveLength(1);
- });
-
- const conceptSelectBtn = wrapper.find(OntologyConceptSelectButton);
- expect(conceptSelectBtn.prop('valueProp')).toBe('conceptSubtree');
- expect(conceptSelectBtn.prop('valueIsPath')).toBe(true);
- expect(conceptSelectBtn.prop('useFieldSourceOntology')).toBe(true);
- }
-
- test('default props', async () => {
- const field = DomainField.create({});
- const domainFields = List.of(field);
-
- const wrapper = mount(
-
- );
- await waitForLifecycle(wrapper);
-
- validate(wrapper, false, undefined, [null], [null]);
- wrapper.unmount();
- });
-
- test('with additional fields and ontology field props', async () => {
- const domainFields = List.of(field1, field2, field3, field4, field5, field6);
- const wrapper = mount(
-
- );
- await waitForLifecycle(wrapper);
-
- validate(wrapper, false, 'NCIT', [null, 'field2', 'field4'], [null, 'field3', 'field4']);
- wrapper.unmount();
- });
-
- test('disabled selects', async () => {
- const domainFields = List.of(field1, field2, field3, field4, field5, field6);
- const wrapper = mount(
-
- );
- await waitForLifecycle(wrapper);
-
- validate(wrapper, true, 'NCIT', [null, 'field2', 'field4'], [null, 'field3', 'field4']);
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyLookupOptions.test.tsx b/packages/components/src/internal/components/ontology/OntologyLookupOptions.test.tsx
new file mode 100644
index 0000000000..6854821f79
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyLookupOptions.test.tsx
@@ -0,0 +1,127 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { waitFor } from '@testing-library/dom';
+import { List } from 'immutable';
+
+import { INTEGER_TYPE, ONTOLOGY_LOOKUP_TYPE, TEXT_TYPE } from '../domainproperties/PropDescType';
+import { DOMAIN_FIELD_FULLY_LOCKED } from '../domainproperties/constants';
+import { DomainField } from '../domainproperties/models';
+import { getDomainPropertiesTestAPIWrapper } from '../domainproperties/APIWrapper';
+
+import { OntologyLookupOptions } from './OntologyLookupOptions';
+import { OntologyModel } from './models';
+
+const field1 = DomainField.create({
+ name: 'field1',
+ conceptURI: ONTOLOGY_LOOKUP_TYPE.conceptURI,
+ rangeURI: ONTOLOGY_LOOKUP_TYPE.rangeURI,
+ sourceOntology: 'NCIT',
+ conceptImportColumn: 'field2',
+ conceptLabelColumn: 'field3',
+});
+const field2 = DomainField.create({ name: 'field2', rangeURI: TEXT_TYPE.rangeURI });
+const field3 = DomainField.create({ name: 'field3', rangeURI: TEXT_TYPE.rangeURI });
+const field4 = DomainField.create({ name: 'field4', rangeURI: TEXT_TYPE.rangeURI });
+const field5 = DomainField.create({ name: 'field5', rangeURI: INTEGER_TYPE.rangeURI });
+const field6 = DomainField.create({ name: '', rangeURI: TEXT_TYPE.rangeURI });
+
+function getDefaultProps() {
+ return {
+ domainContainerPath: '/Where/The/Domain/Lives',
+ index: 0,
+ domainIndex: 0,
+ label: 'Test',
+ lockType: undefined,
+ onChange: jest.fn(),
+ onMultiChange: jest.fn(),
+ api: getDomainPropertiesTestAPIWrapper(jest.fn, {
+ fetchOntologies: jest.fn().mockResolvedValue([
+ new OntologyModel({
+ rowId: 2,
+ name: "Test HOM-UCARE-->\">'>'\"",
+ abbreviation: '45887',
+ }),
+ new OntologyModel({
+ rowId: 1,
+ name: 'Test National Cancer Institute Thesaurus',
+ abbreviation: 'NCIT',
+ }),
+ ]),
+ }),
+ };
+}
+
+async function validateSelects(
+ container: HTMLElement,
+ disabled: boolean,
+ importOptionCount: number,
+ labelOptionCount: number,
+ importOptionValues: string[],
+ labelOptionValues: string[]
+): Promise {
+ await waitFor(() => {
+ const selects = container.querySelectorAll('select');
+ expect(selects).toHaveLength(3);
+ // Source ontology select should have 2 options (the 2 mocked ontologies)
+ expect(selects[0].querySelectorAll('option')).toHaveLength(2);
+ });
+
+ const selects = container.querySelectorAll('select');
+
+ // source ontology select
+ expect((selects[0] as HTMLSelectElement).disabled).toBe(disabled);
+ expect(selects[0].querySelector('option[value="45887"]')).not.toBeNull();
+ expect(selects[0].querySelector('option[value="NCIT"]')).not.toBeNull();
+
+ // import field select
+ expect((selects[1] as HTMLSelectElement).disabled).toBe(disabled);
+ expect(selects[1].querySelectorAll('option')).toHaveLength(importOptionCount);
+ importOptionValues.forEach(value => {
+ if (value !== null) {
+ expect(selects[1].querySelector(`option[value="${value}"]`)).not.toBeNull();
+ }
+ });
+
+ // label field select
+ expect((selects[2] as HTMLSelectElement).disabled).toBe(disabled);
+ expect(selects[2].querySelectorAll('option')).toHaveLength(labelOptionCount);
+ labelOptionValues.forEach(value => {
+ if (value !== null) {
+ expect(selects[2].querySelector(`option[value="${value}"]`)).not.toBeNull();
+ }
+ });
+}
+
+describe('OntologyLookupOptions', () => {
+ test('default props', async () => {
+ const field = DomainField.create({});
+ const domainFields = List.of(field);
+ const { container } = render(
+
+ );
+ await validateSelects(container, false, 1, 1, [null], [null]);
+ expect(container.querySelector('.domain-field-section-heading')).not.toBeNull();
+ expect(container.querySelectorAll('.domain-field-label')).toHaveLength(4);
+ });
+
+ test('with additional fields and ontology field props', async () => {
+ const domainFields = List.of(field1, field2, field3, field4, field5, field6);
+ const { container } = render(
+
+ );
+ await validateSelects(container, false, 3, 3, [null, 'field2', 'field4'], [null, 'field3', 'field4']);
+ });
+
+ test('disabled selects', async () => {
+ const domainFields = List.of(field1, field2, field3, field4, field5, field6);
+ const { container } = render(
+
+ );
+ await validateSelects(container, true, 3, 3, [null, 'field2', 'field4'], [null, 'field3', 'field4']);
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologySelectionPanel.spec.tsx b/packages/components/src/internal/components/ontology/OntologySelectionPanel.spec.tsx
deleted file mode 100644
index 56c1c0b7e6..0000000000
--- a/packages/components/src/internal/components/ontology/OntologySelectionPanel.spec.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-import { LoadingSpinner } from '../base/LoadingSpinner';
-import { SelectInput } from '../forms/input/SelectInput';
-
-import { PathModel } from './models';
-import { OntologySelectionPanelImpl } from './OntologySelectionPanel';
-
-const DEFAULT_PROPS = {
- error: undefined,
- ontologies: undefined,
- asPanel: false,
- onOntologySelection: jest.fn,
-};
-
-describe('OntologySelectionPanel', () => {
- function validate(wrapper: ReactWrapper, ontCount?: number, errorTxt?: string, asPanel = false): void {
- expect(wrapper.find(Alert).first().text()).toBe(errorTxt ?? '');
- expect(wrapper.find(LoadingSpinner)).toHaveLength(ontCount === undefined ? 1 : 0);
- expect(wrapper.find('.alert-warning')).toHaveLength(ontCount !== undefined && ontCount === 0 ? 1 : 0);
- expect(wrapper.find(SelectInput)).toHaveLength(ontCount !== undefined ? 1 : 0);
- expect(wrapper.find('.ontology-browser-container')).toHaveLength(asPanel ? 1 : 0);
- }
-
- test('loading', () => {
- const wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
- });
-
- test('no ontologies, non root admin', () => {
- const ontologies = [];
- const wrapper = mount();
- validate(wrapper, 0);
- expect(wrapper.find('.alert-warning').text()).toBe('No ontologies have been loaded for this server.');
- expect(wrapper.find(SelectInput).prop('options')).toBe(ontologies);
- wrapper.unmount();
- });
-
- test('no ontologies, as root admin', () => {
- LABKEY.user.isRootAdmin = true;
- const ontologies = [];
- const wrapper = mount();
- validate(wrapper, 0);
- expect(wrapper.find('.alert-warning').text()).toContain('Click here to get started.');
- wrapper.unmount();
- });
-
- test('with ontologies', () => {
- const ontologies = [new PathModel()];
- const wrapper = mount();
- validate(wrapper, ontologies.length);
- expect(wrapper.find(SelectInput).prop('options')).toBe(ontologies);
- wrapper.unmount();
- });
-
- test('asPanel', () => {
- const wrapper = mount();
- validate(wrapper, 0, undefined, true);
- expect(wrapper.find('.panel-body')).toHaveLength(1);
- wrapper.unmount();
- });
-
- test('error', () => {
- const wrapper = mount();
- validate(wrapper, 0, 'test error');
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologySelectionPanel.test.tsx b/packages/components/src/internal/components/ontology/OntologySelectionPanel.test.tsx
new file mode 100644
index 0000000000..5ab4b4b3a3
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologySelectionPanel.test.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import { PathModel } from './models';
+import { OntologySelectionPanelImpl } from './OntologySelectionPanel';
+
+const DEFAULT_PROPS = {
+ error: undefined,
+ ontologies: undefined,
+ asPanel: false,
+ onOntologySelection: jest.fn(),
+};
+
+describe('OntologySelectionPanel', () => {
+ afterEach(() => {
+ LABKEY.user.isRootAdmin = false;
+ });
+
+ test('loading', () => {
+ const { container } = render();
+ expect(container.querySelector('.fa-spinner')).not.toBeNull();
+ expect(container.querySelector('.alert-warning')).toBeNull();
+ expect(container.querySelector('[role="alert"]')).toBeNull();
+ expect(container.querySelector('.ontology-browser-container')).toBeNull();
+ });
+
+ test('no ontologies, non root admin', () => {
+ const { container } = render();
+ expect(container.querySelector('.fa-spinner')).toBeNull();
+ expect(container.querySelector('.alert-warning')).not.toBeNull();
+ expect(container.querySelector('.alert-warning').textContent).toBe(
+ 'No ontologies have been loaded for this server.'
+ );
+ });
+
+ test('no ontologies, as root admin', () => {
+ LABKEY.user.isRootAdmin = true;
+ const { container } = render();
+ expect(container.querySelector('.alert-warning')).not.toBeNull();
+ expect(container.querySelector('.alert-warning').textContent).toContain('Click here to get started.');
+ });
+
+ test('with ontologies', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.fa-spinner')).toBeNull();
+ expect(container.querySelector('.alert-warning')).toBeNull();
+ });
+
+ test('asPanel', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.ontology-browser-container')).not.toBeNull();
+ expect(container.querySelector('.panel-body')).not.toBeNull();
+ });
+
+ test('error', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('[role="alert"]')).not.toBeNull();
+ expect(container.querySelector('[role="alert"]').textContent).toBe('test error');
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyTreePanel.spec.tsx b/packages/components/src/internal/components/ontology/OntologyTreePanel.spec.tsx
deleted file mode 100644
index a1d62f9c75..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyTreePanel.spec.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-
-import { FileTree } from '../files/FileTree';
-
-import { FilterIcon, OntologyTreePanel } from './OntologyTreePanel';
-import { PathModel } from './models';
-
-const DEFAULT_PROPS = {
- root: new PathModel({ label: 'test label' }),
- onNodeSelection: jest.fn,
-};
-
-describe('OntologyTreePanel', () => {
- test('default props', () => {
- const wrapper = mount();
- const fileTree = wrapper.find(FileTree);
- expect(fileTree.prop('allowMultiSelect')).toBe(false);
- expect(fileTree.prop('showNodeIcon')).toBe(false);
- expect(fileTree.prop('defaultRootName')).toBe(DEFAULT_PROPS.root.label);
- expect(fileTree.prop('showLoading')).toBe(false);
- expect(fileTree.prop('showAnimations')).toBe(false);
- wrapper.unmount();
- });
-});
-
-const DEFAULT_FILTER_ICON_PROPS = {
- node: undefined,
- onClick: undefined,
- filters: undefined,
-};
-
-describe('FilterIcon', () => {
- test('default props', () => {
- const wrapper = mount();
- const icon = wrapper.find('i');
- expect(icon.prop('className')).toBe('fa fa-filter');
- wrapper.unmount();
- });
-
- test('node selected', () => {
- const testnode = { data: { code: 'test' } };
- const testFilters = new Map().set('test', new PathModel());
-
- const wrapper = mount();
- const icon = wrapper.find('i');
- expect(icon.prop('className')).toBe('fa fa-filter selected');
- expect(icon.prop('title')).toBe('Remove filter');
- wrapper.unmount();
- });
-
- test('node not selected', () => {
- const testnode = { data: { code: 'test' } };
- const testFilters = new Map().set('nope', new PathModel());
-
- const wrapper = mount();
- const icon = wrapper.find('i');
- expect(icon.prop('className')).toBe('fa fa-filter');
- expect(icon.prop('title')).toBe('Add filter');
- wrapper.unmount();
- });
-
- test('clicked', () => {
- const testdata = { code: 'test' };
- const testnode = { data: testdata };
- const onClickHandler = jest.fn();
-
- const wrapper = mount();
- const icon = wrapper.find('i');
- expect(icon.prop('className')).toBe('fa fa-filter');
- wrapper.simulate('click');
- expect(onClickHandler).toBeCalledTimes(1);
- expect(onClickHandler).toHaveBeenCalledWith(testdata);
-
- wrapper.unmount();
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyTreePanel.test.tsx b/packages/components/src/internal/components/ontology/OntologyTreePanel.test.tsx
new file mode 100644
index 0000000000..73a0b324d2
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyTreePanel.test.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+
+import { FilterIcon, OntologyTreePanel } from './OntologyTreePanel';
+import { PathModel } from './models';
+
+jest.mock('./actions', () => ({
+ ...jest.requireActual('./actions'),
+ fetchChildPaths: jest.fn().mockResolvedValue({ children: [] }),
+ fetchParentPaths: jest.fn().mockResolvedValue([]),
+}));
+
+jest.mock('../files/FileTree', () => ({
+ DEFAULT_ROOT_PREFIX: '|root',
+ FileTree: () => ,
+}));
+
+const DEFAULT_PROPS = {
+ root: new PathModel({ label: 'test label' }),
+ onNodeSelection: jest.fn(),
+};
+
+describe('OntologyTreePanel', () => {
+ test('default props', () => {
+ const { container } = render();
+ expect(container.querySelector('.mock-file-tree')).not.toBeNull();
+ });
+});
+
+const DEFAULT_FILTER_ICON_PROPS = {
+ node: undefined,
+ onClick: undefined,
+ filters: undefined,
+};
+
+describe('FilterIcon', () => {
+ test('default props', () => {
+ const { container } = render();
+ const icon = container.querySelector('i');
+ expect(icon.className).toBe('fa fa-filter');
+ });
+
+ test('node selected', () => {
+ const testnode = { data: { code: 'test' } };
+ const testFilters = new Map().set('test', new PathModel());
+ const { container } = render();
+ const icon = container.querySelector('i');
+ expect(icon.className).toBe('fa fa-filter selected');
+ expect(icon.title).toBe('Remove filter');
+ });
+
+ test('node not selected', () => {
+ const testnode = { data: { code: 'test' } };
+ const testFilters = new Map().set('nope', new PathModel());
+ const { container } = render();
+ const icon = container.querySelector('i');
+ expect(icon.className).toBe('fa fa-filter');
+ expect(icon.title).toBe('Add filter');
+ });
+
+ test('clicked', () => {
+ const testdata = { code: 'test' };
+ const testnode = { data: testdata };
+ const onClickHandler = jest.fn();
+ const { container } = render();
+ const icon = container.querySelector('i');
+ expect(icon.className).toBe('fa fa-filter');
+ fireEvent.click(icon);
+ expect(onClickHandler).toHaveBeenCalledTimes(1);
+ expect(onClickHandler).toHaveBeenCalledWith(testdata);
+ });
+});
diff --git a/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.spec.tsx b/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.spec.tsx
deleted file mode 100644
index eb33c250f7..0000000000
--- a/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.spec.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import React from 'react';
-import { mount, ReactWrapper } from 'enzyme';
-
-import { Alert } from '../base/Alert';
-
-import {
- getOntologySearchTerm,
- OntologySearchResultsMenu,
- OntologyTreeSearchContainer,
-} from './OntologyTreeSearchContainer';
-import { ConceptModel, OntologyModel } from './models';
-
-const TEST_ONTOLOGY = new OntologyModel({
- abbreviation: 't',
- name: 'test name',
- conceptCount: 100,
- description: 'test desc',
-});
-
-const TEST_SEARCH_HITS = [
- new ConceptModel({
- code: 'a',
- label: 'A',
- description: 'Description for a',
- }),
- new ConceptModel({
- code: 'b',
- label: 'B',
- description: 'Description for b',
- }),
-];
-
-describe('OntologyTreeSearchContainer', () => {
- test('default props', async () => {
- const wrapper = mount(
-
- );
- expect(wrapper.find('.concept-search-container')).toHaveLength(1);
- expect(wrapper.find('input')).toHaveLength(1);
- expect(wrapper.find('input').prop('placeholder')).toBe('Search t');
- expect(wrapper.find(OntologySearchResultsMenu)).toHaveLength(0); // No initial search term, so no result element expected
-
- wrapper.unmount();
- });
-});
-
-const DEFAULT_PROPS = {
- searchHits: undefined,
- totalHits: undefined,
- isFocused: true,
- error: undefined,
- onItemClick: jest.fn,
-};
-
-describe('OntologySearchResultsMenu', () => {
- function validate(wrapper: ReactWrapper, showMenu = false, itemCount = 0, showFooter = false): void {
- expect(wrapper.find('ul.result-menu')).toHaveLength(showMenu ? 1 : 0);
- expect(wrapper.find(Alert)).toHaveLength(showMenu ? 1 : 0);
- expect(wrapper.find('li')).toHaveLength(itemCount);
- expect(wrapper.find('.result-footer')).toHaveLength(showFooter ? 1 : 0);
- }
-
- test('showMenu', () => {
- let wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
-
- wrapper = mount();
- validate(wrapper);
- wrapper.unmount();
-
- wrapper = mount();
- validate(wrapper, true, 1);
- wrapper.unmount();
-
- wrapper = mount();
- validate(wrapper, true);
- wrapper.unmount();
- });
-
- test('error', () => {
- const wrapper = mount();
- validate(wrapper, true);
- expect(wrapper.find(Alert).text()).toBe('test error');
- wrapper.unmount();
- });
-
- test('no search results found', () => {
- const wrapper = mount();
- validate(wrapper, true, 1);
- expect(wrapper.find('li').text()).toBe('No search results found.');
- wrapper.unmount();
- });
-
- test('totalHits footer', () => {
- const wrapper = mount();
- validate(wrapper, true, 1, true);
- expect(wrapper.find('.result-footer').text()).toContain('2 results found.');
- wrapper.unmount();
- });
-
- test('with searchHits, with descriptions', () => {
- const wrapper = mount(
-
- );
- validate(wrapper, true, TEST_SEARCH_HITS.length);
- expect(wrapper.find('.selectable-item')).toHaveLength(TEST_SEARCH_HITS.length);
- expect(wrapper.find('.bold')).toHaveLength(TEST_SEARCH_HITS.length);
- expect(wrapper.find('.bold').at(0).text()).toBe(TEST_SEARCH_HITS[0].label);
- expect(wrapper.find('.bold').at(1).text()).toBe(TEST_SEARCH_HITS[1].label);
- expect(wrapper.find('.col-xs-2').at(0).text()).toBe(TEST_SEARCH_HITS[0].code);
- expect(wrapper.find('.col-xs-2').at(1).text()).toBe(TEST_SEARCH_HITS[1].code);
- expect(wrapper.find('.col-xs-10')).toHaveLength(0);
- expect(wrapper.find('.col-xs-5')).toHaveLength(TEST_SEARCH_HITS.length * 2);
- expect(wrapper.find('.col-xs-5').last().text()).toBe(TEST_SEARCH_HITS[1].description);
- wrapper.unmount();
- });
-
- test('with searchHits, without descriptions', () => {
- const searchHits = [new ConceptModel({ code: 'a', label: 'A' })];
-
- const wrapper = mount(
-
- );
- validate(wrapper, true, searchHits.length);
- expect(wrapper.find('.col-xs-5')).toHaveLength(0);
- expect(wrapper.find('.col-xs-10')).toHaveLength(searchHits.length);
- expect(wrapper.find('.col-xs-10').text()).toBe(searchHits[0].label);
- wrapper.unmount();
- });
-});
-
-describe('getOntologySearchTerm', () => {
- test('validate', () => {
- const ont = new OntologyModel({ abbreviation: 'abbr' });
- expect(getOntologySearchTerm(ont, 'test')).toBe('+ontology:abbr AND ("test" OR test)');
- expect(getOntologySearchTerm(ont, 'test 123')).toBe('+ontology:abbr AND ("test 123" OR test 123)');
- });
-});
diff --git a/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.test.tsx b/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.test.tsx
new file mode 100644
index 0000000000..a3de075863
--- /dev/null
+++ b/packages/components/src/internal/components/ontology/OntologyTreeSearchContainer.test.tsx
@@ -0,0 +1,124 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import {
+ getOntologySearchTerm,
+ OntologySearchResultsMenu,
+ OntologyTreeSearchContainer,
+} from './OntologyTreeSearchContainer';
+import { ConceptModel, OntologyModel } from './models';
+
+const TEST_ONTOLOGY = new OntologyModel({
+ abbreviation: 't',
+ name: 'test name',
+ conceptCount: 100,
+ description: 'test desc',
+});
+
+const TEST_SEARCH_HITS = [
+ new ConceptModel({ code: 'a', label: 'A', description: 'Description for a' }),
+ new ConceptModel({ code: 'b', label: 'B', description: 'Description for b' }),
+];
+
+describe('OntologyTreeSearchContainer', () => {
+ test('default props', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('.concept-search-container')).not.toBeNull();
+ expect(container.querySelector('input')).not.toBeNull();
+ expect((container.querySelector('input') as HTMLInputElement).placeholder).toBe('Search t');
+ expect(container.querySelector('ul.result-menu')).toBeNull();
+ });
+});
+
+const DEFAULT_PROPS = {
+ searchHits: undefined,
+ totalHits: undefined,
+ isFocused: true,
+ error: undefined,
+ onItemClick: jest.fn(),
+};
+
+describe('OntologySearchResultsMenu', () => {
+ test('showMenu', () => {
+ const { container, rerender } = render();
+ expect(container.querySelector('ul.result-menu')).toBeNull();
+ expect(container.querySelectorAll('li')).toHaveLength(0);
+
+ rerender();
+ expect(container.querySelector('ul.result-menu')).toBeNull();
+
+ rerender();
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelectorAll('li')).toHaveLength(1);
+
+ rerender();
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelectorAll('li')).toHaveLength(0);
+ });
+
+ test('error', () => {
+ const { container } = render();
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelector('[role="alert"]')).not.toBeNull();
+ expect(container.querySelector('[role="alert"]').textContent).toBe('test error');
+ });
+
+ test('no search results found', () => {
+ const { container } = render();
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelectorAll('li')).toHaveLength(1);
+ expect(container.querySelector('li').textContent).toBe('No search results found.');
+ });
+
+ test('totalHits footer', () => {
+ const { container } = render();
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelector('.result-footer')).not.toBeNull();
+ expect(container.querySelector('.result-footer').textContent).toContain('2 results found.');
+ });
+
+ test('with searchHits, with descriptions', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('ul.result-menu')).not.toBeNull();
+ expect(container.querySelectorAll('li')).toHaveLength(TEST_SEARCH_HITS.length);
+ expect(container.querySelectorAll('.selectable-item')).toHaveLength(TEST_SEARCH_HITS.length);
+ const boldItems = container.querySelectorAll('.bold');
+ expect(boldItems).toHaveLength(TEST_SEARCH_HITS.length);
+ expect(boldItems[0].textContent).toBe(TEST_SEARCH_HITS[0].label);
+ expect(boldItems[1].textContent).toBe(TEST_SEARCH_HITS[1].label);
+ const codeItems = container.querySelectorAll('.col-xs-2');
+ expect(codeItems[0].textContent).toBe(TEST_SEARCH_HITS[0].code);
+ expect(codeItems[1].textContent).toBe(TEST_SEARCH_HITS[1].code);
+ expect(container.querySelectorAll('.col-xs-10')).toHaveLength(0);
+ const colXs5Items = container.querySelectorAll('.col-xs-5');
+ expect(colXs5Items).toHaveLength(TEST_SEARCH_HITS.length * 2);
+ expect(colXs5Items[colXs5Items.length - 1].textContent).toBe(TEST_SEARCH_HITS[1].description);
+ });
+
+ test('with searchHits, without descriptions', () => {
+ const searchHits = [new ConceptModel({ code: 'a', label: 'A' })];
+ const { container } = render(
+
+ );
+ expect(container.querySelectorAll('li')).toHaveLength(searchHits.length);
+ expect(container.querySelectorAll('.col-xs-5')).toHaveLength(0);
+ expect(container.querySelectorAll('.col-xs-10')).toHaveLength(searchHits.length);
+ expect(container.querySelector('.col-xs-10').textContent).toBe(searchHits[0].label);
+ });
+});
+
+describe('getOntologySearchTerm', () => {
+ test('validate', () => {
+ const ont = new OntologyModel({ abbreviation: 'abbr' });
+ expect(getOntologySearchTerm(ont, 'test')).toBe('+ontology:abbr AND ("test" OR test)');
+ expect(getOntologySearchTerm(ont, 'test 123')).toBe('+ontology:abbr AND ("test 123" OR test 123)');
+ });
+});
diff --git a/packages/components/src/internal/components/user/UserDeleteConfirmModal.tsx b/packages/components/src/internal/components/user/UserDeleteConfirmModal.tsx
index 99d6b7a013..c2087b4773 100644
--- a/packages/components/src/internal/components/user/UserDeleteConfirmModal.tsx
+++ b/packages/components/src/internal/components/user/UserDeleteConfirmModal.tsx
@@ -63,12 +63,12 @@ export class UserDeleteConfirmModal extends React.Component {
Deletion of a user is permanent and cannot be undone. Deleted users:
-
- - will no longer be displayed with actions taken or data uploaded by them
- - will be removed from groups and permissions settings
- - cannot be reactivated
-
+
+ - will no longer be displayed with actions taken or data uploaded by them
+ - will be removed from groups and permissions settings
+ - cannot be reactivated
+
{Utils.pluralBasic(userCount, 'user')} will be deleted. Do you want to proceed?
{error && {error}}