diff --git a/Composer/packages/client/__tests__/components/CreationFlow/DefineConversation/index.test.tsx b/Composer/packages/client/__tests__/components/CreationFlow/DefineConversation/index.test.tsx
index 9d6305c0f2..3a01c93751 100644
--- a/Composer/packages/client/__tests__/components/CreationFlow/DefineConversation/index.test.tsx
+++ b/Composer/packages/client/__tests__/components/CreationFlow/DefineConversation/index.test.tsx
@@ -2,17 +2,18 @@
// Licensed under the MIT License.
import * as React from 'react';
-import { render, fireEvent } from '@bfc/test-utils';
+import { fireEvent } from '@bfc/test-utils';
-import { StoreContext } from '../../../../src/store';
+import { renderWithStore } from '../../../testUtils';
import { StorageFolder } from '../../../../src/store/types';
import DefineConversation from '../../../../src/components/CreationFlow/DefineConversation';
describe('', () => {
- let onSubmitMock;
- let onDismissMock;
const onCurrentPathUpdateMock = jest.fn();
- let component, storeContext, saveTemplateMock, locationMock;
+ const saveTemplateMock = jest.fn();
+ const onSubmitMock = jest.fn();
+ const onDismissMock = jest.fn();
+ let storeContext, locationMock;
const focusedStorageFolder: StorageFolder = {
name: 'Desktop',
parent: '/test-folder',
@@ -30,49 +31,44 @@ describe('', () => {
],
};
function renderComponent() {
- return render(
-
-
-
+ return renderWithStore(
+ ,
+ storeContext.state,
+ storeContext.action
);
}
beforeEach(() => {
- saveTemplateMock = jest.fn();
locationMock = {};
storeContext = {
actions: {
saveTemplateId: saveTemplateMock,
},
state: {
- templateId: '',
+ templateId: 'EchoBot',
focusedStorageFolder: '',
+ storages: [],
},
};
-
- onSubmitMock = jest.fn();
});
it('should render the component', () => {
- storeContext.state.storages = [];
- component = renderComponent();
+ const component = renderComponent();
expect(component.container).toBeDefined();
});
it('should update formdata with data passed through location props', async () => {
- storeContext.state.storages = [];
- storeContext.state.templateId = 'EchoBot';
locationMock = {
search:
'schemaUrl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fbotframework-sdk%2Fmaster%2Fschemas%2Fcomponent%2Fcomponent.schema%26name%3DEchoBot-11299%26description%3DTest%20Echo',
};
- component = renderComponent();
+ const component = renderComponent();
const node = await component.findByText('Next');
fireEvent.click(node);
expect(onSubmitMock).toHaveBeenCalledWith({
@@ -82,4 +78,12 @@ describe('', () => {
'https://raw.githubusercontent.com/microsoft/botframework-sdk/master/schemas/component/component.schema',
});
});
+
+ it('does not allow submission when the name is invalid', async () => {
+ const component = renderComponent();
+ const nameField = await component.getByTestId('NewDialogName');
+ fireEvent.change(nameField, { target: { value: 'invalidName;' } });
+ const node = await component.getByTestId('SubmitNewBotBtn');
+ expect(node).toBeDisabled();
+ });
});
diff --git a/Composer/packages/client/__tests__/components/createDialogModal.test.tsx b/Composer/packages/client/__tests__/components/createDialogModal.test.tsx
new file mode 100644
index 0000000000..c03806050c
--- /dev/null
+++ b/Composer/packages/client/__tests__/components/createDialogModal.test.tsx
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import * as React from 'react';
+import { fireEvent } from '@bfc/test-utils';
+
+import { CreateDialogModal } from '../../src/pages/design/createDialogModal';
+import { renderWithStore } from '../testUtils';
+
+describe('', () => {
+ const onSubmitMock = jest.fn();
+ const onDismissMock = jest.fn();
+ let storeContext;
+ function renderComponent() {
+ return renderWithStore(
+ ,
+ storeContext.state
+ );
+ }
+
+ beforeEach(() => {
+ storeContext = {
+ state: {
+ showCreateDialogModal: true,
+ dialogs: [],
+ },
+ };
+ });
+
+ it('should render the component', () => {
+ const component = renderComponent();
+ expect(component.container).toBeDefined();
+ });
+
+ it('does not allow submission when the name is invalid', async () => {
+ const component = renderComponent();
+ const nameField = await component.getByTestId('NewDialogName');
+ fireEvent.change(nameField, { target: { value: 'invalidName;' } });
+ const node = await component.getByTestId('SubmitNewDialogBtn');
+ expect(node).toBeDisabled();
+ });
+});
diff --git a/Composer/packages/client/src/components/CreationFlow/DefineConversation/index.tsx b/Composer/packages/client/src/components/CreationFlow/DefineConversation/index.tsx
index 786585c983..d827348dae 100644
--- a/Composer/packages/client/src/components/CreationFlow/DefineConversation/index.tsx
+++ b/Composer/packages/client/src/components/CreationFlow/DefineConversation/index.tsx
@@ -14,7 +14,7 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { RouteComponentProps } from '@reach/router';
import querystring from 'query-string';
-import { DialogCreationCopy } from '../../../constants';
+import { DialogCreationCopy, nameRegex } from '../../../constants';
import { DialogWrapper } from '../../DialogWrapper';
import { DialogTypes } from '../../DialogWrapper/styles';
import { LocationSelectContent } from '../LocationBrowser/LocationSelectContent';
@@ -67,11 +67,8 @@ const DefineConversation: React.FC = (props) => {
name: {
required: true,
validate: (value) => {
- const nameRegex = /^[a-zA-Z0-9-_.]+$/;
if (!value || !nameRegex.test(value)) {
- return formatMessage(
- 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
- );
+ return formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.');
}
const newBotPath =
diff --git a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
index 528ad955b7..4e519dad7c 100644
--- a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
+++ b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
@@ -19,6 +19,7 @@ import { DialogInfo, SDKKinds } from '@bfc/shared';
import { LuEditor, inlineModePlaceholder } from '@bfc/code-editor';
import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox';
+import { nameRegex } from '../../constants';
import {
generateNewDialog,
getTriggerTypes,
@@ -37,7 +38,6 @@ import { StoreContext } from '../../store';
import { styles, dropdownStyles, dialogWindow, intent } from './styles';
-const nameRegex = /^[a-zA-Z0-9-_.]+$/;
const validateForm = (
selectedType: string,
data: TriggerFormData,
@@ -64,9 +64,7 @@ const validateForm = (
}
if (selectedType === intentTypeKey && (!intent || !nameRegex.test(intent))) {
- errors.intent = formatMessage(
- 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
- );
+ errors.intent = formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.');
}
if (selectedType === intentTypeKey && isRegEx && regExIntents.find((ri) => ri.intent === intent)) {
diff --git a/Composer/packages/client/src/constants/index.ts b/Composer/packages/client/src/constants/index.ts
index 576f2761b6..c7b28f69ad 100644
--- a/Composer/packages/client/src/constants/index.ts
+++ b/Composer/packages/client/src/constants/index.ts
@@ -255,3 +255,5 @@ export const DefaultPublishConfig = {
};
export const EmptyBotTemplateId = 'EmptyBot';
+
+export const nameRegex = /^[a-zA-Z0-9-_]+$/;
diff --git a/Composer/packages/client/src/pages/design/createDialogModal.tsx b/Composer/packages/client/src/pages/design/createDialogModal.tsx
index e14cc013ad..48979039bd 100644
--- a/Composer/packages/client/src/pages/design/createDialogModal.tsx
+++ b/Composer/packages/client/src/pages/design/createDialogModal.tsx
@@ -7,7 +7,7 @@ import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
-import { DialogCreationCopy } from '../../constants';
+import { DialogCreationCopy, nameRegex } from '../../constants';
import { DialogWrapper } from '../../components/DialogWrapper';
import { DialogTypes } from '../../components/DialogWrapper/styles';
import { StorageFolder } from '../../store/types';
@@ -37,8 +37,6 @@ export const CreateDialogModal: React.FC = (props) => {
name: {
required: true,
validate: (value) => {
- const nameRegex = /^[a-zA-Z0-9-_.]+$/;
-
if (!nameRegex.test(value)) {
return formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.');
}
@@ -104,7 +102,12 @@ export const CreateDialogModal: React.FC = (props) => {
-
+
diff --git a/Composer/packages/client/src/pages/language-understanding/publish-luis-modal.jsx b/Composer/packages/client/src/pages/language-understanding/publish-luis-modal.jsx
index 6af198edbc..42f0429e69 100644
--- a/Composer/packages/client/src/pages/language-understanding/publish-luis-modal.jsx
+++ b/Composer/packages/client/src/pages/language-understanding/publish-luis-modal.jsx
@@ -18,7 +18,7 @@ import keys from 'lodash/keys';
import { StoreContext } from '../../store';
-import { Text, Tips, Links } from './../../constants';
+import { Text, Tips, Links, nameRegex } from './../../constants';
import { textFieldLabel, dialog, dialogModal, dialogSubTitle, dialogContent, consoleStyle } from './styles';
const STATE = {
@@ -38,7 +38,6 @@ const onRenderLabel = (info) => (props) => (
);
-const nameRegex = /^[a-zA-Z0-9-_.]+$/;
const validationProperties = ['name', 'authoringKey', 'environment'];
const defaultFields = { authoringRegion: 'westus', defaultLanguage: 'en-us' };
const validateForm = (data) => {
@@ -49,7 +48,7 @@ const validateForm = (data) => {
const value = data[key];
if (validationProperties.includes(key) && (!value || !nameRegex.test(value))) {
result.errors[key] = formatMessage(
- 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
+ 'Spaces and special characters are not allowed. Use letters, numbers, -, or _.'
);
} else if (key in defaultFields && value === '') {
result[key] = defaultFields[key];