Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Composer/cypress/fixtures/luPublish/success.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"content": "#Dummy\r\n--I am Dummy",
"diagnostics": [],
"id": "Main",
"intents": [],
"relativePath": "Main/Main.lu",
"lastPublishTime": 2,
"lastUpdateTime": 1,
"parsedContent": {},
"relativePath": "Main/Main.lu"
"lastUpdateTime": 1
}
]
}
}
307 changes: 307 additions & 0 deletions Composer/packages/client/__tests__/utils/luUtil.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { sectionHandler } from '@bfcomposer/bf-lu/lib/parser';

import { updateIntent, addIntent, removeIntent } from '../../src/utils/luUtil';

const { luParser, luSectionTypes } = sectionHandler;

describe('LU Section CRUD test', () => {
const fileContent = `# Greeting
- hi
- hello

# CheckEmail
- check my email
- show my emails
`;

it('parse section test', () => {
const luresource = luParser.parse(fileContent);
const { Sections, Errors, Content } = luresource;

expect(Content).toEqual(fileContent);
expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[0].Errors.length).toEqual(0);
expect(luresource.Sections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(luresource.Sections[0].Name).toEqual('Greeting');
expect(luresource.Sections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(luresource.Sections[0].UtteranceAndEntitiesMap[0].utterance).toEqual('hi');
expect(luresource.Sections[0].UtteranceAndEntitiesMap[1].utterance).toEqual('hello');
});

it('add simpleIntentSection test', () => {
const intent = {
Name: 'CheckUnreadEmail',
Body: `- check my unread email
- show my unread emails`,
};

const fileContentUpdated = addIntent(fileContent, intent);
const luresource = luParser.parse(fileContentUpdated);
const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(3);
expect(Sections[2].Errors.length).toEqual(0);
expect(luresource.Sections[2].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(luresource.Sections[2].Name).toEqual('CheckUnreadEmail');
expect(luresource.Sections[2].UtteranceAndEntitiesMap.length).toEqual(2);
expect(luresource.Sections[2].UtteranceAndEntitiesMap[0].utterance).toEqual('check my unread email');
expect(luresource.Sections[2].UtteranceAndEntitiesMap[1].utterance).toEqual('show my unread emails');
});

it('update section test', () => {
const intentName = 'CheckEmail';

const intent = {
Name: 'CheckEmail',
Body: `- check my email
- show my emails
- check my mail box please`,
};

const fileContentUpdated = updateIntent(fileContent, intentName, intent);
const luresource = luParser.parse(fileContentUpdated);

const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[1].Errors.length).toEqual(0);
expect(luresource.Sections[1].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(luresource.Sections[1].Name).toEqual('CheckEmail');
expect(luresource.Sections[1].UtteranceAndEntitiesMap.length).toEqual(3);
expect(luresource.Sections[1].UtteranceAndEntitiesMap[0].utterance).toEqual('check my email');
expect(luresource.Sections[1].UtteranceAndEntitiesMap[1].utterance).toEqual('show my emails');
expect(luresource.Sections[1].UtteranceAndEntitiesMap[2].utterance).toEqual('check my mail box please');
});

it('delete section test', () => {
const intentName = 'CheckEmail';
const fileContentUpdated = removeIntent(fileContent, intentName);
const luresource = luParser.parse(fileContentUpdated);

const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(1);
expect(Sections[0].Errors.length).toEqual(0);
expect(luresource.Sections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(luresource.Sections[0].Name).toEqual('Greeting');
});
});

describe('LU Nested Section CRUD test', () => {
const fileContent = `> !# @enableSections = true

# CheckTodo
## CheckUnreadTodo
- check my unread todo
- show my unread todos

@ simple todoTitle

## CheckDeletedTodo
- check my deleted todo
- show my deleted todos

@ simple todoSubject`;

it('parse Nested section test', () => {
const luresource = luParser.parse(fileContent);
const { Sections, Errors, Content } = luresource;

expect(Content).toEqual(fileContent);
expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[1].Name).toEqual('CheckTodo');
expect(Sections[1].SimpleIntentSections.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].Name).toEqual('CheckUnreadTodo');
expect(Sections[1].SimpleIntentSections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[1].SimpleIntentSections[0].Errors.length).toEqual(0);
expect(Sections[1].SimpleIntentSections[0].Entities.length).toEqual(1);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[0].utterance).toEqual('check my unread todo');
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[1].utterance).toEqual('show my unread todos');
});

it('add nestedIntentSection test', () => {
const intent = {
Name: 'CheckTodo/CheckCompletedTodo',
Body: `- check my completed todo
- show my completed todos

@ simple todoTime
`,
};

const fileContentUpdated = addIntent(fileContent, intent);
const luresource = luParser.parse(fileContentUpdated);
const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[1].Name).toEqual('CheckTodo');
expect(Sections[1].SimpleIntentSections.length).toEqual(3);
expect(Sections[1].SimpleIntentSections[2].Name).toEqual('CheckCompletedTodo');
expect(Sections[1].SimpleIntentSections[2].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[1].SimpleIntentSections[2].Errors.length).toEqual(0);
expect(Sections[1].SimpleIntentSections[2].Entities.length).toEqual(1);
expect(Sections[1].SimpleIntentSections[2].Entities[0].Name).toEqual('todoTime');
expect(Sections[1].SimpleIntentSections[2].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[2].UtteranceAndEntitiesMap[0].utterance).toEqual('check my completed todo');
expect(Sections[1].SimpleIntentSections[2].UtteranceAndEntitiesMap[1].utterance).toEqual('show my completed todos');
});

