This repository was archived by the owner on Jul 9, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 374
feat: lu shell api #1810
Merged
Merged
feat: lu shell api #1810
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
72b46b9
add lu crud api
zhixzhan 78c5345
add in shell api
zhixzhan c3be644
Merge branch 'master' into lu-api
boydc2014 a933022
Merge branch 'master' into lu-api
zhixzhan 8fa9c2c
fix empty lu file parse
zhixzhan 97acee3
Merge branch 'master' of https://github.com/microsoft/BotFramework-Co…
zhixzhan d34f8e5
Merge branch 'master' into lu-api
zhixzhan 550c95b
support nested section
zhixzhan 973578a
clean up
zhixzhan 0a3473b
refactor luFile interface
zhixzhan 8e5f5bb
refactor LuFile interface
zhixzhan 4fdbee6
Merge branch 'master' of https://github.com/microsoft/BotFramework-Co…
zhixzhan c61fa9a
fix lu parser error
zhixzhan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
307 changes: 307 additions & 0 deletions
307
Composer/packages/client/__tests__/utils/luUtil.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.