• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Microsoft / BotFramework-Emulator / 389488

22 May 2024 05:28PM UTC coverage: 45.955% (-21.6%) from 67.551%
389488

Pull #2459

Azure DevOps

web-flow
Merge 316c951db into 012a66410
Pull Request #2459: Bumped webpack version from 4.32.2 to 5.91.0

1811 of 4558 branches covered (39.73%)

Branch coverage included in aggregate %.

4722 of 9658 relevant lines covered (48.89%)

2.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx
1
//
2
// Copyright (c) Microsoft. All rights reserved.
3
// Licensed under the MIT license.
4
//
5
// Microsoft Bot Framework: http://botframework.com
6
//
7
// Bot Framework Emulator Github:
8
// https://github.com/Microsoft/BotFramwork-Emulator
9
//
10
// Copyright (c) Microsoft Corporation
11
// All rights reserved.
12
//
13
// MIT License:
14
// Permission is hereby granted, free of charge, to any person obtaining
15
// a copy of this software and associated documentation files (the
16
// "Software"), to deal in the Software without restriction, including
17
// without limitation the rights to use, copy, modify, merge, publish,
18
// distribute, sublicense, and/or sell copies of the Software, and to
19
// permit persons to whom the Software is furnished to do so, subject to
20
// the following conditions:
21
//
22
// The above copyright notice and this permission notice shall be
23
// included in all copies or substantial portions of the Software.
24
//
25
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
//
33

34
import {
35
  AutoComplete,
36
  Checkbox,
37
  DefaultButton,
38
  Dialog,
39
  DialogFooter,
40
  LinkButton,
41
  PrimaryButton,
42
  Row,
43
  TextField,
44
} from '@bfemulator/ui-react';
45
import * as React from 'react';
46
import { ChangeEvent, Component, MouseEvent, ReactNode } from 'react';
47
import { EmulatorMode } from '@bfemulator/sdk-shared';
48
import { isLinux } from '@bfemulator/app-shared';
49

50
import * as dialogStyles from '../dialogStyles.scss';
51

52
import * as openBotStyles from './openBotDialog.scss';
53

54
export interface OpenBotDialogProps {
55
  appId?: string;
56
  appPassword?: string;
57
  botUrl?: string;
58
  isAzureGov?: boolean;
59
  isDebug?: boolean;
60
  mode?: EmulatorMode;
61
  onAnchorClick?: (url: string) => void;
62
  onDialogCancel?: () => void;
63
  openBot?: (state: OpenBotDialogState) => void;
64
  savedBotUrls?: { url: string; lastAccessed: string }[];
65
  sendNotification?: (error: Error) => void;
66
  switchToBot?: (path: string) => void;
67
}
68

69
export interface OpenBotDialogState {
70
  isDebug?: boolean;
71
  mode?: EmulatorMode;
72
  botUrl?: string;
73
  appId?: string;
74
  appPassword?: string;
75
  isAzureGov?: boolean;
76
  randomSeed?: number;
77
  randomValue?: number;
78
  speechKey?: string;
79
  speechRegion?: string;
80
}
81

82
enum ValidationResult {
83
  Invalid,
84
  RouteMissing,
85
  Valid,
86
  Empty,
87
}
88

