mirror of
https://github.com/ansible/awx.git
synced 2026-01-20 06:01:25 -03:30
Adds test coverage for node modal components
This commit is contained in:
parent
887469d73e
commit
50c74a2ec8
@ -1,7 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
} from '@contexts/Workflow';
|
||||
import { WorkflowDispatchContext } from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import DeleteAllNodesModal from './DeleteAllNodesModal';
|
||||
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import { WorkflowDispatchContext, WorkflowStateContext } from '@contexts/Workflow';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import LinkAddModal from './LinkAddModal';
|
||||
|
||||
const dispatch = jest.fn();
|
||||
|
||||
const workflowContext = {
|
||||
linkToEdit: null
|
||||
linkToEdit: null,
|
||||
};
|
||||
|
||||
describe('LinkAddModal', () => {
|
||||
@ -17,8 +20,11 @@ describe('LinkAddModal', () => {
|
||||
<LinkAddModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
);
|
||||
wrapper.find('button#link-confirm').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({ type: 'CREATE_LINK', linkType: 'success' });
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'CREATE_LINK',
|
||||
linkType: 'success',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -28,7 +28,7 @@ function LinkDeleteModal({ i18n }) {
|
||||
{i18n._(t`Remove`)}
|
||||
</Button>,
|
||||
<Button
|
||||
id="cancel-link-removal"
|
||||
id="cancel-link-removal"
|
||||
aria-label={i18n._(t`Cancel link removal`)}
|
||||
key="cancel"
|
||||
onClick={() => dispatch({ type: 'SET_LINK_TO_DELETE', value: null })}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import LinkDeleteModal from './LinkDeleteModal';
|
||||
@ -12,13 +12,13 @@ const dispatch = jest.fn();
|
||||
const workflowContext = {
|
||||
linkToDelete: {
|
||||
source: {
|
||||
id: 2
|
||||
id: 2,
|
||||
},
|
||||
target: {
|
||||
id: 3
|
||||
id: 3,
|
||||
},
|
||||
linkType: 'always'
|
||||
}
|
||||
linkType: 'always',
|
||||
},
|
||||
};
|
||||
|
||||
describe('LinkDeleteModal', () => {
|
||||
@ -54,7 +54,8 @@ describe('LinkDeleteModal', () => {
|
||||
test('Close button dispatches as expected', () => {
|
||||
wrapper.find('TimesIcon').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'SET_LINK_TO_DELETE', value: null
|
||||
type: 'SET_LINK_TO_DELETE',
|
||||
value: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import { WorkflowDispatchContext, WorkflowStateContext } from '@contexts/Workflow';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import LinkEditModal from './LinkEditModal';
|
||||
|
||||
const dispatch = jest.fn();
|
||||
@ -8,13 +11,13 @@ const dispatch = jest.fn();
|
||||
const workflowContext = {
|
||||
linkToEdit: {
|
||||
source: {
|
||||
id: 2
|
||||
id: 2,
|
||||
},
|
||||
target: {
|
||||
id: 3
|
||||
id: 3,
|
||||
},
|
||||
linkType: 'always'
|
||||
}
|
||||
linkType: 'always',
|
||||
},
|
||||
};
|
||||
|
||||
describe('LinkEditModal', () => {
|
||||
@ -25,8 +28,11 @@ describe('LinkEditModal', () => {
|
||||
<LinkEditModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
);
|
||||
wrapper.find('button#link-confirm').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({ type: 'UPDATE_LINK', linkType: 'always' });
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'UPDATE_LINK',
|
||||
linkType: 'always',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import LinkModal from './LinkModal';
|
||||
|
||||
const dispatch = jest.fn();
|
||||
const onConfirm = jest.fn();
|
||||
let wrapper;
|
||||
|
||||
describe('LinkModal', () => {
|
||||
describe('Adding new link', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
linkToEdit: null,
|
||||
}}
|
||||
>
|
||||
<LinkModal header="TEST" onConfirm={onConfirm} />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Dropdown defaults to success when adding new link', () => {
|
||||
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('success');
|
||||
});
|
||||
|
||||
test('Cancel button dispatches as expected', () => {
|
||||
wrapper.find('button#link-cancel').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'CANCEL_LINK_MODAL',
|
||||
});
|
||||
});
|
||||
|
||||
test('Close button dispatches as expected', () => {
|
||||
wrapper.find('TimesIcon').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'CANCEL_LINK_MODAL',
|
||||
});
|
||||
});
|
||||
|
||||
test('Confirm button passes callback correct link type after changing dropdown', () => {
|
||||
act(() => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(null, 'always');
|
||||
});
|
||||
wrapper.find('button#link-confirm').simulate('click');
|
||||
expect(onConfirm).toHaveBeenCalledWith('always');
|
||||
});
|
||||
});
|
||||
describe('Editing existing link', () => {
|
||||
test('Dropdown defaults to existing link type when editing link', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
linkToEdit: {
|
||||
source: {
|
||||
id: 2,
|
||||
},
|
||||
target: {
|
||||
id: 3,
|
||||
},
|
||||
linkType: 'failure',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<LinkModal header="TEST" onConfirm={onConfirm} />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('failure');
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -11,13 +11,12 @@ function NodeAddModal({ i18n }) {
|
||||
const dispatch = useContext(WorkflowDispatchContext);
|
||||
const { addNodeSource } = useContext(WorkflowStateContext);
|
||||
|
||||
const addNode = (linkType, resource, nodeType) => {
|
||||
const addNode = (resource, linkType) => {
|
||||
dispatch({
|
||||
type: 'CREATE_NODE',
|
||||
node: {
|
||||
linkType,
|
||||
nodeResource: resource,
|
||||
nodeType,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import NodeAddModal from './NodeAddModal';
|
||||
|
||||
const dispatch = jest.fn();
|
||||
|
||||
const nodeResource = {
|
||||
id: 448,
|
||||
type: 'job_template',
|
||||
name: 'Test JT',
|
||||
};
|
||||
|
||||
const workflowContext = {
|
||||
addNodeSource: 2,
|
||||
};
|
||||
|
||||
describe('NodeAddModal', () => {
|
||||
test('Node modal confirmation dispatches as expected', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider value={workflowContext}>
|
||||
<NodeAddModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
act(() => {
|
||||
wrapper.find('NodeModal').prop('onSave')(nodeResource, 'success');
|
||||
});
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'CREATE_NODE',
|
||||
node: {
|
||||
linkType: 'success',
|
||||
nodeResource,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -19,6 +19,7 @@ function NodeDeleteModal({ i18n }) {
|
||||
onClose={() => dispatch({ type: 'SET_NODE_TO_DELETE', value: null })}
|
||||
actions={[
|
||||
<Button
|
||||
id="confirm-node-removal"
|
||||
key="remove"
|
||||
variant="danger"
|
||||
aria-label={i18n._(t`Confirm node removal`)}
|
||||
@ -27,6 +28,7 @@ function NodeDeleteModal({ i18n }) {
|
||||
{i18n._(t`Remove`)}
|
||||
</Button>,
|
||||
<Button
|
||||
id="cancel-node-removal"
|
||||
key="cancel"
|
||||
variant="secondary"
|
||||
aria-label={i18n._(t`Cancel node removal`)}
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import NodeDeleteModal from './NodeDeleteModal';
|
||||
|
||||
let wrapper;
|
||||
const dispatch = jest.fn();
|
||||
|
||||
describe('NodeDeleteModal', () => {
|
||||
describe('Node with unified job template', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
nodeToDelete: {
|
||||
id: 2,
|
||||
unifiedJobTemplate: {
|
||||
id: 4000,
|
||||
name: 'Test JT',
|
||||
type: 'job_template',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NodeDeleteModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Mounts successfully', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
});
|
||||
|
||||
test('Confirm button dispatches as expected', () => {
|
||||
wrapper.find('button#confirm-node-removal').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'DELETE_NODE',
|
||||
});
|
||||
});
|
||||
|
||||
test('Cancel button dispatches as expected', () => {
|
||||
wrapper.find('button#cancel-node-removal').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'SET_NODE_TO_DELETE',
|
||||
value: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('Close button dispatches as expected', () => {
|
||||
wrapper.find('TimesIcon').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'SET_NODE_TO_DELETE',
|
||||
value: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Node without unified job template', () => {
|
||||
test('Mounts successfully', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
nodeToDelete: {
|
||||
id: 2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NodeDeleteModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
expect(wrapper.length).toBe(1);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -7,13 +7,11 @@ import NodeModal from './NodeModal';
|
||||
function NodeEditModal({ i18n }) {
|
||||
const dispatch = useContext(WorkflowDispatchContext);
|
||||
|
||||
const updateNode = (linkType, resource, nodeType) => {
|
||||
const updateNode = resource => {
|
||||
dispatch({
|
||||
type: 'UPDATE_NODE',
|
||||
node: {
|
||||
linkType,
|
||||
nodeResource: resource,
|
||||
nodeType,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import NodeEditModal from './NodeEditModal';
|
||||
|
||||
const dispatch = jest.fn();
|
||||
|
||||
const nodeResource = {
|
||||
id: 448,
|
||||
type: 'job_template',
|
||||
name: 'Test JT',
|
||||
};
|
||||
|
||||
const workflowContext = {
|
||||
nodeToEdit: {
|
||||
id: 4,
|
||||
unifiedJobTemplate: {
|
||||
id: 30,
|
||||
name: 'Foo JT',
|
||||
type: 'job_template',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('NodeEditModal', () => {
|
||||
test('Node modal confirmation dispatches as expected', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider value={workflowContext}>
|
||||
<NodeEditModal />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
act(() => {
|
||||
wrapper.find('NodeModal').prop('onSave')(nodeResource);
|
||||
});
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'UPDATE_NODE',
|
||||
node: {
|
||||
nodeResource,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -104,7 +104,7 @@ function NodeModal({ askLinkType, i18n, onSave, title }) {
|
||||
}
|
||||
: nodeResource;
|
||||
|
||||
onSave(linkType, resource, nodeType);
|
||||
onSave(resource, askLinkType ? linkType : null);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@ -177,11 +177,15 @@ function NodeModal({ askLinkType, i18n, onSave, title }) {
|
||||
}
|
||||
/>
|
||||
{activeStep && activeStep.id !== 1 && (
|
||||
<Button variant="secondary" onClick={onBack}>
|
||||
<Button id="back-node-modal" variant="secondary" onClick={onBack}>
|
||||
{i18n._(t`Back`)}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="link" onClick={handleCancel}>
|
||||
<Button
|
||||
id="cancel-node-modal"
|
||||
variant="link"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
{i18n._(t`Cancel`)}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@ -0,0 +1,414 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
WorkflowStateContext,
|
||||
} from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import {
|
||||
InventorySourcesAPI,
|
||||
JobTemplatesAPI,
|
||||
ProjectsAPI,
|
||||
WorkflowJobTemplatesAPI,
|
||||
} from '@api';
|
||||
import NodeModal from './NodeModal';
|
||||
|
||||
jest.mock('@api/models/InventorySources');
|
||||
jest.mock('@api/models/JobTemplates');
|
||||
jest.mock('@api/models/Projects');
|
||||
jest.mock('@api/models/WorkflowJobTemplates');
|
||||
|
||||
let wrapper;
|
||||
const dispatch = jest.fn();
|
||||
const onSave = jest.fn();
|
||||
|
||||
describe('NodeModal', () => {
|
||||
beforeAll(() => {
|
||||
JobTemplatesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Job Template',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
ProjectsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Project',
|
||||
type: 'project',
|
||||
url: '/api/v2/projects/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
InventorySourcesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Inventory Source',
|
||||
type: 'inventory_source',
|
||||
url: '/api/v2/inventory_sources/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
WorkflowJobTemplatesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Workflow Job Template',
|
||||
type: 'workflow_job_template',
|
||||
url: '/api/v2/workflow_job_templates/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('Add new node', () => {
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
nodeToEdit: null,
|
||||
}}
|
||||
>
|
||||
<NodeModal askLinkType onSave={onSave} title="Add Node" />
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Can successfully create a new job template node', async () => {
|
||||
act(() => {
|
||||
wrapper.find('#link-type-always').simulate('click');
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
wrapper.find('DataListRadio').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Job Template',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/1',
|
||||
},
|
||||
'always'
|
||||
);
|
||||
});
|
||||
|
||||
test('Can successfully create a new project sync node', async () => {
|
||||
act(() => {
|
||||
wrapper.find('#link-type-failure').simulate('click');
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(null, 'project_sync');
|
||||
});
|
||||
wrapper.update();
|
||||
wrapper.find('DataListRadio').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Project',
|
||||
type: 'project',
|
||||
url: '/api/v2/projects/1',
|
||||
},
|
||||
'failure'
|
||||
);
|
||||
});
|
||||
|
||||
test('Can successfully create a new inventory source sync node', async () => {
|
||||
act(() => {
|
||||
wrapper.find('#link-type-failure').simulate('click');
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(
|
||||
null,
|
||||
'inventory_source_sync'
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
wrapper.find('DataListRadio').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Inventory Source',
|
||||
type: 'inventory_source',
|
||||
url: '/api/v2/inventory_sources/1',
|
||||
},
|
||||
'failure'
|
||||
);
|
||||
});
|
||||
|
||||
test('Can successfully create a new workflow job template node', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(
|
||||
null,
|
||||
'workflow_job_template'
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
wrapper.find('DataListRadio').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Workflow Job Template',
|
||||
type: 'workflow_job_template',
|
||||
url: '/api/v2/workflow_job_templates/1',
|
||||
},
|
||||
'success'
|
||||
);
|
||||
});
|
||||
|
||||
test('Can successfully create a new approval template node', async () => {
|
||||
act(() => {
|
||||
wrapper.find('#link-type-always').simulate('click');
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(null, 'approval');
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('input#approval-name').simulate('change', {
|
||||
target: { value: 'Test Approval', name: 'name' },
|
||||
});
|
||||
wrapper.find('input#approval-description').simulate('change', {
|
||||
target: { value: 'Test Approval Description', name: 'description' },
|
||||
});
|
||||
wrapper.find('input#approval-timeout-minutes').simulate('change', {
|
||||
target: { value: 5, name: 'timeoutMinutes' },
|
||||
});
|
||||
});
|
||||
|
||||
// Updating the minutes and seconds is split to avoid a race condition.
|
||||
// They both update the same state variable in the parent so triggering
|
||||
// them syncronously creates flakey test results.
|
||||
await act(async () => {
|
||||
wrapper.find('input#approval-timeout-seconds').simulate('change', {
|
||||
target: { value: 30, name: 'timeoutSeconds' },
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('input#approval-name').prop('value')).toBe(
|
||||
'Test Approval'
|
||||
);
|
||||
expect(wrapper.find('input#approval-description').prop('value')).toBe(
|
||||
'Test Approval Description'
|
||||
);
|
||||
expect(wrapper.find('input#approval-timeout-minutes').prop('value')).toBe(
|
||||
5
|
||||
);
|
||||
expect(wrapper.find('input#approval-timeout-seconds').prop('value')).toBe(
|
||||
30
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
description: 'Test Approval Description',
|
||||
name: 'Test Approval',
|
||||
timeout: 330,
|
||||
type: 'workflow_approval_template',
|
||||
},
|
||||
'always'
|
||||
);
|
||||
});
|
||||
|
||||
test('Cancel button dispatches as expected', () => {
|
||||
wrapper.find('button#cancel-node-modal').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'CANCEL_NODE_MODAL',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Edit existing node', () => {
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Can successfully change project sync node to workflow approval node', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
nodeToEdit: {
|
||||
id: 2,
|
||||
unifiedJobTemplate: {
|
||||
id: 1,
|
||||
name: 'Test Project',
|
||||
unified_job_type: 'project_update',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NodeModal
|
||||
askLinkType={false}
|
||||
onSave={onSave}
|
||||
title="Edit Node"
|
||||
/>
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('project_sync');
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(null, 'approval');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper.find('input#approval-name').simulate('change', {
|
||||
target: { value: 'Test Approval', name: 'name' },
|
||||
});
|
||||
wrapper.find('input#approval-description').simulate('change', {
|
||||
target: { value: 'Test Approval Description', name: 'description' },
|
||||
});
|
||||
wrapper.find('input#approval-timeout-minutes').simulate('change', {
|
||||
target: { value: 5, name: 'timeoutMinutes' },
|
||||
});
|
||||
});
|
||||
|
||||
// Updating the minutes and seconds is split to avoid a race condition.
|
||||
// They both update the same state variable in the parent so triggering
|
||||
// them syncronously creates flakey test results.
|
||||
await act(async () => {
|
||||
wrapper.find('input#approval-timeout-seconds').simulate('change', {
|
||||
target: { value: 30, name: 'timeoutSeconds' },
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('input#approval-name').prop('value')).toBe(
|
||||
'Test Approval'
|
||||
);
|
||||
expect(wrapper.find('input#approval-description').prop('value')).toBe(
|
||||
'Test Approval Description'
|
||||
);
|
||||
expect(wrapper.find('input#approval-timeout-minutes').prop('value')).toBe(
|
||||
5
|
||||
);
|
||||
expect(wrapper.find('input#approval-timeout-seconds').prop('value')).toBe(
|
||||
30
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
description: 'Test Approval Description',
|
||||
name: 'Test Approval',
|
||||
timeout: 330,
|
||||
type: 'workflow_approval_template',
|
||||
},
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
test('Can successfully change approval node to workflow job template node', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<WorkflowStateContext.Provider
|
||||
value={{
|
||||
nodeToEdit: {
|
||||
id: 2,
|
||||
unifiedJobTemplate: {
|
||||
id: 1,
|
||||
name: 'Test Approval',
|
||||
description: 'Test Approval Description',
|
||||
unified_job_type: 'workflow_approval',
|
||||
timeout: 0,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NodeModal
|
||||
askLinkType={false}
|
||||
onSave={onSave}
|
||||
title="Edit Node"
|
||||
/>
|
||||
</WorkflowStateContext.Provider>
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('approval');
|
||||
await act(async () => {
|
||||
wrapper.find('AnsibleSelect').prop('onChange')(
|
||||
null,
|
||||
'workflow_job_template'
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
wrapper.find('DataListRadio').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('button#next-node-modal').simulate('click');
|
||||
});
|
||||
expect(onSave).toBeCalledWith(
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Workflow Job Template',
|
||||
type: 'workflow_job_template',
|
||||
url: '/api/v2/workflow_job_templates/1',
|
||||
},
|
||||
null
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -18,6 +18,7 @@ function NodeNextButton({
|
||||
|
||||
return (
|
||||
<Button
|
||||
id="next-node-modal"
|
||||
variant="primary"
|
||||
type="submit"
|
||||
onClick={() => onClick(activeStep)}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import NodeNextButton from './NodeNextButton';
|
||||
|
||||
const activeStep = {
|
||||
name: 'Node Type',
|
||||
key: 'node_resource',
|
||||
enableNext: true,
|
||||
component: {},
|
||||
id: 1,
|
||||
};
|
||||
const buttonText = 'Next';
|
||||
const onClick = jest.fn();
|
||||
const onNext = jest.fn();
|
||||
const triggerNext = 0;
|
||||
let wrapper;
|
||||
|
||||
describe('NodeNextButton', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = mount(
|
||||
<NodeNextButton
|
||||
activeStep={activeStep}
|
||||
buttonText={buttonText}
|
||||
onClick={onClick}
|
||||
onNext={onNext}
|
||||
triggerNext={triggerNext}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Button text matches', () => {
|
||||
expect(wrapper.find('button').text()).toBe(buttonText);
|
||||
});
|
||||
|
||||
test('Clicking button makes expected callback', () => {
|
||||
wrapper.find('button').simulate('click');
|
||||
expect(onClick).toBeCalledWith(activeStep);
|
||||
});
|
||||
|
||||
test('onNext triggered when triggerNext counter incrimented', () => {
|
||||
wrapper.setProps({ triggerNext: 1 });
|
||||
expect(onNext).toBeCalled();
|
||||
});
|
||||
});
|
||||
@ -26,6 +26,7 @@ const TimeoutInput = styled(TextInput)`
|
||||
margin-left: 20px;
|
||||
}
|
||||
`;
|
||||
TimeoutInput.displayName = 'TimeoutInput';
|
||||
|
||||
const TimeoutLabel = styled.p`
|
||||
margin-left: 10px;
|
||||
@ -125,12 +126,12 @@ function NodeTypeStep({
|
||||
timeoutMinutes: Math.floor(timeout / 60),
|
||||
timeoutSeconds: timeout - Math.floor(timeout / 60) * 60,
|
||||
}}
|
||||
render={() => (
|
||||
>
|
||||
{() => (
|
||||
<Form css="margin-top: 20px;">
|
||||
<FormRow>
|
||||
<Field
|
||||
name="name"
|
||||
render={({ field, form }) => {
|
||||
<Field name="name">
|
||||
{({ field, form }) => {
|
||||
const isValid =
|
||||
form &&
|
||||
(!form.touched[field.name] || !form.errors[field.name]);
|
||||
@ -150,19 +151,18 @@ function NodeTypeStep({
|
||||
type="text"
|
||||
{...field}
|
||||
onChange={(value, evt) => {
|
||||
onUpdateName(value);
|
||||
onUpdateName(evt.target.value);
|
||||
field.onChange(evt);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<Field
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<Field name="description">
|
||||
{({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="approval-description"
|
||||
label={i18n._(t`Description`)}
|
||||
@ -172,13 +172,13 @@ function NodeTypeStep({
|
||||
type="text"
|
||||
{...field}
|
||||
onChange={(value, evt) => {
|
||||
onUpdateDescription(value);
|
||||
onUpdateDescription(evt.target.value);
|
||||
field.onChange(evt);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<FormGroup
|
||||
@ -186,9 +186,8 @@ function NodeTypeStep({
|
||||
fieldId="approval-timeout"
|
||||
>
|
||||
<div css="display: flex;align-items: center;">
|
||||
<Field
|
||||
name="timeoutMinutes"
|
||||
render={({ field, form }) => (
|
||||
<Field name="timeoutMinutes">
|
||||
{({ field, form }) => (
|
||||
<>
|
||||
<TimeoutInput
|
||||
id="approval-timeout-minutes"
|
||||
@ -197,11 +196,14 @@ function NodeTypeStep({
|
||||
step="1"
|
||||
{...field}
|
||||
onChange={(value, evt) => {
|
||||
if (!value || value === '') {
|
||||
value = 0;
|
||||
if (
|
||||
!evt.target.value ||
|
||||
evt.target.value === ''
|
||||
) {
|
||||
evt.target.value = 0;
|
||||
}
|
||||
onUpdateTimeout(
|
||||
Number(value) * 60 +
|
||||
Number(evt.target.value) * 60 +
|
||||
Number(form.values.timeoutSeconds)
|
||||
);
|
||||
field.onChange(evt);
|
||||
@ -212,10 +214,9 @@ function NodeTypeStep({
|
||||
</TimeoutLabel>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Field
|
||||
name="timeoutSeconds"
|
||||
render={({ field, form }) => (
|
||||
</Field>
|
||||
<Field name="timeoutSeconds">
|
||||
{({ field, form }) => (
|
||||
<>
|
||||
<TimeoutInput
|
||||
id="approval-timeout-seconds"
|
||||
@ -224,11 +225,14 @@ function NodeTypeStep({
|
||||
step="1"
|
||||
{...field}
|
||||
onChange={(value, evt) => {
|
||||
if (!value || value === '') {
|
||||
value = 0;
|
||||
if (
|
||||
!evt.target.value ||
|
||||
evt.target.value === ''
|
||||
) {
|
||||
evt.target.value = 0;
|
||||
}
|
||||
onUpdateTimeout(
|
||||
Number(value) +
|
||||
Number(evt.target.value) +
|
||||
Number(form.values.timeoutMinutes) * 60
|
||||
);
|
||||
field.onChange(evt);
|
||||
@ -239,13 +243,13 @@ function NodeTypeStep({
|
||||
</TimeoutLabel>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</FormRow>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</Formik>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { WorkflowDispatchContext } from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import NodeViewModal from './NodeViewModal';
|
||||
|
||||
let wrapper;
|
||||
const dispatch = jest.fn();
|
||||
|
||||
describe('NodeViewModal', () => {
|
||||
test('Close button dispatches as expected', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<WorkflowDispatchContext.Provider value={dispatch}>
|
||||
<NodeViewModal />
|
||||
</WorkflowDispatchContext.Provider>
|
||||
);
|
||||
wrapper.find('TimesIcon').simulate('click');
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
type: 'SET_NODE_TO_VIEW',
|
||||
value: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -29,6 +29,7 @@ function RunStep({ i18n, linkType, onUpdateLinkType }) {
|
||||
</p>
|
||||
<Grid>
|
||||
<SelectableCard
|
||||
id="link-type-success"
|
||||
isSelected={linkType === 'success'}
|
||||
label={i18n._(t`On Success`)}
|
||||
description={i18n._(
|
||||
@ -37,6 +38,7 @@ function RunStep({ i18n, linkType, onUpdateLinkType }) {
|
||||
onClick={() => onUpdateLinkType('success')}
|
||||
/>
|
||||
<SelectableCard
|
||||
id="link-type-failure"
|
||||
isSelected={linkType === 'failure'}
|
||||
label={i18n._(t`On Failure`)}
|
||||
description={i18n._(
|
||||
@ -45,6 +47,7 @@ function RunStep({ i18n, linkType, onUpdateLinkType }) {
|
||||
onClick={() => onUpdateLinkType('failure')}
|
||||
/>
|
||||
<SelectableCard
|
||||
id="link-type-always"
|
||||
isSelected={linkType === 'always'}
|
||||
label={i18n._(t`Always`)}
|
||||
description={i18n._(
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import RunStep from './RunStep';
|
||||
|
||||
let wrapper;
|
||||
const linkType = 'always';
|
||||
const onUpdateLinkType = jest.fn();
|
||||
|
||||
describe('RunStep', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = mountWithContexts(
|
||||
<RunStep linkType={linkType} onUpdateLinkType={onUpdateLinkType} />
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('Default selected card matches default link type when present', () => {
|
||||
expect(wrapper.find('#link-type-success').props().isSelected).toBe(false);
|
||||
expect(wrapper.find('#link-type-failure').props().isSelected).toBe(false);
|
||||
expect(wrapper.find('#link-type-always').props().isSelected).toBe(true);
|
||||
});
|
||||
|
||||
test('Clicking success card makes expected callback', () => {
|
||||
wrapper.find('#link-type-success').simulate('click');
|
||||
expect(onUpdateLinkType).toHaveBeenCalledWith('success');
|
||||
});
|
||||
|
||||
test('Clicking failure card makes expected callback', () => {
|
||||
wrapper.find('#link-type-failure').simulate('click');
|
||||
expect(onUpdateLinkType).toHaveBeenCalledWith('failure');
|
||||
});
|
||||
|
||||
test('Clicking always card makes expected callback', () => {
|
||||
wrapper.find('#link-type-always').simulate('click');
|
||||
expect(onUpdateLinkType).toHaveBeenCalledWith('always');
|
||||
});
|
||||
});
|
||||
@ -1,7 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
WorkflowDispatchContext,
|
||||
} from '@contexts/Workflow';
|
||||
import { WorkflowDispatchContext } from '@contexts/Workflow';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import UnsavedChangesModal from './UnsavedChangesModal';
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user