From 805c71b812f116633b22b8577d2e0c23e78a9739 Mon Sep 17 00:00:00 2001 From: zeye <2295905420@qq.com> Date: Thu, 2 Jul 2020 00:10:28 +0800 Subject: [PATCH 1/3] test: increase 'adaptive-flow' test coverage to 77% (#3530) * + UT: adaptive-flow-renderer/widgets * + UT: adaptive-flow-editor/utils * remove unref hook: useWindowDimension * + UT: KeyboardZone * + UT: cursorTracker * + UT: adaptive-flow-editor/constants * +UT: AdaptiveFlowEditor * + UT: adaptive-flow-editor/contexts * + UT: useEditorEventApi * fix CI error * + UT: NodeWrapper * + UT: EdgeMenu * KeyboardZone behavioror test * change the test file structure of cursorTracker --- .../AdaptiveFlowEditor.test.tsx | 27 ++++++++ .../components/KeyboardZone.test.tsx | 39 +++++++++++ .../constants/ElementAttributes.test.tsx | 18 +++++ .../constants/KeyboardCommandTypes.test.ts | 66 +++++++++++++++++++ .../constants/MenuTypes.test.ts | 12 ++++ .../constants/ScreenReaderMessage.test.ts | 24 +++++++ .../constants/editorConfig.test.ts | 17 +++++ .../contexts/NodeRendererContext.test.tsx | 50 ++++++++++++++ .../contexts/SelectionContext.test.tsx | 45 +++++++++++++ .../hooks/useEditorEventApi.test.ts | 50 ++++++++++++++ .../renderers/EdgeMenu.test.tsx | 45 +++++++++++++ .../renderers/ElementWrapper.test.tsx | 21 ++++++ .../renderers/NodeWrapper.test.tsx | 30 +++++++++ .../stubs/ShellApiStub.ts | 45 +++++++++++++ .../utils/NodeIndexGetter.test.ts | 25 +++++++ .../utils/calculateRangeSelection.test.ts | 11 ++++ .../utils/cursorTracker/index.test.ts | 46 +++++++++++++ .../utils/cursorTracker/type.test.ts | 18 +++++ .../mapKeyboardCommandToEditorEvent.test.ts | 34 ++++++++++ .../utils/mergePluginConfig.test.ts | 34 ++++++++++ .../widgets/ActionCard.test.tsx | 31 +++++++++ .../widgets/DialogRef.test.tsx | 36 ++++++++++ .../widgets/ForeachWidget.test.tsx | 23 +++++++ .../widgets/IfConditionWidget.test.tsx | 23 +++++++ .../widgets/PromptWidget.test.tsx | 25 +++++++ .../widgets/SwitchConditionWidget.test.tsx | 23 +++++++ .../AdaptiveFlowEditor.tsx | 2 +- .../components/KeyboardZone.tsx | 2 +- .../contexts/NodeRendererContext.ts | 7 +- .../contexts/SelectionContext.ts | 5 +- .../hooks/useWindowDimensions.ts | 25 ------- .../renderers/NodeWrapper.tsx | 1 + ....ts => mapKeyboardCommandToEditorEvent.ts} | 0 .../extensions/extension/src/index.ts | 3 + 34 files changed, 831 insertions(+), 32 deletions(-) create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/AdaptiveFlowEditor.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/components/KeyboardZone.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ElementAttributes.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/KeyboardCommandTypes.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/MenuTypes.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ScreenReaderMessage.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/editorConfig.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/NodeRendererContext.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/SelectionContext.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/hooks/useEditorEventApi.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/EdgeMenu.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/ElementWrapper.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/NodeWrapper.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/stubs/ShellApiStub.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/NodeIndexGetter.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/calculateRangeSelection.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/index.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/type.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mergePluginConfig.test.ts create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ActionCard.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/DialogRef.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ForeachWidget.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/IfConditionWidget.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/PromptWidget.test.tsx create mode 100644 Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/SwitchConditionWidget.test.tsx delete mode 100644 Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/hooks/useWindowDimensions.ts rename Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/utils/{mapKeyboardCommandToEditorEvent.ts.ts => mapKeyboardCommandToEditorEvent.ts} (100%) diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/AdaptiveFlowEditor.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/AdaptiveFlowEditor.test.tsx new file mode 100644 index 0000000000..ad83d07308 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/AdaptiveFlowEditor.test.tsx @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; +import { ExtensionContext } from '@bfc/extension'; + +import AdaptiveFlowEditor from '../../src/adaptive-flow-editor/AdaptiveFlowEditor'; + +import { ShellApiStub } from './stubs/ShellApiStub'; + +describe('', () => { + it('can render.', () => { + const visualDesigner = render( + + + + ); + expect(visualDesigner).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/components/KeyboardZone.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/components/KeyboardZone.test.tsx new file mode 100644 index 0000000000..0625746ee8 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/components/KeyboardZone.test.tsx @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render, fireEvent } from '@bfc/test-utils'; + +import { KeyboardZone } from '../../../src/adaptive-flow-editor/components/KeyboardZone'; + +describe('KeyboardZone', () => { + it('can be rendered.', () => { + const zone = render( + undefined}> + children + + ); + expect(zone).toBeTruthy(); + expect(zone.getByTestId('zone-child')).toBeTruthy(); + }); + + it('can trigger onCommand.', () => { + const mockOnCommand = jest.fn(); + const zone = render( + + children + + ).getByTestId('keyboard-zone'); + + fireEvent.focus(zone); + fireEvent.keyDown( + zone, + new KeyboardEvent('keydown', { + key: 'C', + code: 'C', + ctrlKey: true, + }) + ); + expect(mockOnCommand).toHaveBeenCalled(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ElementAttributes.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ElementAttributes.test.tsx new file mode 100644 index 0000000000..70471db044 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ElementAttributes.test.tsx @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { AttrNames } from '../../../src/adaptive-flow-editor/constants/ElementAttributes'; + +describe('ElementAttributes', () => { + it('should contain necessary attrs.', () => { + expect(AttrNames.SelectableElement).toBeTruthy(); + expect(AttrNames.NodeElement).toBeTruthy(); + expect(AttrNames.EdgeMenuElement).toBeTruthy(); + expect(AttrNames.FocusedId).toBeTruthy(); + expect(AttrNames.InlineLinkElement).toBeTruthy(); + expect(AttrNames.SelectedId).toBeTruthy(); + expect(AttrNames.Tab).toBeTruthy(); + expect(AttrNames.FocusedId).toBeTruthy(); + expect(AttrNames.SelectionIndex).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/KeyboardCommandTypes.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/KeyboardCommandTypes.test.ts new file mode 100644 index 0000000000..edbcb96764 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/KeyboardCommandTypes.test.ts @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + KeyboardCommandTypes, + KeyboardPrimaryTypes, + mapShortcutToKeyboardCommand, +} from '../../../src/adaptive-flow-editor/constants/KeyboardCommandTypes'; + +describe('KeyboardCommandTypes', () => { + it('should be truthy.', () => { + expect(KeyboardCommandTypes).toBeTruthy(); + }); + + it('should contain 3 categories.', () => { + expect(KeyboardCommandTypes.Cursor).toBeTruthy(); + expect(KeyboardCommandTypes.Node).toBeTruthy(); + expect(KeyboardCommandTypes.Operation).toBeTruthy(); + }); + + it(' should contain necessary commands.', () => { + const { Cursor } = KeyboardCommandTypes; + + expect(Cursor.MoveUp).toBeTruthy(); + expect(Cursor.MoveDown).toBeTruthy(); + expect(Cursor.MoveLeft).toBeTruthy(); + expect(Cursor.MoveRight).toBeTruthy(); + expect(Cursor.ShortMoveUp).toBeTruthy(); + expect(Cursor.ShortMoveDown).toBeTruthy(); + expect(Cursor.ShortMoveLeft).toBeTruthy(); + expect(Cursor.ShortMoveRight).toBeTruthy(); + expect(Cursor.MovePrevious).toBeTruthy(); + expect(Cursor.MoveNext).toBeTruthy(); + }); + + it(' should contain necessary actions.', () => { + const { Node } = KeyboardCommandTypes; + + expect(Node.Delete).toBeTruthy(); + expect(Node.Copy).toBeTruthy(); + expect(Node.Cut).toBeTruthy(); + expect(Node.Paste).toBeTruthy(); + }); + + it(' should contain necessary operations.', () => { + const { Operation } = KeyboardCommandTypes; + + expect(Operation.Undo).toBeTruthy(); + expect(Operation.Redo).toBeTruthy(); + }); +}); + +describe('KeyboardPrimaryTypes', () => { + it('should contain 3 types.', () => { + expect(KeyboardPrimaryTypes.Cursor).toBeTruthy(); + expect(KeyboardPrimaryTypes.Node).toBeTruthy(); + expect(KeyboardPrimaryTypes.Operation).toBeTruthy(); + }); +}); + +describe('mapShortcutToKeyboardCommand', () => { + expect(mapShortcutToKeyboardCommand('Windows.Control.Z')).toEqual({ + area: 'Operation', + command: 'undo', + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/MenuTypes.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/MenuTypes.test.ts new file mode 100644 index 0000000000..07c2f13ac2 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/MenuTypes.test.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { MenuTypes, MenuEventTypes } from '../../../src/adaptive-flow-editor/constants/MenuTypes'; + +describe('MenuTypes', () => { + it('should contain necessary keys.', () => { + expect(MenuTypes.EdgeMenu).toBeTruthy(); + expect(MenuTypes.NodeMenu).toBeTruthy(); + expect(MenuEventTypes.Paste).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ScreenReaderMessage.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ScreenReaderMessage.test.ts new file mode 100644 index 0000000000..b477462737 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/ScreenReaderMessage.test.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ScreenReaderMessage } from '../../../src/adaptive-flow-editor/constants/ScreenReaderMessage'; + +describe('ScreenReaderMessage', () => { + it('should contain necessary messages.', () => { + expect(ScreenReaderMessage.EventFocused).toBeTruthy(); + expect(ScreenReaderMessage.ActionFocused).toBeTruthy(); + expect(ScreenReaderMessage.ActionUnfocused).toBeTruthy(); + expect(ScreenReaderMessage.RangeSelection).toBeTruthy(); + expect(ScreenReaderMessage.DialogOpened).toBeTruthy(); + expect(ScreenReaderMessage.ActionDeleted).toBeTruthy(); + expect(ScreenReaderMessage.ActionsDeleted).toBeTruthy(); + expect(ScreenReaderMessage.ActionCreated).toBeTruthy(); + expect(ScreenReaderMessage.ActionsCreated).toBeTruthy(); + expect(ScreenReaderMessage.EventCreated).toBeTruthy(); + expect(ScreenReaderMessage.ActionsCopied).toBeTruthy(); + expect(ScreenReaderMessage.ActionsCut).toBeTruthy(); + expect(ScreenReaderMessage.ActionsMoved).toBeTruthy(); + expect(ScreenReaderMessage.ActionUndo).toBeTruthy(); + expect(ScreenReaderMessage.ActionRedo).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/editorConfig.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/editorConfig.test.ts new file mode 100644 index 0000000000..38abeef675 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/constants/editorConfig.test.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { EditorConfig } from '../../../src/adaptive-flow-editor/constants/editorConfig'; + +describe('EditorConfig', () => { + it('should cover several features.', () => { + expect(EditorConfig).toBeTruthy(); + expect(EditorConfig.features).toBeDefined(); + + expect(EditorConfig.features.showEvents).toBeDefined(); + expect(EditorConfig.features.arrowNavigation).toBeDefined(); + expect(EditorConfig.features.tabNavigation).toBeDefined(); + expect(EditorConfig.features.keyboardNodeEditing).toBeDefined(); + expect(EditorConfig.features.keyboardOperationEditing).toBeDefined(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/NodeRendererContext.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/NodeRendererContext.test.tsx new file mode 100644 index 0000000000..40a9108bca --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/NodeRendererContext.test.tsx @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React, { useContext } from 'react'; +import { render } from '@bfc/test-utils'; +import { DialogFactory } from '@bfc/shared'; + +import { NodeRendererContext } from '../../../src/adaptive-flow-editor/contexts/NodeRendererContext'; + +describe('NodeRendererContext', () => { + const CtxtConsumer = () => { + const { focusedId, focusedEvent, focusedTab, clipboardActions, dialogFactory, customSchemas } = useContext( + NodeRendererContext + ); + + return ( +
+ {focusedId} + {focusedEvent} + {focusedTab} + {clipboardActions.length} + {'' + !!dialogFactory} + {customSchemas.length} +
+ ); + }; + it('can be consumed.', () => { + const ele = render( + + + + ); + + expect(ele.getByTestId('focusedId-value').textContent).toEqual('id1'); + expect(ele.getByTestId('focusedEvent-value').textContent).toEqual('event1'); + expect(ele.getByTestId('focusedTab-value').textContent).toEqual('tab1'); + expect(ele.getByTestId('clipboardAction-length').textContent).toEqual('1'); + expect(ele.getByTestId('dialogFactory-existence').textContent).toEqual('true'); + expect(ele.getByTestId('customSchemas-length').textContent).toEqual('1'); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/SelectionContext.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/SelectionContext.test.tsx new file mode 100644 index 0000000000..9f3e15288a --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/contexts/SelectionContext.test.tsx @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React, { useContext } from 'react'; +import { render } from '@bfc/test-utils'; + +import { SelectionContext } from '../../../src/adaptive-flow-editor/contexts/SelectionContext'; + +describe('SelectionContext', () => { + const ContextConsumer = () => { + const { getNodeIndex, getSelectableIds, selectedIds, setSelectedIds, selectableElements } = useContext( + SelectionContext + ); + return ( +
+ {getNodeIndex('')} + {getSelectableIds().join(',')} + {selectedIds.join(',')} + {'' + !!setSelectedIds} + {selectableElements.length} +
+ ); + }; + it('can be provided.', () => { + const ele = render( + 1, + getSelectableIds: () => ['a', 'b', 'c'], + selectedIds: ['a'], + setSelectedIds: () => null, + selectableElements: [], + }} + > + + + ); + + expect(ele.getByTestId('getNodeIndex-result').textContent).toEqual('1'); + expect(ele.getByTestId('getSelectableIds-result').textContent).toEqual('a,b,c'); + expect(ele.getByTestId('selectedIds-str').textContent).toEqual('a'); + expect(ele.getByTestId('setSelectedIds-existence').textContent).toEqual('true'); + expect(ele.getByTestId('selectableElements-length').textContent).toEqual('0'); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/hooks/useEditorEventApi.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/hooks/useEditorEventApi.test.ts new file mode 100644 index 0000000000..64bfc2d68b --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/hooks/useEditorEventApi.test.ts @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { renderHook } from '@bfc/test-utils/lib/hooks'; + +import { useEditorEventApi } from '../../../src/adaptive-flow-editor/hooks/useEditorEventApi'; +import { ShellApiStub } from '../stubs/ShellApiStub'; +import { defaultRendererContextValue } from '../../../src/adaptive-flow-editor/contexts/NodeRendererContext'; +import { defaultSelectionContextValue } from '../../../src/adaptive-flow-editor/contexts/SelectionContext'; +import { NodeEventTypes } from '../../../src/adaptive-flow-renderer/constants/NodeEventTypes'; + +describe('useSelectionEffect', () => { + const hook = renderHook(() => + useEditorEventApi( + { + path: '', + data: {}, + nodeContext: { ...defaultRendererContextValue, focusedId: 'a' }, + selectionContext: { ...defaultSelectionContextValue, selectedIds: ['a'] }, + }, + ShellApiStub + ) + ).result.current; + + it('returns necessary apis.', () => { + expect(hook.handleEditorEvent).toBeTruthy(); + }); + + it('can handle events.', () => { + const { handleEditorEvent } = hook; + + handleEditorEvent('event.view.focus' as NodeEventTypes, {}); + handleEditorEvent('event.view.ctrl-click' as NodeEventTypes, {}); + handleEditorEvent('event.view.shift-click' as NodeEventTypes, {}); + handleEditorEvent('event.view.focus-event' as NodeEventTypes, {}); + handleEditorEvent('event.view.move-cursor' as NodeEventTypes, {}); + handleEditorEvent('event.nav.opendialog' as NodeEventTypes, {}); + handleEditorEvent('event.data.delete' as NodeEventTypes, {}); + handleEditorEvent('event.data.insert' as NodeEventTypes, {}); + handleEditorEvent('event.data.copy-selection' as NodeEventTypes, {}); + handleEditorEvent('event.data.cut-selection' as NodeEventTypes, {}); + handleEditorEvent('event.data.paste-selection' as NodeEventTypes, {}); + handleEditorEvent('event.data.move-selection' as NodeEventTypes, {}); + handleEditorEvent('event.data.delete-selection' as NodeEventTypes, {}); + handleEditorEvent('event.data.paste-selection--keyboard' as NodeEventTypes, {}); + handleEditorEvent('event.data.paste-selection--menu' as NodeEventTypes, {}); + handleEditorEvent('event.operation.undo' as NodeEventTypes, {}); + handleEditorEvent('event.operation.redo' as NodeEventTypes, {}); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/EdgeMenu.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/EdgeMenu.test.tsx new file mode 100644 index 0000000000..177479e59a --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/EdgeMenu.test.tsx @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; +import { DialogGroup } from '@bfc/shared'; + +import { EdgeMenu } from '../../../src/adaptive-flow-editor/renderers/EdgeMenu'; +import { createActionMenu } from '../../../src/adaptive-flow-editor/renderers/EdgeMenu/createSchemaMenu'; + +describe('', () => { + it('can render.', () => { + const menu = render( undefined} />); + expect(menu).toBeTruthy(); + }); +}); + +describe('createActionMenu()', () => { + it('options.enablePaste should control Paste button state.', () => { + const menuItems1 = createActionMenu(() => null, { isSelfHosted: false, enablePaste: true }); + expect(menuItems1.findIndex((x) => x.key === 'Paste')).toEqual(0); + expect(menuItems1[0].disabled).toBeFalsy(); + + const menuItems2 = createActionMenu(() => null, { isSelfHosted: false, enablePaste: false }); + expect(menuItems2[0].disabled).toBeTruthy(); + }); + + it('should return builtin $kinds.', () => { + const menuItemsHosted = createActionMenu(() => null, { isSelfHosted: true, enablePaste: true }); + expect(menuItemsHosted.findIndex((x) => x.key === DialogGroup.RESPONSE)).toBeTruthy(); + }); + + it('should show custom actions as last item.', () => { + const menuItemsWithoutCustomActions = createActionMenu(() => null, { isSelfHosted: false, enablePaste: false }, []); + expect(menuItemsWithoutCustomActions.findIndex((x) => x.key === 'Custom Actions')).toEqual(-1); + + const customActions = [ + [{ title: 'Custom1', description: 'Custom1', $ref: 'Group1.Custom1' }], + [{ title: 'Custom2', description: 'Custom2', $ref: 'Group2.Custom2' }], + ]; + const withCustomActions = createActionMenu(() => null, { isSelfHosted: false, enablePaste: false }, customActions); + expect(withCustomActions.findIndex((x) => x.key === 'Custom Actions')).toEqual(withCustomActions.length - 1); + expect(withCustomActions[withCustomActions.length - 1].subMenuProps?.items.length).toEqual(3); // 2 action labels + 1 sep line + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/ElementWrapper.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/ElementWrapper.test.tsx new file mode 100644 index 0000000000..81a2850a7b --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/ElementWrapper.test.tsx @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { ElementWrapper } from '../../../src/adaptive-flow-editor/renderers/ElementWrapper'; + +describe('', () => { + it('can render.', () => { + const ele = render( + + Content + + ); + + expect(ele).toBeTruthy(); + expect(ele.getByTestId('wrapped-content')).toBeTruthy(); + expect(ele.getByTestId('wrapped-content').textContent).toEqual('Content'); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/NodeWrapper.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/NodeWrapper.test.tsx new file mode 100644 index 0000000000..8ae45c2f02 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/renderers/NodeWrapper.test.tsx @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render, fireEvent } from '@bfc/test-utils'; +import ExtensionContext from '@bfc/extension/lib/extensionContext'; + +import { ActionNodeWrapper } from '../../../src/adaptive-flow-editor/renderers/NodeWrapper'; +import { ShellApiStub } from '../stubs/ShellApiStub'; + +describe('', () => { + it('can render.', () => { + const mockOnEvent = jest.fn(); + const ele = render( + + + + ); + expect(ele.getByTestId('ActionNodeWrapper')).toBeTruthy(); + + fireEvent.click(ele.getByTestId('ActionNodeWrapper')); + expect(mockOnEvent).toHaveBeenCalled(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/stubs/ShellApiStub.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/stubs/ShellApiStub.ts new file mode 100644 index 0000000000..71a0939388 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/stubs/ShellApiStub.ts @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ShellApi } from '@bfc/shared'; + +const fn = () => ({} as any); +const fnList = () => [] as any[]; +const fnPromise = () => Promise.resolve({} as any); + +export const ShellApiStub: ShellApi = { + getDialog: fn, + saveDialog: fn, + saveData: fn, + navTo: fn, + onFocusSteps: fn, + onFocusEvent: fn, + onSelect: fn, + getLgTemplates: fnList, + copyLgTemplate: fnPromise, + addLgTemplate: fnPromise, + updateLgTemplate: fnPromise, + removeLgTemplate: fnPromise, + removeLgTemplates: fnPromise, + getLuIntent: fn, + getLuIntents: fnList, + addLuIntent: fnPromise, + updateLuIntent: fnPromise, + removeLuIntent: fn, + updateRegExIntent: fn, + createDialog: fnPromise, + addCoachMarkRef: fn, + onCopy: fn, + undo: fn, + redo: fn, + updateUserSettings: fn, + addSkillDialog: fnPromise, + announce: fn, + displayManifestModal: fn, +}; + +describe('ShellApiStub', () => { + it('be truthy.', () => { + expect(ShellApiStub).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/NodeIndexGetter.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/NodeIndexGetter.test.ts new file mode 100644 index 0000000000..ebda36e5e4 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/NodeIndexGetter.test.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { NodeIndexGenerator } from '../../../src/adaptive-flow-editor/utils/NodeIndexGetter'; + +describe('NodeIndexGetter', () => { + it('can work e2e.', () => { + const n = new NodeIndexGenerator(); + + const aId = n.getNodeIndex('a'); + expect(aId).toEqual(0); + expect(n.getItemList()).toEqual([{ key: 'a' }]); + + const bId = n.getNodeIndex('b'); + expect(bId).toEqual(1); + expect(n.getItemList()).toEqual([{ key: 'a' }, { key: 'b' }]); + + const bId2 = n.getNodeIndex('b'); + expect(bId2).toEqual(1); + expect(n.getItemList()).toEqual([{ key: 'a' }, { key: 'b' }]); + + n.reset(); + expect(n.getItemList()).toEqual([]); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/calculateRangeSelection.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/calculateRangeSelection.test.ts new file mode 100644 index 0000000000..85457c6b55 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/calculateRangeSelection.test.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { calculateRangeSelection } from '../../../src/adaptive-flow-editor/utils/calculateRangeSelection'; + +describe('calculateRangeSelection()', () => { + it('could pick correct range.', () => { + expect(calculateRangeSelection('b', 'd', ['a', 'b', 'c', 'd'])).toEqual(['b', 'c', 'd']); + expect(calculateRangeSelection('d', 'b', ['a', 'b', 'c', 'd'])).toEqual(['b', 'c', 'd']); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/index.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/index.test.ts new file mode 100644 index 0000000000..6e83131758 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/index.test.ts @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { moveCursor } from '../../../../src/adaptive-flow-editor/utils/cursorTracker'; +import { KeyboardCommandTypes } from '../../../../src/adaptive-flow-editor/constants/KeyboardCommandTypes'; + +describe('moveCursor', () => { + it('returns undfined when no selectableElements.', () => { + expect(moveCursor([], 'test', KeyboardCommandTypes.Cursor.MovePrevious)).toEqual({ + selected: 'test', + focused: undefined, + }); + }); + + it('can handle Tab move.', () => { + expect( + moveCursor( + [ + { + selectedId: 'test', + focusedId: 'test-focused', + bounds: { left: 0, top: 0, right: 10, bottom: 10 }, + } as any, + ], + 'test', + KeyboardCommandTypes.Cursor.MoveNext + ) + ).toEqual({ focused: 'test-focused', selected: 'test', tab: '' }); + }); + + it('can handle Arrow move.', () => { + expect( + moveCursor( + [ + { + selectedId: 'test', + focusedId: 'test-focused', + bounds: { left: 0, top: 0, right: 10, bottom: 10 }, + } as any, + ], + 'test', + KeyboardCommandTypes.Cursor.MoveLeft + ) + ).toEqual({ focused: 'test-focused', selected: 'test', tab: '' }); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/type.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/type.test.ts new file mode 100644 index 0000000000..f2a8ab5f92 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/cursorTracker/type.test.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + SelectorElement, + Direction, + BoundRect, + Axle, +} from '../../../../src/adaptive-flow-editor/utils/cursorTracker/type'; + +describe('types', () => { + it('should be declared.', () => { + expect(SelectorElement).toBeTruthy(); + expect(Direction).toBeTruthy(); + expect(BoundRect).toBeTruthy(); + expect(Axle).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.test.ts new file mode 100644 index 0000000000..1c0d4b5658 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.test.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { mapKeyboardCommandToEditorEvent } from '../../../src/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent'; +import { + KeyboardPrimaryTypes, + KeyboardCommandTypes, +} from '../../../src/adaptive-flow-editor/constants/KeyboardCommandTypes'; +import { NodeEventTypes } from '../../../src/adaptive-flow-renderer/constants/NodeEventTypes'; + +describe('mapKeyboardCommandToEditorEvent()', () => { + it('can map event to correct result.', () => { + const validationChart = { + [KeyboardPrimaryTypes.Node]: { + [KeyboardCommandTypes.Node.Delete]: NodeEventTypes.DeleteSelection, + [KeyboardCommandTypes.Node.Copy]: NodeEventTypes.CopySelection, + [KeyboardCommandTypes.Node.Cut]: NodeEventTypes.CutSelection, + [KeyboardCommandTypes.Node.Paste]: NodeEventTypes.PasteSelection, + }, + [KeyboardPrimaryTypes.Operation]: { + [KeyboardCommandTypes.Operation.Undo]: NodeEventTypes.Undo, + [KeyboardCommandTypes.Operation.Redo]: NodeEventTypes.Redo, + }, + }; + + Object.keys(validationChart).forEach((area) => { + const subchart = validationChart[area]; + Object.keys(subchart).forEach((command) => { + const resultType = subchart[command]; + expect(mapKeyboardCommandToEditorEvent({ area, command })?.type).toEqual(resultType); + }); + }); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mergePluginConfig.test.ts b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mergePluginConfig.test.ts new file mode 100644 index 0000000000..e6f444199c --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-editor/utils/mergePluginConfig.test.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { mergePluginConfig } from '../../../src/adaptive-flow-editor/utils/mergePluginConfig'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('mergePluginConfig()', () => { + it('can generate correct config.', () => { + const plugins: any = [ + { + visualSchema: { + widgets: { widget1: 'w1' }, + schema: { [AdaptiveKinds.IfCondition]: 'widget1' }, + }, + }, + { + visualSchema: { + widgets: { widget2: 'w2' }, + schema: { [AdaptiveKinds.SwitchCondition]: 'widget2' }, + }, + }, + ]; + expect(mergePluginConfig(...plugins)).toMatchObject({ + widgets: { + widget1: 'w1', + widget2: 'w2', + }, + schema: { + [AdaptiveKinds.IfCondition]: 'widget1', + [AdaptiveKinds.SwitchCondition]: 'widget2', + }, + }); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ActionCard.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ActionCard.test.tsx new file mode 100644 index 0000000000..0ff082eae3 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ActionCard.test.tsx @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { ActionCard } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('ActionCard', () => { + it('can be rendered.', () => { + const card = render( null} />); + expect(card).toBeTruthy(); + }); + + it('can be rendered with injected content.', () => { + const card = render( + Body} + data={{ $kind: AdaptiveKinds.SendActivity }} + footer={Footer} + header={Header} + id="test" + onEvent={() => null} + /> + ); + expect(card.getByTestId('test-header')).toBeTruthy(); + expect(card.getByTestId('test-body')).toBeTruthy(); + expect(card.getByTestId('test-footer')).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/DialogRef.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/DialogRef.test.tsx new file mode 100644 index 0000000000..477089175c --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/DialogRef.test.tsx @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { DialogRef } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('DialogRef', () => { + it('can be rendered.', () => { + const dialogRef = render( + null} /> + ); + expect(dialogRef).toBeTruthy(); + }); + + it('can ref string dialog value correctly.', () => { + const dialogRef = render( + null} /> + ); + expect(dialogRef.queryAllByText('test-dialog')).toHaveLength(1); + }); + + it('can ref object dialog value correctly.', () => { + const dialogRef = render( + null} + /> + ); + expect(dialogRef.queryAllByText('test-dialog-obj')).toHaveLength(1); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ForeachWidget.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ForeachWidget.test.tsx new file mode 100644 index 0000000000..c9f3f24d09 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/ForeachWidget.test.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { ForeachWidget } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('ForeachWidget', () => { + it('can be rendered correctly.', () => { + const foreachNode = render( + Loop Head} + onEvent={() => null} + /> + ); + expect(foreachNode).toBeTruthy(); + expect(foreachNode.getByTestId('test-loop')).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/IfConditionWidget.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/IfConditionWidget.test.tsx new file mode 100644 index 0000000000..a6a872cf52 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/IfConditionWidget.test.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { IfConditionWidget } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('IfConditionWidget', () => { + it('can be rendered correctly.', () => { + const ifCondition = render( + Condition Judgement} + onEvent={() => null} + /> + ); + expect(ifCondition).toBeTruthy(); + expect(ifCondition.getByTestId('test-judgement')).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/PromptWidget.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/PromptWidget.test.tsx new file mode 100644 index 0000000000..5d3c89a208 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/PromptWidget.test.tsx @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { PromptWidget } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('PromptWidget', () => { + it('can be rendered correctly.', () => { + const promptNode = render( + BotAsks} + data={{ $kind: AdaptiveKinds.TextInput }} + id="test" + userInput={UserInput} + onEvent={() => null} + /> + ); + expect(promptNode).toBeTruthy(); + expect(promptNode.getByTestId('test-botAsks')).toBeTruthy(); + expect(promptNode.getByTestId('test-userInput')).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/SwitchConditionWidget.test.tsx b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/SwitchConditionWidget.test.tsx new file mode 100644 index 0000000000..89c65e65a5 --- /dev/null +++ b/Composer/packages/extensions/adaptive-flow/__tests__/adaptive-flow-renderer/widgets/SwitchConditionWidget.test.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import { render } from '@bfc/test-utils'; + +import { SwitchConditionWidget } from '../../../src/adaptive-flow-renderer/widgets'; +import { AdaptiveKinds } from '../../../src/adaptive-flow-renderer/constants/AdaptiveKinds'; + +describe('SwitchConditionWidget', () => { + it('can be rendered correctly.', () => { + const switchCondition = render( + Condition Judgement} + onEvent={() => null} + /> + ); + expect(switchCondition).toBeTruthy(); + expect(switchCondition.getByTestId('test-judgement')).toBeTruthy(); + }); +}); diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx index 0118d98d45..810167bdb5 100644 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx +++ b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx @@ -21,7 +21,7 @@ import { mergePluginConfig } from './utils/mergePluginConfig'; import { getCustomSchema } from './utils/getCustomSchema'; import { SelectionContext } from './contexts/SelectionContext'; import { KeyboardZone } from './components/KeyboardZone'; -import { mapKeyboardCommandToEditorEvent } from './utils/mapKeyboardCommandToEditorEvent.ts'; +import { mapKeyboardCommandToEditorEvent } from './utils/mapKeyboardCommandToEditorEvent'; import { useSelectionEffect } from './hooks/useSelectionEffect'; import { useEditorEventApi } from './hooks/useEditorEventApi'; import { diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/components/KeyboardZone.tsx b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/components/KeyboardZone.tsx index 788e76d8ae..7f97598d45 100644 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/components/KeyboardZone.tsx +++ b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/components/KeyboardZone.tsx @@ -62,7 +62,7 @@ export const KeyboardZone = React.forwardRef( }; return ( -
+
{children}
); diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/NodeRendererContext.ts b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/NodeRendererContext.ts index 4a8cc7d126..d78c9e64e5 100644 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/NodeRendererContext.ts +++ b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/NodeRendererContext.ts @@ -13,11 +13,12 @@ export interface NodeRendererContextValue { customSchemas: OBISchema[]; } -export const NodeRendererContext = React.createContext({ +export const defaultRendererContextValue = { focusedId: '', focusedEvent: '', focusedTab: '', clipboardActions: [], - dialogFactory: new DialogFactory(), + dialogFactory: new DialogFactory({}), customSchemas: [], -}); +}; +export const NodeRendererContext = React.createContext(defaultRendererContextValue); diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/SelectionContext.ts b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/SelectionContext.ts index a7b576af26..c3a48ee572 100644 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/SelectionContext.ts +++ b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/contexts/SelectionContext.ts @@ -13,10 +13,11 @@ export interface SelectionContextData { selectableElements: SelectorElement[]; } -export const SelectionContext = React.createContext({ +export const defaultSelectionContextValue = { getNodeIndex: (_: string): number => 0, getSelectableIds: () => [], selectedIds: [] as string[], setSelectedIds: () => null, selectableElements: [], -}); +}; +export const SelectionContext = React.createContext(defaultSelectionContextValue); diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/hooks/useWindowDimensions.ts b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/hooks/useWindowDimensions.ts deleted file mode 100644 index 6707cce97a..0000000000 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/hooks/useWindowDimensions.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { useState, useEffect, useRef } from 'react'; -import debounce from 'lodash/debounce'; - -const getWindowDimensions = () => { - const { innerWidth: width, innerHeight: height } = window; - return { - width, - height, - }; -}; - -export const useWindowDimensions = () => { - const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions()); - const handleResize = useRef(debounce(() => setWindowDimensions(getWindowDimensions()), 200)).current; - - useEffect(() => { - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - return windowDimensions; -}; diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/renderers/NodeWrapper.tsx b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/renderers/NodeWrapper.tsx index 3255718da8..c8799f64df 100644 --- a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/renderers/NodeWrapper.tsx +++ b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/renderers/NodeWrapper.tsx @@ -74,6 +74,7 @@ export const ActionNodeWrapper: FC = ({ id, tab, data, onEvent ${!nodeFocused && nodeBorderHoveredStyle} } `} + data-testid="ActionNodeWrapper" {...declareElementAttributes(selectableId, id)} aria-label={generateSDKTitle(data, '', tab)} onClick={(e) => { diff --git a/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.ts.ts b/Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.ts similarity index 100% rename from Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.ts.ts rename to Composer/packages/extensions/adaptive-flow/src/adaptive-flow-editor/utils/mapKeyboardCommandToEditorEvent.ts diff --git a/Composer/packages/extensions/extension/src/index.ts b/Composer/packages/extensions/extension/src/index.ts index 3ecebc1a7c..931425d29c 100644 --- a/Composer/packages/extensions/extension/src/index.ts +++ b/Composer/packages/extensions/extension/src/index.ts @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { Extension } from './components'; +import extensionContext from './extensionContext'; export * from './components'; export * from './hooks'; export * from './types'; export default Extension; + +export const ExtensionContext = extensionContext; From ab5dc06f936af7910235850aa4b66d2bb5bbcd12 Mon Sep 17 00:00:00 2001 From: liweitian Date: Thu, 2 Jul 2020 00:57:47 +0800 Subject: [PATCH 2/3] update a test case (#3531) Co-authored-by: Chris Whitten --- .../CreationFlow/LocationBrowser/FileSelector.test.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/__tests__/components/CreationFlow/LocationBrowser/FileSelector.test.tsx b/Composer/packages/client/__tests__/components/CreationFlow/LocationBrowser/FileSelector.test.tsx index 4454a51810..2cd9b23ebc 100644 --- a/Composer/packages/client/__tests__/components/CreationFlow/LocationBrowser/FileSelector.test.tsx +++ b/Composer/packages/client/__tests__/components/CreationFlow/LocationBrowser/FileSelector.test.tsx @@ -82,14 +82,18 @@ describe('', () => { expect(await component.findByText('You do not have permission to save bots here')).toBeInTheDocument(); }); - it('should update folder name', async () => { + it('should create a new folder', async () => { const component = renderComponent(); const createFolderBtn = await component.findByText('create new folder'); fireEvent.click(createFolderBtn); const textField = await component.findByTestId('newFolderTextField'); fireEvent.change(textField, { target: { value: 'newFolder' } }); fireEvent.keyDown(textField, { key: 'Enter' }); - //locally this should be 'C:\\test-folder\\Desktop', but it should be 'C:/test-folder/Desktop' online - expect(createFolder).toBeCalledWith('C:/test-folder/Desktop', 'newFolder'); + //locally this should be 'C:\\test-folder\\Desktop', but online it should be 'C:/test-folder/Desktop' + expect( + createFolder.mock.calls[0][0] === 'C:/test-folder/Desktop' || + createFolder.mock.calls[0][0] === 'C:\\test-folder\\Desktop' + ).toBeTruthy(); + expect(createFolder.mock.calls[0][1]).toBe('newFolder'); }); }); From 4242f7c990c0866823f1c2996689eefbc9971552 Mon Sep 17 00:00:00 2001 From: Zhixiang Zhan Date: Thu, 2 Jul 2020 01:46:01 +0800 Subject: [PATCH 3/3] fix lgWorker test failure (#3529) Co-authored-by: Chris Whitten --- .../language-generation/src/lgParser.ts | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts b/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts index 9a2621802d..07a597bd6b 100644 --- a/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts +++ b/Composer/packages/tools/language-servers/language-generation/src/lgParser.ts @@ -4,28 +4,51 @@ import { fork, ChildProcess } from 'child_process'; import path from 'path'; +import { Templates, Diagnostic } from 'botbuilder-lg'; +import { importResolverGenerator } from '@bfc/shared'; import { ResolverResource } from '@bfc/shared'; import uniqueId from 'lodash/uniqueId'; const isTest = process.env?.NODE_ENV === 'test'; - export interface WorkerMsg { id: string; error?: any; payload?: any; } -// Wrapper class -export class LgParser { +function createDiagnostic(diagnostic: Diagnostic) { + const { code, range, severity, source, message } = diagnostic; + const { start, end } = range; + return { + code, + range: { + start: { line: start.line, character: start.character }, + end: { line: end.line, character: end.character }, + }, + severity, + source, + message, + }; +} + +class LgParserWithoutWorker { + public async parseText(content: string, id: string, resources: ResolverResource[]) { + const resolver = importResolverGenerator(resources, '.lg'); + const { allTemplates, allDiagnostics } = Templates.parseText(content, id, resolver); + const templates = allTemplates.map((item) => ({ name: item.name, parameters: item.parameters, body: item.body })); + const diagnostics = allDiagnostics.map((item) => createDiagnostic(item)); + return { templates, diagnostics }; + } +} + +class LgParserWithWorker { private worker: ChildProcess; private resolves = {}; private rejects = {}; constructor() { - const fileName = isTest ? 'lgWorker.ts' : 'lgWorker.js'; - const execArgv = isTest ? ['-r', 'ts-node/register'] : []; - const workerScriptPath = path.join(__dirname, fileName); - this.worker = fork(workerScriptPath, [], { execArgv }); + const workerScriptPath = path.join(__dirname, 'lgWorker.js'); + this.worker = fork(workerScriptPath, []); this.worker.on('message', this.handleMsg.bind(this)); } @@ -55,3 +78,8 @@ export class LgParser { delete this.rejects[id]; } } + +// Do not use worker when running test. +const LgParser = isTest ? LgParserWithoutWorker : LgParserWithWorker; + +export { LgParser };