it('add nestedIntentSection test, recursive', () => {
const intent = {
Name: 'CheckMyTodo/CheckCompletedTodo',
Body: `- check my completed todo
- show my completed todos

@ simple todoTime
`,
};

const fileContentUpdated = addIntent(fileContent, intent);
const luresource = luParser.parse(fileContentUpdated);
const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(3);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[2].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[2].Name).toEqual('CheckMyTodo');
expect(Sections[2].SimpleIntentSections.length).toEqual(1);
expect(Sections[2].SimpleIntentSections[0].Name).toEqual('CheckCompletedTodo');
expect(Sections[2].SimpleIntentSections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[2].SimpleIntentSections[0].Errors.length).toEqual(0);
expect(Sections[2].SimpleIntentSections[0].Entities.length).toEqual(1);
expect(Sections[2].SimpleIntentSections[0].Entities[0].Name).toEqual('todoTime');
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap[0].utterance).toEqual('check my completed todo');
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap[1].utterance).toEqual('show my completed todos');
});

it('update nestedIntentSection test', () => {
const intentName = 'CheckTodo/CheckUnreadTodo';
const intent = {
Name: 'CheckMyUnreadTodo',
Body: `- please check my unread todo
- please show my unread todos

@ simple todoTitle
@ simple todoContent
`,
};

const fileContentUpdated = updateIntent(fileContent, intentName, intent);
const luresource = luParser.parse(fileContentUpdated);
const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[1].Name).toEqual('CheckTodo');
expect(Sections[1].SimpleIntentSections.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].Name).toEqual('CheckMyUnreadTodo');
expect(Sections[1].SimpleIntentSections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[1].SimpleIntentSections[0].Errors.length).toEqual(0);
expect(Sections[1].SimpleIntentSections[0].Entities.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].Entities[1].Name).toEqual('todoContent');
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[0].utterance).toEqual(
'please check my unread todo'
);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[1].utterance).toEqual(
'please show my unread todos'
);
});

/**
* this will add #CheckMyTodo
* in #CheckMyTodo, ##CheckUnreadTodo not exist, then will do add ##CheckMyUnreadTodo
*/
it('update nestedIntentSection test, recursive', () => {
const intentName = 'CheckMyTodo/CheckUnreadTodo';
const intent = {
Name: 'CheckMyUnreadTodo',
Body: `- please check my unread todo
- please show my unread todos

@ simple todoContent
`,
};

const fileContentUpdated = updateIntent(fileContent, intentName, intent);
const luresource = luParser.parse(fileContentUpdated);
const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(3);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[2].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[2].Name).toEqual('CheckMyTodo');
expect(Sections[2].SimpleIntentSections.length).toEqual(1);
expect(Sections[2].SimpleIntentSections[0].Name).toEqual('CheckMyUnreadTodo');
expect(Sections[2].SimpleIntentSections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[2].SimpleIntentSections[0].Errors.length).toEqual(0);
expect(Sections[2].SimpleIntentSections[0].Entities.length).toEqual(1);
expect(Sections[2].SimpleIntentSections[0].Entities[0].Name).toEqual('todoContent');
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap[0].utterance).toEqual(
'please check my unread todo'
);
expect(Sections[2].SimpleIntentSections[0].UtteranceAndEntitiesMap[1].utterance).toEqual(
'please show my unread todos'
);
});

