diff options
Diffstat (limited to 'gui-clamp/ui-react/src/components/dialogs/Loop')
14 files changed, 1920 insertions, 0 deletions
diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js new file mode 100644 index 0000000..690dcbb --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.js @@ -0,0 +1,193 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +import React from 'react' +import Select from 'react-select'; +import Button from 'react-bootstrap/Button'; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import Row from 'react-bootstrap/Row'; +import Col from 'react-bootstrap/Col'; +import styled from 'styled-components'; +import LoopService from '../../../api/LoopService'; +import TemplateService from '../../../api/TemplateService'; +import LoopCache from '../../../api/LoopCache'; +import SvgGenerator from '../../loop_viewer/svg/SvgGenerator'; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` + +const ErrMsgStyled = styled.div` + color: red; +` + +export default class CreateLoopModal extends React.Component { + constructor(props, context) { + super(props, context); + + this.getAllLoopTemplates = this.getAllLoopTemplates.bind(this); + this.handleCreate = this.handleCreate.bind(this); + this.handleModelName = this.handleModelName.bind(this); + this.handleClose = this.handleClose.bind(this); + this.handleDropDownListChange = this.handleDropDownListChange.bind(this); + this.renderSvg = this.renderSvg.bind(this); + this.state = { + show: true, + chosenTemplateName: '', + modelInputErrMsg: '', + modelName: '', + templateNames: [], + fakeLoopCacheWithTemplate: new LoopCache({}) + }; + } + + async componentDidMount() { + await this.getAllLoopTemplates(); + await this.getModelNames(); + } + + handleClose() { + this.setState({ show: false }); + this.props.history.push('/'); + } + + handleDropDownListChange(e) { + if (typeof e.value !== "undefined") { + this.setState({ + fakeLoopCacheWithTemplate: + new LoopCache({ + "loopTemplate": e.templateObject, + "name": "fakeLoop" + }), + chosenTemplateName: e.value + }) + } else { + this.setState({ fakeLoopCacheWithTemplate: new LoopCache({}) }) + } + } + + getAllLoopTemplates() { + TemplateService.getAllLoopTemplates().then(templatesData => { + const templateOptions = templatesData.map((templateData) => { + return { label: templateData.name, value: templateData.name, templateObject: templateData } + }); + this.setState({ + templateNames: templateOptions + }) + }); + } + + getModelNames() { + TemplateService.getLoopNames().then(loopNames => { + if (!loopNames) { + loopNames = []; + } + // Remove LOOP_ prefix + let trimmedLoopNames = loopNames.map(str => str.replace('LOOP_', '')); + this.setState({ modelNames: trimmedLoopNames }); + }); + } + + handleCreate() { + if (!this.state.modelName) { + alert("A model name is required"); + return; + } + console.debug("Create Model " + this.state.modelName + ", Template " + this.state.chosenTemplateName + " is chosen"); + this.setState({ show: false }); + LoopService.createLoop("LOOP_" + this.state.modelName, this.state.chosenTemplateName).then(text => { + console.debug("CreateLoop response received: ", text); + try { + this.props.history.push('/'); + this.props.loadLoopFunction("LOOP_" + this.state.modelName); + } catch (err) { + alert(text); + this.props.history.push('/'); + } + }) + .catch(error => { + console.debug("Create Loop failed"); + }); + } + + handleModelName(event) { + if (this.state.modelNames.includes(event.target.value)) { + this.setState({ + modelInputErrMsg: 'A model named "' + event.target.value + '" already exists. Please pick another name.', + modelName: event.target.value + }); + return; + } else { + this.setState({ + modelInputErrMsg: '', + modelName: event.target.value + }); + } + } + + renderSvg() { + return ( + <SvgGenerator loopCache={ this.state.fakeLoopCacheWithTemplate } clickable={ false } generatedFrom={ SvgGenerator.GENERATED_FROM_TEMPLATE }/> + ); + } + + render() { + return ( + <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Create Model</Modal.Title> + </Modal.Header> + <Modal.Body> + <Form.Group as={ Row } controlId="formPlaintextEmail"> + <Form.Label column sm="2">Template Name:</Form.Label> + <Col sm="10"> + <Select onChange={ this.handleDropDownListChange } options={ this.state.templateNames }/> + </Col> + </Form.Group> + <Form.Group as={ Row } style={ { alignItems: 'center' } } controlId="formSvgPreview"> + <Form.Label column sm="2">Model Preview:</Form.Label> + <Col sm="10"> + { this.renderSvg() } + </Col> + </Form.Group> + <Form.Group as={ Row } controlId="formPlaintextEmail"> + <Form.Label column sm="2">Model Name:</Form.Label> + <input sm="5" type="text" style={ { width: '50%', marginLeft: '1em' } } + value={ this.state.modelName } + onChange={ this.handleModelName } + /> + <span sm="5"/> + </Form.Group> + <Form.Group as={ Row } controlId="formPlaintextEmail"> + <Form.Label column sm="2"> </Form.Label> + <ErrMsgStyled>{ this.state.modelInputErrMsg }</ErrMsgStyled> + </Form.Group> + </Modal.Body> + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button> + <Button variant="primary" type="submit" onClick={ this.handleCreate }>Create</Button> + </Modal.Footer> + </ModalStyled> + ); + } +} diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js new file mode 100644 index 0000000..401bb6a --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js @@ -0,0 +1,144 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import CreateLoopModal from './CreateLoopModal'; +import LoopService from '../../../api/LoopService'; +import TemplateService from '../../../api/TemplateService'; + +describe('Verify CreateLoopModal', () => { + + it('Test the render method', async () => { + const flushPromises = () => new Promise(setImmediate); + TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => { + return Promise.resolve([{ "name": "template1" }, { "name": "template2" }]); + }); + TemplateService.getLoopNames = jest.fn().mockImplementation(() => { + return Promise.resolve([]); + }); + + const component = shallow(<CreateLoopModal/>); + expect(component).toMatchSnapshot(); + await flushPromises(); + component.update(); + expect(component.state('templateNames')).toStrictEqual([{ "label": "template1", "value": "template1", "templateObject": { "name": "template1" } }, { + "label": "template2", + "value": "template2", + "templateObject": { "name": "template2" } + }]); + }); + + it('handleDropdownListChange event', async () => { + const flushPromises = () => new Promise(setImmediate); + + const component = shallow(<CreateLoopModal/>); + component.find('StateManager').simulate('change', { value: 'template1', templateObject: { "name": "template1" } }); + await flushPromises(); + component.update(); + expect(component.state('chosenTemplateName')).toEqual("template1"); + expect(component.state('fakeLoopCacheWithTemplate').getLoopTemplate()['name']).toEqual("template1"); + expect(component.state('fakeLoopCacheWithTemplate').getLoopName()).toEqual("fakeLoop"); + + component.find('StateManager').simulate('change', { value: 'template2', templateObject: { "name": "template2" } }); + await flushPromises(); + component.update(); + expect(component.state('chosenTemplateName')).toEqual("template2"); + expect(component.state('fakeLoopCacheWithTemplate').getLoopTemplate()['name']).toEqual("template2"); + expect(component.state('fakeLoopCacheWithTemplate').getLoopName()).toEqual("fakeLoop"); + }); + + it('handleModelName event', async () => { + const flushPromises = () => new Promise(setImmediate); + TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => { + return Promise.resolve([{ "name": "template1" }, { "name": "template2" }]); + }); + TemplateService.getLoopNames = jest.fn().mockImplementation(() => { + return Promise.resolve([]); + }); + const event = { target: { value: "model1" } }; + const component = shallow(<CreateLoopModal/>); + await flushPromises(); + component.find('input').simulate('change', event); + component.update(); + expect(component.state('modelName')).toEqual("model1"); + }); + + it('Test handleClose', () => { + const historyMock = { push: jest.fn() }; + const handleClose = jest.spyOn(CreateLoopModal.prototype, 'handleClose'); + const component = shallow(<CreateLoopModal history={ historyMock }/>) + + component.find('[variant="secondary"]').prop('onClick')(); + + expect(handleClose).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + + handleClose.mockClear(); + }); + + it('Test handleCreate Fail', () => { + const handleCreate = jest.spyOn(CreateLoopModal.prototype, 'handleCreate'); + const component = shallow(<CreateLoopModal/>) + + component.find('[variant="primary"]').prop('onClick')(); + + expect(handleCreate).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(true); + + handleCreate.mockClear(); + }); + + it('Test handleCreate Suc', async () => { + const flushPromises = () => new Promise(setImmediate); + const historyMock = { push: jest.fn() }; + const loadLoopFunction = jest.fn(); + + LoopService.createLoop = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + json: () => { + } + }); + }); + + const handleCreate = jest.spyOn(CreateLoopModal.prototype, 'handleCreate'); + const component = shallow(<CreateLoopModal history={ historyMock } loadLoopFunction={ loadLoopFunction }/>) + component.setState({ + modelName: "modelNameTest", + chosenTemplateName: "template1" + }); + + component.find('[variant="primary"]').prop('onClick')(); + await flushPromises(); + component.update(); + + expect(handleCreate).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + + handleCreate.mockClear(); + }); + +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js new file mode 100644 index 0000000..b7ab8a5 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.js @@ -0,0 +1,183 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import LoopActionService from '../../../api/LoopActionService'; +import LoopService from '../../../api/LoopService'; +import Button from 'react-bootstrap/Button'; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import Tabs from 'react-bootstrap/Tabs'; +import Tab from 'react-bootstrap/Tab'; +import styled from 'styled-components'; +import Spinner from 'react-bootstrap/Spinner' + +const StyledSpinnerDiv = styled.div` + justify-content: center !important; + display: flex !important; +`; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` +const FormStyled = styled(Form.Group)` + padding: .25rem 1.5rem; +` +export default class DeployLoopModal extends React.Component { + + constructor(props, context) { + super(props, context); + + this.handleSave = this.handleSave.bind(this); + this.handleClose = this.handleClose.bind(this); + this.handleChange = this.handleChange.bind(this); + this.refreshStatus = this.refreshStatus.bind(this); + this.renderDeployParam = this.renderDeployParam.bind(this); + this.renderSpinner = this.renderSpinner.bind(this); + + const propertiesJson = JSON.parse(JSON.stringify(this.props.loopCache.getGlobalProperties())); + this.state = { + loopCache: this.props.loopCache, + temporaryPropertiesJson: propertiesJson, + show: true, + key: this.getInitialKeyValue(propertiesJson) + }; + } + + getInitialKeyValue(temporaryPropertiesJson) { + const deployJsonList = temporaryPropertiesJson["dcaeDeployParameters"]; + let initialKey; + Object.keys(deployJsonList) + .filter((obj) => Object.keys(deployJsonList).indexOf(obj) === 0) + .map(obj => + initialKey = obj + ); + return initialKey; + } + + componentWillReceiveProps(newProps) { + this.setState({ + loopName: newProps.loopCache.getLoopName(), + show: true + }); + } + + handleClose() { + this.setState({ show: false }); + this.props.history.push('/'); + } + + renderSpinner() { + if (this.state.deploying) { + return ( + <StyledSpinnerDiv> + <Spinner animation="border" role="status"> + <span className="sr-only">Loading...</span> + </Spinner> + </StyledSpinnerDiv> + ); + } else { + return (<div></div>); + } + } + + handleSave() { + const loopName = this.props.loopCache.getLoopName(); + // save the global propserties + this.setState({ deploying: true }); + LoopService.updateGlobalProperties(loopName, this.state.temporaryPropertiesJson).then(resp => { + LoopActionService.performAction(loopName, "deploy").then(pars => { + this.props.showSucAlert("Action deploy successfully performed"); + // refresh status and update loop logs + this.refreshStatus(loopName); + }) + .catch(error => { + this.props.showFailAlert("Action deploy failed"); + // refresh status and update loop logs + this.refreshStatus(loopName); + }); + }); + } + + refreshStatus(loopName) { + LoopActionService.refreshStatus(loopName).then(data => { + this.props.updateLoopFunction(data); + this.setState({ show: false, deploying: false }); + this.props.history.push('/'); + }) + .catch(error => { + this.props.showFailAlert("Refresh status failed"); + this.setState({ show: false, deploying: false }); + this.props.history.push('/'); + }); + } + + handleChange(event) { + let deploymentParam = this.state.temporaryPropertiesJson["dcaeDeployParameters"]; + deploymentParam[this.state.key][event.target.name] = event.target.value; + + this.setState({ temporaryPropertiesJson: { dcaeDeployParameters: deploymentParam } }); + } + + renderDeployParamTabs() { + if (typeof (this.state.temporaryPropertiesJson) === "undefined") { + return ""; + } + + const deployJsonList = this.state.temporaryPropertiesJson["dcaeDeployParameters"]; + var indents = []; + Object.keys(deployJsonList).map((item, key) => + indents.push(<Tab eventKey={ item } title={ item }> + { this.renderDeployParam(deployJsonList[item]) } + </Tab>) + ); + return indents; + } + + renderDeployParam(deployJson) { + var indents = []; + Object.keys(deployJson).map((item, key) => + indents.push(<FormStyled> + <Form.Label>{ item }</Form.Label> + <Form.Control type="text" name={ item } onChange={ this.handleChange } defaultValue={ deployJson[item] }></Form.Control> + </FormStyled>)); + return indents; + } + + render() { + return ( + <ModalStyled size="lg" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Deployment parameters</Modal.Title> + </Modal.Header> + <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key }) }> + { this.renderDeployParamTabs() } + </Tabs> + { this.renderSpinner() } + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button> + <Button variant="primary" type="submit" onClick={ this.handleSave }>Deploy</Button> + </Modal.Footer> + </ModalStyled> + ); + } +} diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js new file mode 100644 index 0000000..0c68f23 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js @@ -0,0 +1,114 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import DeployLoopModal from './DeployLoopModal'; +import LoopCache from '../../../api/LoopCache'; +import LoopActionService from '../../../api/LoopActionService'; +import LoopService from '../../../api/LoopService'; + +describe('Verify DeployLoopModal', () => { + const loopCache = new LoopCache({ + "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca", + "globalPropertiesJson": { + "dcaeDeployParameters": { + "testMs": { + "location_id": "", + "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + } + } + } + }); + + it('Test the render method', () => { + const component = shallow( + <DeployLoopModal loopCache={ loopCache }/> + ) + + expect(component).toMatchSnapshot(); + }); + + it('Test handleClose', () => { + const historyMock = { push: jest.fn() }; + const handleClose = jest.spyOn(DeployLoopModal.prototype, 'handleClose'); + const component = shallow(<DeployLoopModal history={ historyMock } loopCache={ loopCache }/>) + + component.find('[variant="secondary"]').prop('onClick')(); + + expect(handleClose).toHaveBeenCalledTimes(1); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + }); + + it('Test handleSave successful', async () => { + const flushPromises = () => new Promise(setImmediate); + const historyMock = { push: jest.fn() }; + const updateLoopFunction = jest.fn(); + const showSucAlert = jest.fn(); + const showFailAlert = jest.fn(); + const handleSave = jest.spyOn(DeployLoopModal.prototype, 'handleSave'); + LoopService.updateGlobalProperties = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + text: () => "OK" + }); + }); + LoopActionService.performAction = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + json: () => { + } + }); + }); + LoopActionService.refreshStatus = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + json: () => { + } + }); + }); + + const component = shallow(<DeployLoopModal history={ historyMock } + loopCache={ loopCache } updateLoopFunction={ updateLoopFunction } showSucAlert={ showSucAlert } showFailAlert={ showFailAlert }/>) + + component.find('[variant="primary"]').prop('onClick')(); + await flushPromises(); + component.update(); + + expect(handleSave).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + handleSave.mockClear(); + }); + + it('Onchange event', () => { + const event = { target: { name: "location_id", value: "testLocation" } }; + const component = shallow(<DeployLoopModal loopCache={ loopCache }/>); + + component.find('[name="location_id"]').simulate('change', event); + component.update(); + expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.testMs.location_id).toEqual("testLocation"); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js new file mode 100644 index 0000000..6917713 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.js @@ -0,0 +1,118 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +import React from 'react' +import Button from 'react-bootstrap/Button'; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import styled from 'styled-components'; +import LoopService from '../../../api/LoopService'; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` +export default class LoopPropertiesModal extends React.Component { + + state = { + show: true, + loopCache: this.props.loopCache, + temporaryPropertiesJson: JSON.parse(JSON.stringify(this.props.loopCache.getGlobalProperties())) + }; + + constructor(props, context) { + super(props, context); + + this.handleClose = this.handleClose.bind(this); + this.handleSave = this.handleSave.bind(this); + this.handleChange = this.handleChange.bind(this); + + this.renderDcaeParameters = this.renderDcaeParameters.bind(this); + this.renderAllParameters = this.renderAllParameters.bind(this); + this.getDcaeParameters = this.getDcaeParameters.bind(this); + this.readOnly = props.readOnly !== undefined ? props.readOnly : false; + } + + componentWillReceiveProps(newProps) { + this.setState({ + loopCache: newProps.loopCache, + temporaryPropertiesJson: JSON.parse(JSON.stringify(newProps.loopCache.getGlobalProperties())) + }); + } + + handleClose() { + this.props.history.push('/'); + } + + handleSave(event) { + LoopService.updateGlobalProperties(this.state.loopCache.getLoopName(), this.state.temporaryPropertiesJson).then(resp => { + this.setState({ show: false }); + this.props.history.push('/'); + this.props.loadLoopFunction(this.state.loopCache.getLoopName()); + }); + } + + handleChange(event) { + this.setState({ temporaryPropertiesJson: { [event.target.name]: JSON.parse(event.target.value) } }); + } + + renderAllParameters() { + return (<Modal.Body> + <Form> + { this.renderDcaeParameters() } + </Form> + </Modal.Body> + ); + } + + getDcaeParameters() { + if (typeof (this.state.temporaryPropertiesJson) !== "undefined") { + return JSON.stringify(this.state.temporaryPropertiesJson["dcaeDeployParameters"]); + } else { + return ""; + } + + } + + renderDcaeParameters() { + return ( + <Form.Group> + <Form.Label>Deploy Parameters</Form.Label> + <Form.Control as="textarea" rows="3" name="dcaeDeployParameters" onChange={ this.handleChange } defaultValue={ this.getDcaeParameters() }></Form.Control> + </Form.Group> + ); + } + + render() { + return ( + <ModalStyled size="lg" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Model Properties</Modal.Title> + </Modal.Header> + { this.renderAllParameters() } + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button> + <Button variant="primary" type="submit" disabled={ this.readOnly } onClick={ this.handleSave }>Save Changes</Button> + </Modal.Footer> + </ModalStyled> + ); + } +} diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js new file mode 100644 index 0000000..a9c5903 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/LoopPropertiesModal.test.js @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import LoopPropertiesModal from './LoopPropertiesModal'; +import LoopCache from '../../../api/LoopCache'; +import LoopService from '../../../api/LoopService'; + +describe('Verify LoopPropertiesModal', () => { + const loopCache = new LoopCache({ + "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca", + "globalPropertiesJson": { + "dcaeDeployParameters": { + "location_id": "", + "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + } + } + }); + + it('Test the render method', () => { + const component = shallow( + <LoopPropertiesModal loopCache={ loopCache }/> + ) + component.setState({ + show: true, + temporaryPropertiesJson: { + "dcaeDeployParameters": { + "location_id": "", + "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + } + } + }); + + expect(component.state('temporaryPropertiesJson')).toEqual({ + "dcaeDeployParameters": { + "location_id": "", + "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + } + }); + expect(component.state('show')).toEqual(true); + + expect(component).toMatchSnapshot(); + }); + + it('Test handleClose', () => { + const historyMock = { push: jest.fn() }; + const handleClose = jest.spyOn(LoopPropertiesModal.prototype, 'handleClose'); + const component = shallow(<LoopPropertiesModal history={ historyMock } loopCache={ loopCache }/>) + + component.find('[variant="secondary"]').prop('onClick')(); + + expect(handleClose).toHaveBeenCalledTimes(1); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + }); + + it('Test handleSave successful', async () => { + const flushPromises = () => new Promise(setImmediate); + const historyMock = { push: jest.fn() }; + const loadLoopFunction = jest.fn(); + const handleSave = jest.spyOn(LoopPropertiesModal.prototype, 'handleSave'); + LoopService.updateGlobalProperties = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + text: () => "OK" + }); + }); + + const component = shallow(<LoopPropertiesModal history={ historyMock } + loopCache={ loopCache } loadLoopFunction={ loadLoopFunction }/>) + + component.find('[variant="primary"]').prop('onClick')(); + await flushPromises(); + component.update(); + + expect(handleSave).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + }); + + it('Onchange event', () => { + const event = { target: { name: "dcaeDeployParameters", value: "{\"location_id\": \"testLocation\",\"policy_id\": \"TCA_h2NMX_v1_0_ResourceInstanceName1_tca\"}" } }; + const component = shallow(<LoopPropertiesModal loopCache={ loopCache }/>); + + component.find('FormControl').simulate('change', event); + component.update(); + + expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.location_id).toEqual("testLocation"); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js new file mode 100644 index 0000000..8f8b74d --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.js @@ -0,0 +1,269 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +import React, { forwardRef } from 'react' +import MaterialTable from "material-table"; +import Button from 'react-bootstrap/Button'; +import Modal from 'react-bootstrap/Modal'; +import styled from 'styled-components'; +import PolicyToscaService from '../../../api/PolicyToscaService'; +import ArrowUpward from '@material-ui/icons/ArrowUpward'; +import ChevronLeft from '@material-ui/icons/ChevronLeft'; +import ChevronRight from '@material-ui/icons/ChevronRight'; +import Clear from '@material-ui/icons/Clear'; +import FirstPage from '@material-ui/icons/FirstPage'; +import LastPage from '@material-ui/icons/LastPage'; +import Search from '@material-ui/icons/Search'; +import LoopService from '../../../api/LoopService'; +import Tabs from 'react-bootstrap/Tabs'; +import Tab from 'react-bootstrap/Tab'; +import Alert from 'react-bootstrap/Alert'; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` +const TextModal = styled.textarea` + margin-top: 20px; + white-space: pre; + background-color: ${ props => props.theme.toscaTextareaBackgroundColor }; + text-align: justify; + font-size: ${ props => props.theme.toscaTextareaFontSize }; + width: 100%; + height: 300px; +` +const cellStyle = { border: '1px solid black' }; +const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' }; +const rowHeaderStyle = { backgroundColor: '#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black' }; + +export default class ModifyLoopModal extends React.Component { + + state = { + show: true, + loopCache: this.props.loopCache, + content: 'Please select Tosca model to view the details', + selectedRowData: {}, + toscaPolicyModelsData: [], + selectedPolicyModelsData: [], + key: 'add', + showFailAlert: false, + toscaColumns: [ + { + title: "#", field: "index", render: rowData => rowData.tableData.id + 1, + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Policy Model Type", field: "policyModelType", + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Policy Acronym", field: "policyAcronym", + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Policy Name", field: "policyName", + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Version", field: "version", + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Uploaded By", field: "updatedBy", + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Uploaded Date", field: "updatedDate", editable: 'never', + cellStyle: cellStyle, + headerStyle: headerStyle + }, + { + title: "Created Date", field: "createdDate", editable: 'never', + cellStyle: cellStyle, + headerStyle: headerStyle + } + ], + tableIcons: { + FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>), + LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>), + NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>), + PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>), + ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>), + Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>), + SortArrow: forwardRef((props, ref) => <ArrowUpward { ...props } ref={ ref }/>) + } + }; + + constructor(props, context) { + super(props, context); + this.handleClose = this.handleClose.bind(this); + this.initializeToscaPolicyModelsInfo = this.initializeToscaPolicyModelsInfo.bind(this); + this.handleYamlContent = this.handleYamlContent.bind(this); + this.getToscaPolicyModelYaml = this.getToscaPolicyModelYaml.bind(this); + this.handleAdd = this.handleAdd.bind(this); + this.handleRemove = this.handleRemove.bind(this); + this.initializeToscaPolicyModelsInfo(); + } + + componentWillReceiveProps(newProps) { + this.setState({ + loopCache: newProps.loopCache, + temporaryPropertiesJson: JSON.parse(JSON.stringify(newProps.loopCache.getGlobalProperties())) + }); + } + + initializeToscaPolicyModelsInfo() { + var operationalPolicies = this.state.loopCache.getOperationalPolicies(); + var selectedPolicyModels = []; + for (var policy in operationalPolicies) { + var newRow = operationalPolicies[policy]["policyModel"]; + newRow["policyName"] = operationalPolicies[policy].name; + selectedPolicyModels.push(newRow); + } + + PolicyToscaService.getToscaPolicyModels().then(allToscaModels => { + this.setState({ + toscaPolicyModelsData: allToscaModels, + selectedPolicyModelsData: selectedPolicyModels + }); + }); + } + + getToscaPolicyModelYaml(policyModelType, policyModelVersion) { + if (typeof policyModelType !== "undefined") { + PolicyToscaService.getToscaPolicyModelYaml(policyModelType, policyModelVersion).then(toscaYaml => { + if (toscaYaml.length !== 0) { + this.setState({ content: toscaYaml }) + } else { + this.setState({ content: 'No Tosca model Yaml available' }) + } + }); + } else { + this.setState({ content: 'Please select Tosca model to view the details' }) + } + } + + handleYamlContent(event) { + this.setState({ content: event.target.value }); + } + + handleClose() { + this.setState({ show: false }); + this.props.history.push('/'); + } + + renderAlert() { + return ( + <div> + <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible> + { this.state.showMessage } + </Alert> + </div> + ); + } + + handleAdd() { + LoopService.addOperationalPolicyType(this.state.loopCache.getLoopName(), this.state.selectedRowData.policyModelType, this.state.selectedRowData.version) + .then(pars => { + this.props.loadLoopFunction(this.state.loopCache.getLoopName()); + this.handleClose(); + }) + .catch(error => { + this.setState({ showFailAlert: true, showMessage: "Adding failed with error: " + error.message }); + }); + } + + handleRemove() { + LoopService.removeOperationalPolicyType(this.state.loopCache.getLoopName(), this.state.selectedRowData.policyModelType, this.state.selectedRowData.version, this.state.selectedRowData.policyName); + this.props.loadLoopFunction(this.state.loopCache.getLoopName()); + this.handleClose(); + } + + render() { + return ( + <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Modify Loop Operational Policies</Modal.Title> + </Modal.Header> + <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key, selectedRowData: {} }) }> + <Tab eventKey="add" title="Add Operational Policies"> + <Modal.Body> + <MaterialTable + title={ "View Tosca Policy Models" } + data={ this.state.toscaPolicyModelsData } + columns={ this.state.toscaColumns } + icons={ this.state.tableIcons } + onRowClick={ (event, rowData) => { + this.getToscaPolicyModelYaml(rowData.policyModelType, rowData.version); + this.setState({ selectedRowData: rowData }) + } } + options={ { + headerStyle: rowHeaderStyle, + rowStyle: rowData => ({ + backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined + && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF' + }) + } } + /> + <div> + <TextModal value={ this.state.content } onChange={ this.handleYamlContent }/> + </div> + </Modal.Body> + { this.renderAlert() } + </Tab> + <Tab eventKey="remove" title="Remove Operational Policies"> + <Modal.Body> + <MaterialTable + title={ "Tosca Policy Models already added" } + data={ this.state.selectedPolicyModelsData } + columns={ this.state.toscaColumns } + icons={ this.state.tableIcons } + onRowClick={ (event, rowData) => { + this.setState({ selectedRowData: rowData }) + } } + options={ { + headerStyle: rowHeaderStyle, + rowStyle: rowData => ({ + backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined + && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF' + }) + } } + /> + </Modal.Body> + </Tab> + </Tabs> + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button> + <Button variant="primary" disabled={ (this.state.key === "remove") } type="submit" onClick={ this.handleAdd }>Add</Button> + <Button variant="primary" disabled={ (this.state.key === "add") } type="submit" onClick={ this.handleRemove }>Remove</Button> + </Modal.Footer> + + </ModalStyled> + ); + } +} diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js new file mode 100644 index 0000000..79267af --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js @@ -0,0 +1,109 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import { mount } from 'enzyme'; +import ModifyLoopModal from './ModifyLoopModal'; +import LoopCache from '../../../api/LoopCache'; +import LoopService from '../../../api/LoopService'; +import PolicyToscaService from '../../../api/PolicyToscaService'; + +describe('Verify ModifyLoopModal', () => { + beforeEach(() => { + PolicyToscaService.getToscaPolicyModels = jest.fn().mockImplementation(() => { + return Promise.resolve([{ + "policyModelType": "test", + "policyAcronym": "test", + "version": "1.0.0", + "updatedBy": "", + "updatedDate": "" + }]); + }); + PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => { + return Promise.resolve("OK"); + }); + }) + + const loopCache = new LoopCache({ + "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca", + "microServicePolicies": [{ + "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca", + "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app", + "properties": { "domain": "measurementsForVfScaling" }, + "shared": false, + "jsonRepresentation": { "schema": {} } + }], + "globalPropertiesJson": { + "dcaeDeployParameters": { + "testMs": { + "location_id": "", + "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + } + } + } + }); + const historyMock = { push: jest.fn() }; + const flushPromises = () => new Promise(setImmediate); + + it('Test handleClose', () => { + const handleClose = jest.spyOn(ModifyLoopModal.prototype, 'handleClose'); + const component = mount(<ModifyLoopModal history={ historyMock } loopCache={ loopCache }/>) + + component.find('[variant="secondary"]').get(0).props.onClick(); + + expect(handleClose).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + }); + + it('Test getToscaPolicyModelYaml', async () => { + const flushPromises = () => new Promise(setImmediate); + const component = mount(<ModifyLoopModal history={ historyMock } loopCache={ loopCache }/>) + component.setState({ + "selectedRowData": { "tableData": { "id": 0 } } + }); + const instance = component.instance(); + + instance.getToscaPolicyModelYaml("", "1.0.0"); + expect(component.state('content')).toEqual("Please select Tosca model to view the details"); + + instance.getToscaPolicyModelYaml("test", "1.0.0"); + await flushPromises(); + expect(component.state('content')).toEqual("OK"); + + PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => { + return Promise.resolve(""); + }); + instance.getToscaPolicyModelYaml("test", "1.0.0"); + await flushPromises(); + expect(component.state('content')).toEqual("No Tosca model Yaml available"); + }); + + it('Test handleYamlContent', async () => { + const component = mount(<ModifyLoopModal loopCache={ loopCache }/>) + const instance = component.instance(); + + const event = { "target": { "value": "testValue" } } + instance.handleYamlContent(event); + expect(component.state('content')).toEqual("testValue"); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js new file mode 100644 index 0000000..b6407fb --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.js @@ -0,0 +1,139 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +import React from 'react' +import Select from 'react-select'; +import Button from 'react-bootstrap/Button'; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import Row from 'react-bootstrap/Row'; +import Col from 'react-bootstrap/Col'; +import FormCheck from 'react-bootstrap/FormCheck' +import styled from 'styled-components'; +import LoopService from '../../../api/LoopService'; +import SvgGenerator from '../../loop_viewer/svg/SvgGenerator'; +import LoopCache from '../../../api/LoopCache'; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` +const CheckBoxStyled = styled(FormCheck.Input)` + margin-left: 3rem; +` + +export default class OpenLoopModal extends React.Component { + constructor(props, context) { + super(props, context); + + this.getLoopNames = this.getLoopNames.bind(this); + this.handleOpen = this.handleOpen.bind(this); + this.handleClose = this.handleClose.bind(this); + this.handleDropDownListChange = this.handleDropDownListChange.bind(this); + this.renderSvg = this.renderSvg.bind(this); + this.showReadOnly = props.showReadOnly !== undefined ? props.showReadOnly : true; + this.state = { + show: true, + chosenLoopName: '', + loopNames: [], + loopCacheOpened: new LoopCache({}) + }; + } + + componentWillMount() { + this.getLoopNames(); + } + + handleClose() { + this.setState({ show: false }); + this.props.history.push('/'); + } + + handleDropDownListChange(e) { + LoopService.getLoop(e.value).then(loop => { + this.setState({ + chosenLoopName: e.value, + loopCacheOpened: new LoopCache(loop) + }); + }); + } + + getLoopNames() { + LoopService.getLoopNames().then(loopNames => { + if (Object.entries(loopNames).length !== 0) { + const loopOptions = loopNames.filter(loopName => loopName !== 'undefined').map((loopName) => { + return { label: loopName, value: loopName } + }); + this.setState({ loopNames: loopOptions }) + } + }); + } + + handleOpen() { + console.info("Loop " + this.state.chosenLoopName + " is chosen"); + this.handleClose(); + this.props.loadLoopFunction(this.state.chosenLoopName); + } + + renderSvg() { + return ( + <SvgGenerator loopCache={ this.state.loopCacheOpened } clickable={ false } generatedFrom={ SvgGenerator.GENERATED_FROM_INSTANCE }/> + ); + } + + render() { + return ( + <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Open Model</Modal.Title> + </Modal.Header> + <Modal.Body> + <Form.Group as={ Row } controlId="formPlaintextEmail"> + <Form.Label column sm="2">Model Name:</Form.Label> + <Col sm="10"> + <Select onChange={ this.handleDropDownListChange } + options={ this.state.loopNames }/> + </Col> + </Form.Group> + <Form.Group as={ Row } style={ { alignItems: 'center' } } controlId="formSvgPreview"> + <Form.Label column sm="2">Model Preview:</Form.Label> + <Col sm="10"> + { this.renderSvg() } + </Col> + </Form.Group> + { this.showReadOnly === true ? + <Form.Group as={ Row } controlId="formBasicCheckbox"> + <Form.Check> + <FormCheck.Label>Read Only Mode:</FormCheck.Label> + <CheckBoxStyled style={ { marginLeft: '3.5em' } } type="checkbox"/> + </Form.Check> + </Form.Group> + : null } + </Modal.Body> + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ this.handleClose }>Cancel</Button> + <Button variant="primary" type="submit" onClick={ this.handleOpen }>Open</Button> + </Modal.Footer> + </ModalStyled> + + ); + } +} diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js new file mode 100644 index 0000000..6b2dc4a --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/OpenLoopModal.test.js @@ -0,0 +1,93 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * =================================================================== + * + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import OpenLoopModal from './OpenLoopModal'; +import LoopService from '../../../api/LoopService'; + +describe('Verify OpenLoopModal', () => { + + beforeEach(() => { + fetch.resetMocks(); + fetch.mockResponse(JSON.stringify([ + "LOOP_gmtAS_v1_0_ResourceInstanceName1_tca", + "LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3", + "LOOP_gmtAS_v1_0_ResourceInstanceName2_tca_2" + ])); + }); + + it('Test the render method', () => { + + const component = shallow(<OpenLoopModal/>); + expect(component).toMatchSnapshot(); + }); + + it('Onchange event', async () => { + const flushPromises = () => new Promise(setImmediate); + LoopService.getLoop = jest.fn().mockImplementation(() => { + return Promise.resolve({ + ok: true, + status: 200, + json: () => { + } + }); + }); + const event = { value: 'LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3' }; + const component = shallow(<OpenLoopModal/>); + component.find('StateManager').simulate('change', event); + await flushPromises(); + component.update(); + expect(component.state('chosenLoopName')).toEqual("LOOP_gmtAS_v1_0_ResourceInstanceName1_tca_3"); + }); + + + it('Test handleClose', () => { + const historyMock = { push: jest.fn() }; + const handleClose = jest.spyOn(OpenLoopModal.prototype, 'handleClose'); + const component = shallow(<OpenLoopModal history={ historyMock }/>) + + component.find('[variant="secondary"]').prop('onClick')(); + + expect(handleClose).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + + handleClose.mockClear(); + }); + + it('Test handleSubmit', () => { + const historyMock = { push: jest.fn() }; + const loadLoopFunction = jest.fn(); + const handleOpen = jest.spyOn(OpenLoopModal.prototype, 'handleOpen'); + const component = shallow(<OpenLoopModal history={ historyMock } loadLoopFunction={ loadLoopFunction }/>) + + component.find('[variant="primary"]').prop('onClick')(); + + expect(handleOpen).toHaveBeenCalledTimes(1); + expect(component.state('show')).toEqual(false); + expect(historyMock.push.mock.calls[0]).toEqual(['/']); + + handleOpen.mockClear(); + }); + +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap new file mode 100644 index 0000000..1ba9dba --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap @@ -0,0 +1,167 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify CreateLoopModal Test the render method 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="xl" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Create Model + </ModalTitle> + </ModalHeader> + <ModalBody> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formPlaintextEmail" + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + Template Name: + </FormLabel> + <Col + sm="10" + > + <StateManager + defaultInputValue="" + defaultMenuIsOpen={false} + defaultValue={null} + onChange={[Function]} + options={Array []} + /> + </Col> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formSvgPreview" + style={ + Object { + "alignItems": "center", + } + } + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + Model Preview: + </FormLabel> + <Col + sm="10" + > + <withRouter(SvgGenerator) + clickable={false} + generatedFrom="TEMPLATE" + loopCache={ + LoopCache { + "loopJsonCache": Object {}, + } + } + /> + </Col> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formPlaintextEmail" + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + Model Name: + </FormLabel> + <input + onChange={[Function]} + sm="5" + style={ + Object { + "marginLeft": "1em", + "width": "50%", + } + } + type="text" + value="" + /> + <span + sm="5" + /> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formPlaintextEmail" + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + + </FormLabel> + <styled.div /> + </FormGroup> + </ModalBody> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Cancel + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="submit" + variant="primary" + > + Create + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap new file mode 100644 index 0000000..4779ced --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify DeployLoopModal Test the render method 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="lg" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Deployment parameters + </ModalTitle> + </ModalHeader> + <Tabs + activeKey="testMs" + id="controlled-tab-example" + mountOnEnter={false} + onSelect={[Function]} + unmountOnExit={false} + variant="tabs" + > + <Tab + eventKey="testMs" + title="testMs" + > + <Styled(FormGroup)> + <FormLabel + column={false} + srOnly={false} + > + location_id + </FormLabel> + <FormControl + defaultValue="" + name="location_id" + onChange={[Function]} + type="text" + /> + </Styled(FormGroup)> + <Styled(FormGroup)> + <FormLabel + column={false} + srOnly={false} + > + policy_id + </FormLabel> + <FormControl + defaultValue="TCA_h2NMX_v1_0_ResourceInstanceName1_tca" + name="policy_id" + onChange={[Function]} + type="text" + /> + </Styled(FormGroup)> + </Tab> + </Tabs> + <div /> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Cancel + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="submit" + variant="primary" + > + Deploy + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap new file mode 100644 index 0000000..3baaa57 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/LoopPropertiesModal.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify LoopPropertiesModal Test the render method 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="lg" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Model Properties + </ModalTitle> + </ModalHeader> + <ModalBody> + <Form + inline={false} + > + <FormGroup> + <FormLabel + column={false} + srOnly={false} + > + Deploy Parameters + </FormLabel> + <FormControl + as="textarea" + defaultValue="{\\"location_id\\":\\"\\",\\"policy_id\\":\\"TCA_h2NMX_v1_0_ResourceInstanceName1_tca\\"}" + name="dcaeDeployParameters" + onChange={[Function]} + rows="3" + /> + </FormGroup> + </Form> + </ModalBody> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Cancel + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="submit" + variant="primary" + > + Save Changes + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap new file mode 100644 index 0000000..581fd0e --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Loop/__snapshots__/OpenLoopModal.test.js.snap @@ -0,0 +1,137 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify OpenLoopModal Test the render method 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="xl" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Open Model + </ModalTitle> + </ModalHeader> + <ModalBody> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formPlaintextEmail" + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + Model Name: + </FormLabel> + <Col + sm="10" + > + <StateManager + defaultInputValue="" + defaultMenuIsOpen={false} + defaultValue={null} + onChange={[Function]} + options={Array []} + /> + </Col> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formSvgPreview" + style={ + Object { + "alignItems": "center", + } + } + > + <FormLabel + column={true} + sm="2" + srOnly={false} + > + Model Preview: + </FormLabel> + <Col + sm="10" + > + <withRouter(SvgGenerator) + clickable={false} + generatedFrom="INSTANCE" + loopCache={ + LoopCache { + "loopJsonCache": Object {}, + } + } + /> + </Col> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + controlId="formBasicCheckbox" + > + <FormCheck> + <FormCheckLabel> + Read Only Mode: + </FormCheckLabel> + <Styled(FormCheckInput) + style={ + Object { + "marginLeft": "3.5em", + } + } + type="checkbox" + /> + </FormCheck> + </FormGroup> + </ModalBody> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Cancel + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="submit" + variant="primary" + > + Open + </Button> + </ModalFooter> +</Styled(Modal)> +`; |