diff --git a/conda/js/src/components/toolchain/ToolchainEditor.spec.jsx b/conda/js/src/components/toolchain/ToolchainEditor.spec.jsx index 3c82fbc47937c8d6aea66a7523f917b36cf73e46..0abe32af92e5bda34c3eef2e51d41f6bf3493e73 100644 --- a/conda/js/src/components/toolchain/ToolchainEditor.spec.jsx +++ b/conda/js/src/components/toolchain/ToolchainEditor.spec.jsx @@ -158,7 +158,7 @@ const getIrisTc = (color1: string = '#008000', color2: string = '#FF0000') => { chai.use(deepEqualInAnyOrder); -describe.only('<ToolchainEditor />', function() { +describe('<ToolchainEditor />', function() { // these tests might take a long time, comparatively this.timeout(10000); diff --git a/conda/js/src/components/toolchain/ToolchainModal.jsx b/conda/js/src/components/toolchain/ToolchainModal.jsx index 0828e6a4db154c98ab43e02151dff63dfcbf28a5..0d6bb84e0e630bd0aa55b8db79ff3b011dd3c4e1 100644 --- a/conda/js/src/components/toolchain/ToolchainModal.jsx +++ b/conda/js/src/components/toolchain/ToolchainModal.jsx @@ -30,16 +30,19 @@ import { import CacheInput from '../CacheInput.jsx'; import DeleteInputBtn from '../DeleteInputBtn.jsx'; import { generateNewKey } from '@helpers'; +import type { BlockType } from '@helpers/toolchainTypes'; type Props = { - // block data - data: any, - // func to close modal - toggle: () => any, // is the modal active? active: boolean, + // block data + data: BlockType, // name of all blocks blockNames: string[], + // possible channels the block can be sync'd to (an obj w keys as channel names & vals as colours) + possibleChannels: { [string]: string }, + // func to close modal + toggle: () => any, // change the block name updateBlockName: (newName: string) => any, // change the name of an input or output @@ -50,8 +53,6 @@ type Props = { deleteBlockIO: (ioType: 'input' | 'output', ioName: string) => any, // delete the block deleteBlock: () => any, - // possible channels the block can be sync'd to (an obj w keys as channel names & vals as colours) - possibleChannels: any, // update sync'd channel updateBlockChannel: (channel: string) => any, }; @@ -112,11 +113,16 @@ class ToolchainModal extends React.Component<Props, State> { this.props.updateBlockName(e.target.value); }} validateFunc={(str) => { - return ( - str === data.name || - !this.props.blockNames.includes(str) || - <span>This name is already taken by an existing block</span> - ); + if(str === data.name){ + return true; + } + if(this.props.blockNames.includes(str)){ + return <span>This name is already taken by an existing block</span>; + } + if(str.includes('-')){ + return <span>{`Cannot include the dash ("-") character`}</span>; + } + return true; }} /> </FormGroup> diff --git a/conda/js/src/components/toolchain/ToolchainModal.spec.jsx b/conda/js/src/components/toolchain/ToolchainModal.spec.jsx new file mode 100644 index 0000000000000000000000000000000000000000..7849ab9d4ad3ff0b7d38f64fe16049d0813ef351 --- /dev/null +++ b/conda/js/src/components/toolchain/ToolchainModal.spec.jsx @@ -0,0 +1,122 @@ +// @flow +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import sinon from 'sinon'; +import { spies } from '@test'; + +import C from './ToolchainModal.jsx'; + +// sleep for a bit, to wait for async things +const sleep = (ms) => { + return new Promise(resolve => setTimeout(resolve, ms)); +}; + +describe.only('<ToolchainModal />', () => { + let wrapper; + + afterEach(() => { + if(wrapper && wrapper.unmount) + wrapper.unmount(); + }); + + const testBlockData = { + name: 'block1', + inputs: [ + 'input1', + ], + outputs: [ + 'output1', + ], + synchronized_channel: 'dataset1', + }; + + const testChannels = { 'dataset1': '#880000' }; + + const testBlockNames = ['block1']; + + it('accepts the test data with the right props', () => { + wrapper = mount( + <C + active={true} + data={testBlockData} + blockNames={testBlockNames} + possibleChannels={testChannels} + toggle={() => {}} + updateBlockName={() => {}} + updateBlockChannel={() => {}} + updateBlockIOName={() => {}} + addBlockIO={() => {}} + deleteBlockIO={() => {}} + deleteBlock={() => {}} + /> + ); + expect(wrapper).to.have.props([ + 'active', + 'data', + 'blockNames', + 'possibleChannels', + 'toggle', + 'updateBlockName', + 'updateBlockChannel', + 'updateBlockIOName', + 'addBlockIO', + 'deleteBlockIO', + 'deleteBlock', + ]); + }); + + it('lets you change the name to another valid name', async () => { + const newBlockName = 'block2'; + const _updateBlockName = (str: string) => { + expect(str).to.equal(newBlockName); + }; + wrapper = mount( + <C + active={true} + data={testBlockData} + blockNames={testBlockNames} + possibleChannels={testChannels} + toggle={() => {}} + updateBlockName={_updateBlockName} + updateBlockChannel={() => {}} + updateBlockIOName={() => {}} + addBlockIO={() => {}} + deleteBlockIO={() => {}} + deleteBlock={() => {}} + /> + ); + wrapper.find('input#tcModalInitFocus').prop('onChange')( { target: { value: newBlockName }}); + wrapper.update(); + await sleep(300); + wrapper.update(); + expect(wrapper.exists('.input-group-append .text-danger')).to.equal(false); + }); + + it('doesnt let you change the name to a valid name with a dash ("-") in it', async () => { + const newBlockName = 'block-2'; + const _updateBlockName = (str: string) => { + expect(str).to.equal(newBlockName); + }; + wrapper = mount( + <C + active={true} + data={testBlockData} + blockNames={testBlockNames} + possibleChannels={testChannels} + toggle={() => {}} + updateBlockName={_updateBlockName} + updateBlockChannel={() => {}} + updateBlockIOName={() => {}} + addBlockIO={() => {}} + deleteBlockIO={() => {}} + deleteBlock={() => {}} + /> + ); + wrapper.find('input#tcModalInitFocus').prop('onChange')( { target: { value: newBlockName }}); + wrapper.update(); + await sleep(300); + wrapper.update(); + expect(wrapper.exists('.input-group-append .text-danger')).to.equal(true); + }); +});