Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit a9a8fdd

Browse files
authored
feat: Local Bots Start/Stop runtime (#4498)
1 parent 2348502 commit a9a8fdd

File tree

100 files changed

+2834
-1850
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+2834
-1850
lines changed

Composer/cypress/integration/LuisDeploy.spec.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,32 @@ context('Luis Deploy', () => {
66
cy.server();
77
cy.route('POST', '/api/publish/*/publish/default', { endpointURL: 'anything', status: 202 });
88
cy.route('POST', '/api/projects/*/settings', 'OK');
9-
cy.route('GET', '/api/publish/*/status/default', { endpointURL: 'anything', status: 200 });
9+
cy.route('GET', '/api/publish/*/status/default', { endpointURL: 'anything', status: 404 });
1010
cy.visit('/home');
1111
cy.createBot('ToDoBotWithLuisSample');
1212
});
1313

1414
it('can deploy luis success', () => {
1515
cy.findByTestId('LeftNav-CommandBarButtonUser Input').click();
1616
cy.url().should('contain', 'language-understanding/all');
17+
cy.findByTestId('LeftNav-CommandBarButtonDesign').click();
1718
cy.route({
1819
method: 'POST',
1920
url: 'api/projects/*/build',
20-
status: 200,
21-
response: 'fixture:luPublish/success',
21+
status: 400,
22+
response: 'fixture:luPublish/failure',
2223
});
23-
cy.findByText(/^(Start|Restart) Bot$/).click();
24-
25-
// clear its settings before
26-
cy.enterTextAndSubmit('ProjectNameInput', 'MyProject');
27-
cy.enterTextAndSubmit('EnvironmentInput', 'composer');
28-
cy.enterTextAndSubmit('AuthoringKeyInput', '0d4991873f334685a9686d1b48e0ff48');
29-
// wait for the debounce interval of sync settings
30-
cy.findByText('OK').click();
31-
cy.findByText('Restart Bot').should('exist');
32-
cy.findByText('Test in Emulator').should('exist');
24+
cy.findByText(/^Start all bots/).click();
25+
cy.findByTitle('Open start bots panel').click();
26+
cy.findByText('See Details').click();
3327

3428
cy.route({
3529
method: 'POST',
3630
url: 'api/projects/*/build',
37-
status: 400,
38-
response: 'fixture:luPublish/error',
31+
status: 200,
32+
response: 'fixture:luPublish/success',
3933
});
40-
cy.findByText('Restart Bot').click();
4134
cy.findByText('Try again').click();
42-
cy.findByTestId('AuthoringKeyInput').type('no-id');
43-
cy.findByText('OK').click();
35+
cy.findByText(/^Stop all bots/).click();
4436
});
4537
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import * as React from 'react';
5+
import { fireEvent, act } from '@botframework-composer/test-utils';
6+
7+
import { OpenEmulatorButton } from '../../../src/components/BotRuntimeController/OpenEmulatorButton';
8+
import { botEndpointsState, botStatusState, settingsState } from '../../../src/recoilModel';
9+
import { BotStatus } from '../../../src/constants';
10+
import { renderWithRecoil } from '../../testUtils';
11+
12+
const mockCallEmulator = jest.fn();
13+
14+
jest.mock('../../../src/utils/navigation', () => {
15+
return {
16+
openInEmulator: mockCallEmulator,
17+
};
18+
});
19+
20+
jest.mock('office-ui-fabric-react/lib/Button', () => ({
21+
ActionButton: ({ onClick, children }) => (
22+
<button data-testid="button" onClick={onClick}>
23+
{children}
24+
</button>
25+
),
26+
IconButton: ({ onClick, children }) => (
27+
<button data-testid="iconButton" onClick={onClick}>
28+
{children}
29+
</button>
30+
),
31+
}));
32+
33+
jest.mock('office-ui-fabric-react/lib/Tooltip', () => ({
34+
TooltipHost: ({ children }) => <div>{children}</div>,
35+
}));
36+
37+
const projectId = '123.abc';
38+
39+
const initialState = ({ currentStatus = BotStatus.connected } = {}) => ({ set }) => {
40+
set(botStatusState(projectId), currentStatus);
41+
set(botEndpointsState, { [projectId]: 'http://open-in-emulator/api/messages' });
42+
set(settingsState(projectId), {});
43+
};
44+
45+
describe('<OpenEmulatorButton />', () => {
46+
it('should show the button to open emulator', async () => {
47+
mockCallEmulator.mockImplementationOnce((url) => {
48+
expect(url).toBeDefined();
49+
});
50+
const { findByTestId } = renderWithRecoil(<OpenEmulatorButton projectId={projectId} />, initialState());
51+
const button = await findByTestId('button');
52+
act(() => {
53+
fireEvent.click(button);
54+
});
55+
});
56+
57+
it('should not show the button if the status is not `BotStatus.connected`', () => {
58+
const { container } = renderWithRecoil(
59+
<OpenEmulatorButton projectId={projectId} />,
60+
initialState({ currentStatus: BotStatus.pending })
61+
);
62+
expect(container).not.toHaveTextContent('Test in Emulator');
63+
});
64+
});

Composer/packages/client/__tests__/components/TestController/errorCallout.test.tsx renamed to Composer/packages/client/__tests__/components/BotRuntimeController/errorCallout.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import * as React from 'react';
55
import { render, fireEvent, getByText } from '@botframework-composer/test-utils';
66

7-
import { ErrorCallout } from '../../../src/components/TestController/errorCallout';
7+
import { ErrorCallout } from '../../../src/components/BotRuntimeController/errorCallout';
88

99
describe('<ErrorCallout />', () => {
1010
it('should render the <ErrorCallout />', () => {

Composer/packages/client/__tests__/components/TestController/errorInfo.test.tsx renamed to Composer/packages/client/__tests__/components/BotRuntimeController/errorInfo.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import * as React from 'react';
55
import { render, fireEvent } from '@botframework-composer/test-utils';
66

7-
import { ErrorInfo } from '../../../src/components/TestController/errorInfo';
7+
import { ErrorInfo } from '../../../src/components/BotRuntimeController/errorInfo';
88

99
describe('<ErrorInfo />', () => {
1010
it('should render <ErrorInfo />', () => {

Composer/packages/client/__tests__/components/TestController/loading.test.tsx renamed to Composer/packages/client/__tests__/components/BotRuntimeController/loading.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import * as React from 'react';
55
import { render } from '@botframework-composer/test-utils';
66

7-
import { Loading } from '../../../src/components/TestController/loading';
7+
import { Loading } from '../../../src/components/BotRuntimeController/loading';
88
import { BotStatus } from '../../../src/constants';
99

1010
describe('<Loading />', () => {

Composer/packages/client/__tests__/components/TestController/publish-luis-modal.test.tsx renamed to Composer/packages/client/__tests__/components/BotRuntimeController/publish-luis-modal.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as React from 'react';
44
import { fireEvent } from '@botframework-composer/test-utils';
55

6-
import { PublishDialog } from '../../../src/components/TestController/publishDialog';
6+
import { PublishDialog } from '../../../src/components/BotRuntimeController/publishDialog';
77
import { botDisplayNameState, settingsState, dispatcherState, currentProjectIdState } from '../../../src/recoilModel';
88
import { renderWithRecoil } from '../../testUtils';
99
jest.useFakeTimers();

Composer/packages/client/__tests__/components/TestController/emulatorOpenButton.test.tsx

Lines changed: 0 additions & 56 deletions
This file was deleted.

Composer/packages/client/__tests__/components/header.test.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,35 @@ import * as React from 'react';
55

66
import { renderWithRecoil } from '../testUtils';
77
import { Header } from '../../src/components/Header';
8+
import { currentModeState } from '../../src/recoilModel';
89

910
describe('<Header />', () => {
1011
it('should render the header', () => {
1112
const { container } = renderWithRecoil(<Header />);
12-
1313
expect(container).toHaveTextContent('Bot Framework Composer');
1414
});
15+
16+
it('should not show the start bots widget in Home page', async () => {
17+
const initRecoilState = ({ set }) => {
18+
set(currentModeState, 'home');
19+
};
20+
const { queryByText } = renderWithRecoil(<Header />, initRecoilState);
21+
expect(queryByText('Start all bots')).toBeNull();
22+
});
23+
24+
it('should show the start bots widget on design page', async () => {
25+
const initRecoilState = ({ set }) => {
26+
set(currentModeState, 'design');
27+
};
28+
const result = renderWithRecoil(<Header />, initRecoilState);
29+
expect(result.queryByText('Start all bots')).not.toBeNull();
30+
});
31+
32+
it('should show the start bots widget on settings page', async () => {
33+
const initRecoilState = ({ set }) => {
34+
set(currentModeState, 'settings');
35+
};
36+
const result = renderWithRecoil(<Header />, initRecoilState);
37+
expect(result.queryByText('Start all bots')).not.toBeNull();
38+
});
1539
});

0 commit comments

Comments
 (0)