it('delete nestedIntentSection test', () => {
const Name = 'CheckTodo/CheckUnreadTodo';
const fileContentUpdated = removeIntent(fileContent, Name);
const luresource = luParser.parse(fileContentUpdated);

const { Sections, Errors } = luresource;

expect(Errors.length).toEqual(0);
expect(Sections.length).toEqual(2);
expect(Sections[0].SectionType).toEqual(luSectionTypes.MODELINFOSECTION);
expect(Sections[1].SectionType).toEqual(luSectionTypes.NESTEDINTENTSECTION);
expect(Sections[1].Name).toEqual('CheckTodo');
expect(Sections[1].SimpleIntentSections.length).toEqual(1);
expect(Sections[1].SimpleIntentSections[0].Name).toEqual('CheckDeletedTodo');
expect(Sections[1].SimpleIntentSections[0].SectionType).toEqual(luSectionTypes.SIMPLEINTENTSECTION);
expect(Sections[1].SimpleIntentSections[0].Errors.length).toEqual(0);
expect(Sections[1].SimpleIntentSections[0].Entities.length).toEqual(1);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap.length).toEqual(2);
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[0].utterance).toEqual('check my deleted todo');
expect(Sections[1].SimpleIntentSections[0].UtteranceAndEntitiesMap[1].utterance).toEqual('show my deleted todos');
});

it('delete nestedIntentSection test, parrent not exist', () => {
const Name = 'CheckTodoNotExist/CheckUnreadTodo';
const fileContentUpdated = removeIntent(fileContent, Name);
const luresource = luParser.parse(fileContentUpdated);
const { Content } = luresource;
expect(Content).toEqual(fileContent);
});

it('delete nestedIntentSection test, child not exist', () => {
const Name = 'CheckTodo/CheckUnreadTodoNotExist';
const fileContentUpdated = removeIntent(fileContent, Name);
const luresource = luParser.parse(fileContentUpdated);
const { Content } = luresource;
expect(Content).toEqual(fileContent);
});
});
1 change: 1 addition & 0 deletions Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@bfc/extensions": "*",
"@bfc/indexers": "*",
"@bfc/shared": "*",
"@bfcomposer/bf-lu": "^1.1.8",
"@emotion/core": "^10.0.7",
"@reach/router": "^1.2.1",
"axios": "^0.18.0",
Expand Down
46 changes: 46 additions & 0 deletions Composer/packages/client/src/ShellApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import get from 'lodash/get';

import { isExpression } from './utils';
import * as lgUtil from './utils/lgUtil';
import * as luUtil from './utils/luUtil';
import { StoreContext } from './store';
import ApiClient from './messenger/ApiClient';
import { getDialogData, setDialogData, sanitizeDialogData } from './utils';
Expand Down Expand Up @@ -202,6 +203,48 @@ export const ShellApi: React.FC = () => {
});
}

/**
*
* @param {
* id: string,
* intentName: string,
* intent: { name: string, body: string }
* }
*
* @param {*} event
*/
async function updateLuIntentHandler({ id, intentName, intent }, event) {
if (isEventSourceValid(event) === false) return false;
const file = luFiles.find(file => file.id === id);
if (!file) throw new Error(`lu file ${id} not found`);
if (!intentName) throw new Error(`intentName is missing or empty`);

const newLuContent = luUtil.updateIntent(file.content, intentName, intent);

return await updateLuFile({ id, newLuContent });
}

async function addLuIntentHandler({ id, intent }, event) {
if (isEventSourceValid(event) === false) return false;
const file = luFiles.find(file => file.id === id);
if (!file) throw new Error(`lu file ${id} not found`);

const newLuContent = luUtil.addIntent(file.content, intent);

return await updateLuFile({ id, newLuContent });
}

async function removeLuIntentHandler({ id, intentName }, event) {
if (isEventSourceValid(event) === false) return false;
const file = luFiles.find(file => file.id === id);
if (!file) throw new Error(`lu file ${id} not found`);
if (!intentName) throw new Error(`intentName is missing or empty`);

const newLuContent = luUtil.removeIntent(file.content, intentName);

return await updateLuFile({ id, newLuContent });
}

async function fileHandler(fileTargetType, fileChangeType, { id, content }, event) {
if (isEventSourceValid(event) === false) return false;

Expand Down Expand Up @@ -301,6 +344,9 @@ export const ShellApi: React.FC = () => {
apiClient.registerApi('removeLgTemplate', removeLgTemplateHandler);
apiClient.registerApi('removeLgTemplates', removeLgTemplatesHandler);
apiClient.registerApi('getLgTemplates', ({ id }, event) => getLgTemplates({ id }, event));
apiClient.registerApi('addLuIntent', addLuIntentHandler);
apiClient.registerApi('updateLuIntent', updateLuIntentHandler);
apiClient.registerApi('removeLuIntent', removeLuIntentHandler);
apiClient.registerApi('navTo', navTo);
apiClient.registerApi('onFocusEvent', focusEvent);
apiClient.registerApi('onFocusSteps', focusSteps);
Expand Down
Loading