89
export class OpenBotDialog extends Component<OpenBotDialogProps, OpenBotDialogState> {
90
  private static getErrorMessage(result: ValidationResult): string {
91
    if (result === ValidationResult.Empty || result === ValidationResult.Valid) {
×
92
      return ''; // Allow empty endpoints
×
93
    }
94

95
    return result === ValidationResult.Invalid
×
96
      ? 'Please choose a valid bot file or endpoint URL'
97
      : `Please include route if necessary: "/api/messages"`;
98
  }
99

100
  private static validateEndpoint(endpoint: string): ValidationResult {
101
    if (!endpoint) {
×
102
      return ValidationResult.Empty;
×
103
    }
104

105
    if (/(http)(s)?(:\/\/)[\w+]/.test(endpoint)) {
×
106
      return endpoint.endsWith('/api/messages') ? ValidationResult.Valid : ValidationResult.RouteMissing;
×
107
    }
108

109
    return endpoint.endsWith('.bot') ? ValidationResult.Valid : ValidationResult.Invalid;
×
110
  }
111

112
  constructor(props: OpenBotDialogProps) {
113
    super(props);
×
114
    const { appId = '', appPassword = '', botUrl = '', isAzureGov = false, isDebug = false, mode = 'livechat' } = props;
×
115
    this.state = {
×
116
      appId,
117
      appPassword,
118
      botUrl,
119
      isAzureGov,
120
      isDebug,
121
      mode,
122
    };
123
  }
124

125
  private onEmulatorAzureGovDocsClick = () => {
×
126
    this.props.onAnchorClick('https://aka.ms/bot-framework-emulator-azuregov');
×
127
  };
128

129
  public render(): ReactNode {
130
    const { savedBotUrls = [] } = this.props;
×
131
    const {
132
      botUrl,
133
      appId,
134
      appPassword,
135
      mode,
136
      isDebug,
137
      isAzureGov,
138
      randomSeed,
139
      randomValue,
140
      speechKey,
141
      speechRegion,
142
    } = this.state;
×
143
    const validationResult = OpenBotDialog.validateEndpoint(botUrl);
×
144
    const errorMessage = OpenBotDialog.getErrorMessage(validationResult);
×
145
    const shouldBeDisabled =
146
      validationResult === ValidationResult.Invalid || validationResult === ValidationResult.Empty;
×
147

148
    return (
×
149
      <Dialog cancel={this.props.onDialogCancel} className={openBotStyles.themeOverrides} title="Open a bot">
150
        <form id="open-bot-dialog" role="presentation" onSubmit={this.onSubmit}>
151
          <div className={openBotStyles.autoCompleteBar}>
152
            <AutoComplete
153
              autoFocus={true}
154
              errorMessage={errorMessage}
155
              label={'Bot URL'}
156
              items={savedBotUrls.map(elem => elem.url).slice(0, 9)}
×
157
              onChange={this.onBotUrlChange}
158
              placeholder={'Enter your bot URL'}
159
              value={this.state.botUrl}
160
            />
161
            {this.browseButton}
162
          </div>
163
          <Row className={openBotStyles.multiInputRow}>
164
            <TextField
165
              inputContainerClassName={openBotStyles.inputContainerRow}
166
              name="appId"
167
              label="Microsoft App ID"
168
              onChange={this.onInputChange}
169
              placeholder="Optional"
170
              value={appId}
171
            />
172
            <TextField
173
              inputContainerClassName={openBotStyles.inputContainerRow}
174
              label="Microsoft App password"
175
              ariaLabel={isLinux() ? 'Microsoft App' : null}
×
176
              name="appPassword"
177
              onChange={this.onInputChange}
178
              placeholder="Optional"
179
              type="password"
180
              value={appPassword}
181
            />
182
          </Row>
183
          {!isDebug && (
×
184
            <Row className={openBotStyles.multiInputRow}>
185
              <TextField
186
                inputContainerClassName={openBotStyles.inputContainerRow}
187
                name="speechRegion"
188
                label="Direct Line Speech Region"
189
                onChange={this.onInputChange}
190
                placeholder="Optional"
191
                value={speechRegion}
192
              />
193
              <TextField
194
                inputContainerClassName={openBotStyles.inputContainerRow}
195
                label="Direct Line Speech Key"
196
                name="speechKey"
197
                onChange={this.onInputChange}
198
                placeholder="Optional"
199
                type="password"
200
                value={speechKey}
201
              />
202
            </Row>
203
          )}
204
          <Row className={openBotStyles.multiInputRow}>
205
            <TextField
206
              inputContainerClassName={openBotStyles.inputContainerRow}
207
              name="randomSeed"
208
              label="Test Options - Random Seed"
209
              onChange={this.onInputChange}
210
              placeholder="Optional"
211
              type="number"
212
              value={randomSeed}
213
              min="0"
214
              max="9999"
215
            />
216
            <TextField
217
              inputContainerClassName={openBotStyles.inputContainerRow}
218
              label="Test Options - Random Value"
219
              name="randomValue"
220
              onChange={this.onInputChange}
221
              placeholder="Optional"
222
              type="number"
223
              value={randomValue}
224
              min="0"
225
              max="9999"
226
            />
227
          </Row>
228
          <Row className={openBotStyles.rowOverride}>
229
            <Checkbox
230
              label="Open in debug mode"
231
              checked={isDebug && mode === 'debug'}
×
232
              onClick={this.onDebugCheckboxClick}
233
            />
234
          </Row>
235
          <Row className={openBotStyles.rowOverride}>
236
            <Checkbox
237
              label="Azure for US Government"
238
              checked={isAzureGov}
239
              onClick={this.onChannelServiceCheckboxClick}
240
            />
241
            <LinkButton
242
              ariaLabel="Learn more about Azure for US Government"
243
              className={dialogStyles.dialogLink}
244
              linkRole={true}
245
              onClick={this.onEmulatorAzureGovDocsClick}
246
              type="button"
247
            >
248
              &nbsp;Learn more.
249
            </LinkButton>
250
          </Row>
251
          <DialogFooter>
252
            <DefaultButton type="button" onClick={this.props.onDialogCancel}>
253
              Cancel
254
            </DefaultButton>
255
            <PrimaryButton type="submit" disabled={shouldBeDisabled} id={'connect-open-bot'}>
256
              Connect
257
            </PrimaryButton>
258
          </DialogFooter>
259
        </form>
260
      </Dialog>
261
    );
262
  }
263

264
  private onInputChange = (event: ChangeEvent<HTMLInputElement>) => {
×
265
    const { type, files, value, name } = event.target;
×
266
    const newValue = type === 'file' ? files.item(0).path : value;
×
267
    this.setState({ [name]: newValue });
×
268
  };
269

270
  private onChannelServiceCheckboxClick = (event: MouseEvent<HTMLInputElement>) => {
×
271
    const { checked: isAzureGov } = event.currentTarget;
×
272
    const newState = { ...this.state, isAzureGov } as OpenBotDialogState;
×
273

274
    if (isAzureGov && !this.state.botUrl.startsWith('http')) {
×
275
      newState.botUrl = '';
×
276
    }
277

278
    this.setState(newState);
×
279
  };
280

281
  private onDebugCheckboxClick = (event: MouseEvent<HTMLInputElement>) => {
×
282
    const { checked: isDebug } = event.currentTarget;
×
283
    const newState = { isDebug, mode: isDebug ? 'debug' : 'livechat-url' } as OpenBotDialogState;
×
284

285
    if (isDebug && !this.state.botUrl.startsWith('http')) {
×
286
      newState.botUrl = '';
×
287
    }
288

289
    this.setState(newState);
×
290
  };
291

292
  private onBotUrlChange = (botUrl: string) => {
×
293
    const newState = { botUrl } as OpenBotDialogState;
×
294
    newState.mode = botUrl.startsWith('http') ? 'livechat-url' : 'livechat';
×
295
    this.setState({ botUrl });
×
296
  };
297

298
  private onSubmit = () => {
×
299
    this.props.openBot(this.state);
×
300
  };
301

302
  private get browseButton(): React.ReactNode {
303
    if (!this.state.isAzureGov && !this.state.isDebug) {
×
304
      return (
×
305
        <div className={openBotStyles.browseButton}>
306
          <input accept=".bot" id="openBotBrowse" name="botUrl" onChange={this.onInputChange} type="file" />
307
          <label htmlFor="openBotBrowse" aria-hidden="true">
308
            Browse
309
          </label>
310
        </div>
311
      );
312
    }
313
    return null;
×
314
  }
315
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc