diff --git a/awx/ui_next/src/components/Lookup/Lookup.test.jsx b/awx/ui_next/src/components/Lookup/Lookup.test.jsx
index 0729d537c2..186f231f11 100644
--- a/awx/ui_next/src/components/Lookup/Lookup.test.jsx
+++ b/awx/ui_next/src/components/Lookup/Lookup.test.jsx
@@ -1,285 +1,362 @@
/* eslint-disable react/jsx-pascal-case */
import React from 'react';
import { createMemoryHistory } from 'history';
-import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
+import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import Lookup, { _Lookup } from './Lookup';
let mockData = [{ name: 'foo', id: 1, isChecked: false }];
const mockColumns = [{ name: 'Name', key: 'name', isSortable: true }];
-describe('', () => {
- test('initially renders succesfully', () => {
- mountWithContexts(
- {}}
- getItems={() => {}}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- );
+
+/**
+ * Check that an element is present on the document body
+ * @param {selector} query selector
+ */
+function checkRootElementPresent(selector) {
+ const queryResult = global.document.querySelector(selector);
+ expect(queryResult).not.toEqual(null);
+}
+
+/**
+ * Check that an element isn't present on the document body
+ * @param {selector} query selector
+ */
+function checkRootElementNotPresent(selector) {
+ const queryResult = global.document.querySelector(selector);
+ expect(queryResult).toEqual(null);
+}
+
+/**
+ * Check lookup input group tags for expected values
+ * @param {wrapper} enzyme wrapper instance
+ * @param {expected} array of expected tag values
+ */
+async function checkInputTagValues(wrapper, expected) {
+ checkRootElementNotPresent('body div[role="dialog"]');
+ // check input group chip values
+ const chips = await waitForElement(
+ wrapper,
+ 'Lookup InputGroup Chip span',
+ el => el.length === expected.length
+ );
+ expect(chips).toHaveLength(expected.length);
+ chips.forEach((el, index) => {
+ expect(el.text()).toEqual(expected[index]);
});
+}
- test('API response is formatted properly', done => {
- const wrapper = mountWithContexts(
- {}}
- getItems={() => ({
- data: { results: [{ name: 'test instance', id: 1 }] },
- })}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- ).find('Lookup');
-
- setImmediate(() => {
- expect(wrapper.state().results).toEqual([
- { id: 1, name: 'test instance' },
- ]);
- done();
- });
+/**
+ * Check lookup modal list for expected values
+ * @param {wrapper} enzyme wrapper instance
+ * @param {expected} array of [selected, text] pairs describing
+ * the expected visible state of the modal data list
+ */
+async function checkModalListValues(wrapper, expected) {
+ // fail if modal isn't actually visible
+ checkRootElementPresent('body div[role="dialog"]');
+ // check list item values
+ const rows = await waitForElement(
+ wrapper,
+ 'DataListItemRow',
+ el => el.length === expected.length
+ );
+ expect(rows).toHaveLength(expected.length);
+ rows.forEach((el, index) => {
+ const [expectedChecked, expectedText] = expected[index];
+ expect(expectedText).toEqual(el.text());
+ expect(expectedChecked).toEqual(el.find('input').props().checked);
});
+}
- test('Opens modal when search icon is clicked', () => {
- const spy = jest.spyOn(_Lookup.prototype, 'handleModalToggle');
- const mockSelected = [{ name: 'foo', id: 1 }];
- const wrapper = mountWithContexts(
+/**
+ * Check lookup modal selection tags for expected values
+ * @param {wrapper} enzyme wrapper instance
+ * @param {expected} array of expected tag values
+ */
+async function checkModalTagValues(wrapper, expected) {
+ // fail if modal isn't actually visible
+ checkRootElementPresent('body div[role="dialog"]');
+ // check modal chip values
+ const chips = await waitForElement(
+ wrapper,
+ 'Modal Chip span',
+ el => el.length === expected.length
+ );
+ expect(chips).toHaveLength(expected.length);
+ chips.forEach((el, index) => {
+ expect(el.text()).toEqual(expected[index]);
+ });
+}
+
+describe('', () => {
+ let wrapper;
+ let onChange;
+
+ beforeEach(() => {
+ const mockSelected = [{ name: 'foo', id: 1, url: '/api/v2/item/1' }];
+ onChange = jest.fn();
+ document.body.innerHTML = '';
+ wrapper = mountWithContexts(
{}}
- getItems={() => {}}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- ).find('Lookup');
- expect(spy).not.toHaveBeenCalled();
- expect(wrapper.state('lookupSelectedItems')).toEqual(mockSelected);
- const searchItem = wrapper.find('button[aria-label="Search"]');
- searchItem.first().simulate('click');
- expect(spy).toHaveBeenCalled();
- expect(wrapper.state('lookupSelectedItems')).toEqual([
- {
- id: 1,
- name: 'foo',
- },
- ]);
- expect(wrapper.state('isModalOpen')).toEqual(true);
- });
-
- test('calls "toggleSelected" when a user changes a checkbox', done => {
- const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected');
- const mockSelected = [{ name: 'foo', id: 1 }];
- const data = {
- results: [{ name: 'test instance', id: 1, url: '/foo' }],
- count: 1,
- };
- const wrapper = mountWithContexts(
- {}}
- getItems={() => ({ data })}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- );
- setImmediate(() => {
- const searchItem = wrapper.find('button[aria-label="Search"]');
- searchItem.first().simulate('click');
- wrapper.find('input[type="checkbox"]').simulate('change');
- expect(spy).toHaveBeenCalled();
- done();
- });
- });
-
- test('calls "toggleSelected" when remove icon is clicked', () => {
- const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected');
- mockData = [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }];
- const data = {
- results: [{ name: 'test instance', id: 1, url: '/foo' }],
- count: 1,
- };
- const wrapper = mountWithContexts(
- {}}
- getItems={() => ({ data })}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- );
- const removeIcon = wrapper.find('button[aria-label="close"]').first();
- removeIcon.simulate('click');
- expect(spy).toHaveBeenCalled();
- });
-
- test('renders chips from prop value', () => {
- mockData = [{ name: 'foo', id: 0 }, { name: 'bar', id: 1 }];
- const wrapper = mountWithContexts(
- {}}
- value={mockData}
- selected={[]}
- getItems={() => {}}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- ).find('Lookup');
- const chip = wrapper.find('.pf-c-chip');
- expect(chip).toHaveLength(2);
- });
-
- test('toggleSelected successfully adds/removes row from lookupSelectedItems state', () => {
- mockData = [];
- const wrapper = mountWithContexts(
- {}}
- value={mockData}
- getItems={() => {}}
- columns={mockColumns}
- sortedColumnKey="name"
- multiple
- />
- ).find('Lookup');
- wrapper.instance().toggleSelected({
- id: 1,
- name: 'foo',
- });
- expect(wrapper.state('lookupSelectedItems')).toEqual([
- {
- id: 1,
- name: 'foo',
- },
- ]);
- wrapper.instance().toggleSelected({
- id: 1,
- name: 'foo',
- });
- expect(wrapper.state('lookupSelectedItems')).toEqual([]);
- });
-
- test('saveModal calls callback with selected items', () => {
- mockData = [];
- const onLookupSaveFn = jest.fn();
- const wrapper = mountWithContexts(
- {}}
- sortedColumnKey="name"
- multiple
- />
- ).find('Lookup');
- wrapper.instance().toggleSelected({
- id: 1,
- name: 'foo',
- });
- expect(wrapper.state('lookupSelectedItems')).toEqual([
- {
- id: 1,
- name: 'foo',
- },
- ]);
- wrapper.instance().saveModal();
- expect(onLookupSaveFn).toHaveBeenCalledWith(
- [
- {
- id: 1,
- name: 'foo',
- },
- ],
- 'fooBar'
- );
- });
-
- test('should call callback with selected single item', () => {
- mockData = { name: 'foo', id: 1, isChecked: false, url: 'https://foo' };
- const onLookupSaveFn = jest.fn();
- const wrapper = mountWithContexts(
- ({
data: {
- results: [mockData],
- count: 1,
+ count: 2,
+ results: [
+ ...mockSelected,
+ { name: 'bar', id: 2, url: '/api/v2/item/2' },
+ ],
},
})}
+ columns={mockColumns}
sortedColumnKey="name"
/>
);
+ });
+
+ test('Initially renders succesfully', () => {
+ expect(wrapper.find('Lookup')).toHaveLength(1);
+ });
+
+ test('Expected items are shown', async done => {
+ expect(wrapper.find('Lookup')).toHaveLength(1);
+ await checkInputTagValues(wrapper, ['foo']);
+ done();
+ });
+
+ test('Open and close modal', async done => {
+ checkRootElementNotPresent('body div[role="dialog"]');
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ checkRootElementPresent('body div[role="dialog"]');
+ // This check couldn't pass unless api response was formatted properly
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ wrapper.find('Modal button[aria-label="Close"]').simulate('click');
+ checkRootElementNotPresent('body div[role="dialog"]');
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ checkRootElementPresent('body div[role="dialog"]');
wrapper
- .find('Lookup')
- .instance()
- .toggleSelected({
- id: 1,
- name: 'foo',
- });
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Cancel')
+ .first()
+ .simulate('click');
+ checkRootElementNotPresent('body div[role="dialog"]');
+ done();
+ });
+
+ test('Add item with checkbox then save', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
wrapper
- .find('Lookup')
- .instance()
- .saveModal();
- expect(onLookupSaveFn).toHaveBeenCalledWith(
- {
- id: 1,
- name: 'foo',
- },
- 'fooBar'
+ .find('DataListItemRow')
+ .findWhere(el => el.text() === 'bar')
+ .find('input[type="checkbox"]')
+ .simulate('change');
+ await checkModalListValues(wrapper, [[true, 'foo'], [true, 'bar']]);
+ wrapper
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Save')
+ .first()
+ .simulate('click');
+ expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange.mock.calls[0][0].map(({ name }) => name)).toEqual([
+ 'foo',
+ 'bar',
+ ]);
+ done();
+ });
+
+ test('Add item with checkbox then cancel', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ wrapper
+ .find('DataListItemRow')
+ .findWhere(el => el.text() === 'bar')
+ .find('input[type="checkbox"]')
+ .simulate('change');
+ await checkModalListValues(wrapper, [[true, 'foo'], [true, 'bar']]);
+ wrapper
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Cancel')
+ .first()
+ .simulate('click');
+ expect(onChange).toHaveBeenCalledTimes(0);
+ await checkInputTagValues(wrapper, ['foo']);
+ done();
+ });
+
+ test('Remove item with checkbox', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, ['foo']);
+ wrapper
+ .find('DataListItemRow')
+ .findWhere(el => el.text() === 'foo')
+ .find('input[type="checkbox"]')
+ .simulate('change');
+ await checkModalListValues(wrapper, [[false, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, []);
+ done();
+ });
+
+ test('Remove item with selected icon button', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, ['foo']);
+ wrapper
+ .find('Modal Chip')
+ .findWhere(el => el.text() === 'foo')
+ .first()
+ .find('button')
+ .simulate('click');
+ await checkModalListValues(wrapper, [[false, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, []);
+ done();
+ });
+
+ test('Remove item with input group button', async done => {
+ await checkInputTagValues(wrapper, ['foo']);
+ wrapper
+ .find('Lookup InputGroup Chip')
+ .findWhere(el => el.text() === 'foo')
+ .first()
+ .find('button')
+ .simulate('click');
+ expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledWith([], 'foobar');
+ done();
+ });
+});
+
+describe('', () => {
+ let wrapper;
+ let onChange;
+
+ beforeEach(() => {
+ const mockSelected = { name: 'foo', id: 1, url: '/api/v2/item/1' };
+ onChange = jest.fn();
+ document.body.innerHTML = '';
+ wrapper = mountWithContexts(
+ ({
+ data: {
+ count: 2,
+ results: [
+ mockSelected,
+ { name: 'bar', id: 2, url: '/api/v2/item/2' },
+ ],
+ },
+ })}
+ columns={mockColumns}
+ sortedColumnKey="name"
+ />
);
});
- test('should re-fetch data when URL params change', async () => {
+ test('Initially renders succesfully', () => {
+ expect(wrapper.find('Lookup')).toHaveLength(1);
+ });
+
+ test('Expected items are shown', async done => {
+ expect(wrapper.find('Lookup')).toHaveLength(1);
+ await checkInputTagValues(wrapper, ['foo']);
+ done();
+ });
+
+ test('Open and close modal', async done => {
+ checkRootElementNotPresent('body div[role="dialog"]');
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ checkRootElementPresent('body div[role="dialog"]');
+ // This check couldn't pass unless api response was formatted properly
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ wrapper.find('Modal button[aria-label="Close"]').simulate('click');
+ checkRootElementNotPresent('body div[role="dialog"]');
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ checkRootElementPresent('body div[role="dialog"]');
+ wrapper
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Cancel')
+ .first()
+ .simulate('click');
+ checkRootElementNotPresent('body div[role="dialog"]');
+ done();
+ });
+
+ test('Change selected item with radio control then save', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, ['foo']);
+ wrapper
+ .find('DataListItemRow')
+ .findWhere(el => el.text() === 'bar')
+ .find('input[type="radio"]')
+ .simulate('change');
+ await checkModalListValues(wrapper, [[false, 'foo'], [true, 'bar']]);
+ await checkModalTagValues(wrapper, ['bar']);
+ wrapper
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Save')
+ .first()
+ .simulate('click');
+ expect(onChange).toHaveBeenCalledTimes(1);
+ const [[{ name }]] = onChange.mock.calls;
+ expect(name).toEqual('bar');
+ done();
+ });
+
+ test('Change selected item with checkbox then cancel', async done => {
+ wrapper.find('button[aria-label="Search"]').simulate('click');
+ await checkModalListValues(wrapper, [[true, 'foo'], [false, 'bar']]);
+ await checkModalTagValues(wrapper, ['foo']);
+ wrapper
+ .find('DataListItemRow')
+ .findWhere(el => el.text() === 'bar')
+ .find('input[type="radio"]')
+ .simulate('change');
+ await checkModalListValues(wrapper, [[false, 'foo'], [true, 'bar']]);
+ await checkModalTagValues(wrapper, ['bar']);
+ wrapper
+ .find('Modal button')
+ .findWhere(e => e.text() === 'Cancel')
+ .first()
+ .simulate('click');
+ expect(onChange).toHaveBeenCalledTimes(0);
+ done();
+ });
+
+ test('should re-fetch data when URL params change', async done => {
mockData = [{ name: 'foo', id: 1, isChecked: false }];
const history = createMemoryHistory({
initialEntries: ['/organizations/add'],
});
const getItems = jest.fn();
- const wrapper = mountWithContexts(
+ const LookupWrapper = mountWithContexts(
<_Lookup
+ multiple
+ name="foo"
lookupHeader="Foo Bar"
onLookupSave={() => {}}
value={mockData}
- selected={[]}
columns={mockColumns}
sortedColumnKey="name"
getItems={getItems}
- multiple
- handleHttpError={() => {}}
location={{ history }}
i18n={{ _: val => val.toString() }}
/>
);
-
expect(getItems).toHaveBeenCalledTimes(1);
history.push('organizations/add?page=2');
- wrapper.setProps({
+ LookupWrapper.setProps({
location: { history },
});
- wrapper.update();
+ LookupWrapper.update();
expect(getItems).toHaveBeenCalledTimes(2);
+ done();
});
});