mirror of
https://github.com/ansible/awx.git
synced 2026-05-11 03:17:38 -02:30
Adds test coverage for node modal components
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { WorkflowDispatchContext } from '@contexts/Workflow';
|
||||||
WorkflowDispatchContext,
|
|
||||||
} from '@contexts/Workflow';
|
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import DeleteAllNodesModal from './DeleteAllNodesModal';
|
import DeleteAllNodesModal from './DeleteAllNodesModal';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import { WorkflowDispatchContext, WorkflowStateContext } from '@contexts/Workflow';
|
import {
|
||||||
|
WorkflowDispatchContext,
|
||||||
|
WorkflowStateContext,
|
||||||
|
} from '@contexts/Workflow';
|
||||||
import LinkAddModal from './LinkAddModal';
|
import LinkAddModal from './LinkAddModal';
|
||||||
|
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
const workflowContext = {
|
const workflowContext = {
|
||||||
linkToEdit: null
|
linkToEdit: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('LinkAddModal', () => {
|
describe('LinkAddModal', () => {
|
||||||
@@ -17,8 +20,11 @@ describe('LinkAddModal', () => {
|
|||||||
<LinkAddModal />
|
<LinkAddModal />
|
||||||
</WorkflowStateContext.Provider>
|
</WorkflowStateContext.Provider>
|
||||||
</WorkflowDispatchContext.Provider>
|
</WorkflowDispatchContext.Provider>
|
||||||
);
|
);
|
||||||
wrapper.find('button#link-confirm').simulate('click');
|
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`)}
|
{i18n._(t`Remove`)}
|
||||||
</Button>,
|
</Button>,
|
||||||
<Button
|
<Button
|
||||||
id="cancel-link-removal"
|
id="cancel-link-removal"
|
||||||
aria-label={i18n._(t`Cancel link removal`)}
|
aria-label={i18n._(t`Cancel link removal`)}
|
||||||
key="cancel"
|
key="cancel"
|
||||||
onClick={() => dispatch({ type: 'SET_LINK_TO_DELETE', value: null })}
|
onClick={() => dispatch({ type: 'SET_LINK_TO_DELETE', value: null })}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
WorkflowDispatchContext,
|
WorkflowDispatchContext,
|
||||||
WorkflowStateContext
|
WorkflowStateContext,
|
||||||
} from '@contexts/Workflow';
|
} from '@contexts/Workflow';
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import LinkDeleteModal from './LinkDeleteModal';
|
import LinkDeleteModal from './LinkDeleteModal';
|
||||||
@@ -12,13 +12,13 @@ const dispatch = jest.fn();
|
|||||||
const workflowContext = {
|
const workflowContext = {
|
||||||
linkToDelete: {
|
linkToDelete: {
|
||||||
source: {
|
source: {
|
||||||
id: 2
|
id: 2,
|
||||||
},
|
},
|
||||||
target: {
|
target: {
|
||||||
id: 3
|
id: 3,
|
||||||
},
|
},
|
||||||
linkType: 'always'
|
linkType: 'always',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('LinkDeleteModal', () => {
|
describe('LinkDeleteModal', () => {
|
||||||
@@ -54,7 +54,8 @@ describe('LinkDeleteModal', () => {
|
|||||||
test('Close button dispatches as expected', () => {
|
test('Close button dispatches as expected', () => {
|
||||||
wrapper.find('TimesIcon').simulate('click');
|
wrapper.find('TimesIcon').simulate('click');
|
||||||
expect(dispatch).toHaveBeenCalledWith({
|
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 React from 'react';
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import { WorkflowDispatchContext, WorkflowStateContext } from '@contexts/Workflow';
|
import {
|
||||||
|
WorkflowDispatchContext,
|
||||||
|
WorkflowStateContext,
|
||||||
|
} from '@contexts/Workflow';
|
||||||
import LinkEditModal from './LinkEditModal';
|
import LinkEditModal from './LinkEditModal';
|
||||||
|
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
@@ -8,13 +11,13 @@ const dispatch = jest.fn();
|
|||||||
const workflowContext = {
|
const workflowContext = {
|
||||||
linkToEdit: {
|
linkToEdit: {
|
||||||
source: {
|
source: {
|
||||||
id: 2
|
id: 2,
|
||||||
},
|
},
|
||||||
target: {
|
target: {
|
||||||
id: 3
|
id: 3,
|
||||||
},
|
},
|
||||||
linkType: 'always'
|
linkType: 'always',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('LinkEditModal', () => {
|
describe('LinkEditModal', () => {
|
||||||
@@ -25,8 +28,11 @@ describe('LinkEditModal', () => {
|
|||||||
<LinkEditModal />
|
<LinkEditModal />
|
||||||
</WorkflowStateContext.Provider>
|
</WorkflowStateContext.Provider>
|
||||||
</WorkflowDispatchContext.Provider>
|
</WorkflowDispatchContext.Provider>
|
||||||
);
|
);
|
||||||
wrapper.find('button#link-confirm').simulate('click');
|
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 dispatch = useContext(WorkflowDispatchContext);
|
||||||
const { addNodeSource } = useContext(WorkflowStateContext);
|
const { addNodeSource } = useContext(WorkflowStateContext);
|
||||||
|
|
||||||
const addNode = (linkType, resource, nodeType) => {
|
const addNode = (resource, linkType) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'CREATE_NODE',
|
type: 'CREATE_NODE',
|
||||||
node: {
|
node: {
|
||||||
linkType,
|
linkType,
|
||||||
nodeResource: resource,
|
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 })}
|
onClose={() => dispatch({ type: 'SET_NODE_TO_DELETE', value: null })}
|
||||||
actions={[
|
actions={[
|
||||||
<Button
|
<Button
|
||||||
|
id="confirm-node-removal"
|
||||||
key="remove"
|
key="remove"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
aria-label={i18n._(t`Confirm node removal`)}
|
aria-label={i18n._(t`Confirm node removal`)}
|
||||||
@@ -27,6 +28,7 @@ function NodeDeleteModal({ i18n }) {
|
|||||||
{i18n._(t`Remove`)}
|
{i18n._(t`Remove`)}
|
||||||
</Button>,
|
</Button>,
|
||||||
<Button
|
<Button
|
||||||
|
id="cancel-node-removal"
|
||||||
key="cancel"
|
key="cancel"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
aria-label={i18n._(t`Cancel node removal`)}
|
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 }) {
|
function NodeEditModal({ i18n }) {
|
||||||
const dispatch = useContext(WorkflowDispatchContext);
|
const dispatch = useContext(WorkflowDispatchContext);
|
||||||
|
|
||||||
const updateNode = (linkType, resource, nodeType) => {
|
const updateNode = resource => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'UPDATE_NODE',
|
type: 'UPDATE_NODE',
|
||||||
node: {
|
node: {
|
||||||
linkType,
|
|
||||||
nodeResource: resource,
|
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;
|
: nodeResource;
|
||||||
|
|
||||||
onSave(linkType, resource, nodeType);
|
onSave(resource, askLinkType ? linkType : null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@@ -177,11 +177,15 @@ function NodeModal({ askLinkType, i18n, onSave, title }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{activeStep && activeStep.id !== 1 && (
|
{activeStep && activeStep.id !== 1 && (
|
||||||
<Button variant="secondary" onClick={onBack}>
|
<Button id="back-node-modal" variant="secondary" onClick={onBack}>
|
||||||
{i18n._(t`Back`)}
|
{i18n._(t`Back`)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button variant="link" onClick={handleCancel}>
|
<Button
|
||||||
|
id="cancel-node-modal"
|
||||||
|
variant="link"
|
||||||
|
onClick={handleCancel}
|
||||||
|
>
|
||||||
{i18n._(t`Cancel`)}
|
{i18n._(t`Cancel`)}
|
||||||
</Button>
|
</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 (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
id="next-node-modal"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={() => onClick(activeStep)}
|
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;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
TimeoutInput.displayName = 'TimeoutInput';
|
||||||
|
|
||||||
const TimeoutLabel = styled.p`
|
const TimeoutLabel = styled.p`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
@@ -125,12 +126,12 @@ function NodeTypeStep({
|
|||||||
timeoutMinutes: Math.floor(timeout / 60),
|
timeoutMinutes: Math.floor(timeout / 60),
|
||||||
timeoutSeconds: timeout - Math.floor(timeout / 60) * 60,
|
timeoutSeconds: timeout - Math.floor(timeout / 60) * 60,
|
||||||
}}
|
}}
|
||||||
render={() => (
|
>
|
||||||
|
{() => (
|
||||||
<Form css="margin-top: 20px;">
|
<Form css="margin-top: 20px;">
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Field
|
<Field name="name">
|
||||||
name="name"
|
{({ field, form }) => {
|
||||||
render={({ field, form }) => {
|
|
||||||
const isValid =
|
const isValid =
|
||||||
form &&
|
form &&
|
||||||
(!form.touched[field.name] || !form.errors[field.name]);
|
(!form.touched[field.name] || !form.errors[field.name]);
|
||||||
@@ -150,19 +151,18 @@ function NodeTypeStep({
|
|||||||
type="text"
|
type="text"
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(value, evt) => {
|
onChange={(value, evt) => {
|
||||||
onUpdateName(value);
|
onUpdateName(evt.target.value);
|
||||||
field.onChange(evt);
|
field.onChange(evt);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
</Field>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Field
|
<Field name="description">
|
||||||
name="description"
|
{({ field }) => (
|
||||||
render={({ field }) => (
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="approval-description"
|
fieldId="approval-description"
|
||||||
label={i18n._(t`Description`)}
|
label={i18n._(t`Description`)}
|
||||||
@@ -172,13 +172,13 @@ function NodeTypeStep({
|
|||||||
type="text"
|
type="text"
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(value, evt) => {
|
onChange={(value, evt) => {
|
||||||
onUpdateDescription(value);
|
onUpdateDescription(evt.target.value);
|
||||||
field.onChange(evt);
|
field.onChange(evt);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
/>
|
</Field>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -186,9 +186,8 @@ function NodeTypeStep({
|
|||||||
fieldId="approval-timeout"
|
fieldId="approval-timeout"
|
||||||
>
|
>
|
||||||
<div css="display: flex;align-items: center;">
|
<div css="display: flex;align-items: center;">
|
||||||
<Field
|
<Field name="timeoutMinutes">
|
||||||
name="timeoutMinutes"
|
{({ field, form }) => (
|
||||||
render={({ field, form }) => (
|
|
||||||
<>
|
<>
|
||||||
<TimeoutInput
|
<TimeoutInput
|
||||||
id="approval-timeout-minutes"
|
id="approval-timeout-minutes"
|
||||||
@@ -197,11 +196,14 @@ function NodeTypeStep({
|
|||||||
step="1"
|
step="1"
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(value, evt) => {
|
onChange={(value, evt) => {
|
||||||
if (!value || value === '') {
|
if (
|
||||||
value = 0;
|
!evt.target.value ||
|
||||||
|
evt.target.value === ''
|
||||||
|
) {
|
||||||
|
evt.target.value = 0;
|
||||||
}
|
}
|
||||||
onUpdateTimeout(
|
onUpdateTimeout(
|
||||||
Number(value) * 60 +
|
Number(evt.target.value) * 60 +
|
||||||
Number(form.values.timeoutSeconds)
|
Number(form.values.timeoutSeconds)
|
||||||
);
|
);
|
||||||
field.onChange(evt);
|
field.onChange(evt);
|
||||||
@@ -212,10 +214,9 @@ function NodeTypeStep({
|
|||||||
</TimeoutLabel>
|
</TimeoutLabel>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
</Field>
|
||||||
<Field
|
<Field name="timeoutSeconds">
|
||||||
name="timeoutSeconds"
|
{({ field, form }) => (
|
||||||
render={({ field, form }) => (
|
|
||||||
<>
|
<>
|
||||||
<TimeoutInput
|
<TimeoutInput
|
||||||
id="approval-timeout-seconds"
|
id="approval-timeout-seconds"
|
||||||
@@ -224,11 +225,14 @@ function NodeTypeStep({
|
|||||||
step="1"
|
step="1"
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(value, evt) => {
|
onChange={(value, evt) => {
|
||||||
if (!value || value === '') {
|
if (
|
||||||
value = 0;
|
!evt.target.value ||
|
||||||
|
evt.target.value === ''
|
||||||
|
) {
|
||||||
|
evt.target.value = 0;
|
||||||
}
|
}
|
||||||
onUpdateTimeout(
|
onUpdateTimeout(
|
||||||
Number(value) +
|
Number(evt.target.value) +
|
||||||
Number(form.values.timeoutMinutes) * 60
|
Number(form.values.timeoutMinutes) * 60
|
||||||
);
|
);
|
||||||
field.onChange(evt);
|
field.onChange(evt);
|
||||||
@@ -239,13 +243,13 @@ function NodeTypeStep({
|
|||||||
</TimeoutLabel>
|
</TimeoutLabel>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Form>
|
</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>
|
</p>
|
||||||
<Grid>
|
<Grid>
|
||||||
<SelectableCard
|
<SelectableCard
|
||||||
|
id="link-type-success"
|
||||||
isSelected={linkType === 'success'}
|
isSelected={linkType === 'success'}
|
||||||
label={i18n._(t`On Success`)}
|
label={i18n._(t`On Success`)}
|
||||||
description={i18n._(
|
description={i18n._(
|
||||||
@@ -37,6 +38,7 @@ function RunStep({ i18n, linkType, onUpdateLinkType }) {
|
|||||||
onClick={() => onUpdateLinkType('success')}
|
onClick={() => onUpdateLinkType('success')}
|
||||||
/>
|
/>
|
||||||
<SelectableCard
|
<SelectableCard
|
||||||
|
id="link-type-failure"
|
||||||
isSelected={linkType === 'failure'}
|
isSelected={linkType === 'failure'}
|
||||||
label={i18n._(t`On Failure`)}
|
label={i18n._(t`On Failure`)}
|
||||||
description={i18n._(
|
description={i18n._(
|
||||||
@@ -45,6 +47,7 @@ function RunStep({ i18n, linkType, onUpdateLinkType }) {
|
|||||||
onClick={() => onUpdateLinkType('failure')}
|
onClick={() => onUpdateLinkType('failure')}
|
||||||
/>
|
/>
|
||||||
<SelectableCard
|
<SelectableCard
|
||||||
|
id="link-type-always"
|
||||||
isSelected={linkType === 'always'}
|
isSelected={linkType === 'always'}
|
||||||
label={i18n._(t`Always`)}
|
label={i18n._(t`Always`)}
|
||||||
description={i18n._(
|
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 React from 'react';
|
||||||
import {
|
import { WorkflowDispatchContext } from '@contexts/Workflow';
|
||||||
WorkflowDispatchContext,
|
|
||||||
} from '@contexts/Workflow';
|
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import UnsavedChangesModal from './UnsavedChangesModal';
|
import UnsavedChangesModal from './UnsavedChangesModal';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user