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

Microsoft / BotFramework-Emulator / 389595

23 May 2024 02:16AM UTC coverage: 45.975% (-21.6%) from 67.547%
389595

Pull #2459

Azure DevOps

web-flow
Merge 88a7da94c into e554dc423
Pull Request #2459: Bumped webpack version from 4.32.2 to 5.91.0

1815 of 4567 branches covered (39.74%)

Branch coverage included in aggregate %.

4730 of 9669 relevant lines covered (48.92%)

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 {
115
      appId = '',
×
116
      appPassword = '',
×
117
      botUrl = '',
×
118
      isAzureGov = false,
×
119
      isDebug = false,
×
120
      mode = 'livechat',
×
121
      tenantId,
122
    } = props;
×
123
    this.state = {
×
124
      appId,
125
      appPassword,
126
      tenantId,
127
      botUrl,
128
      isAzureGov,
129
      isDebug,
130
      mode,
131
    };
132
  }
133

134
  private onEmulatorAzureGovDocsClick = () => {
×
135
    this.props.onAnchorClick('https://aka.ms/bot-framework-emulator-azuregov');
×
136
  };
137

138
  public render(): ReactNode {
139
    const { savedBotUrls = [] } = this.props;
×
140
    const {
141
      botUrl,
142
      appId,
143
      appPassword,
144
      tenantId,
145
      mode,
146
      isDebug,
147
      isAzureGov,
148
      randomSeed,
149
      randomValue,
150
      speechKey,
151
      speechRegion,
152
    } = this.state;
×
153
    const validationResult = OpenBotDialog.validateEndpoint(botUrl);
×
154
    const errorMessage = OpenBotDialog.getErrorMessage(validationResult);
×
155
    const shouldBeDisabled =
156
      validationResult === ValidationResult.Invalid || validationResult === ValidationResult.Empty;
×
157

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

281
  private onInputChange = (event: ChangeEvent<HTMLInputElement>) => {
×
282
    const { type, files, value, name } = event.target;
×
283
    const newValue = type === 'file' ? files.item(0).path : value;
×
284
    this.setState({ [name]: newValue });
×
285
  };
286

287
  private onChannelServiceCheckboxClick = (event: MouseEvent<HTMLInputElement>) => {
×
288
    const { checked: isAzureGov } = event.currentTarget;
×
289
    const newState = { ...this.state, isAzureGov } as OpenBotDialogState;
×
290

291
    if (isAzureGov && !this.state.botUrl.startsWith('http')) {
×
292
      newState.botUrl = '';
×
293
    }
294

295
    this.setState(newState);
×
296
  };
297

298
  private onDebugCheckboxClick = (event: MouseEvent<HTMLInputElement>) => {
×
299
    const { checked: isDebug } = event.currentTarget;
×
300
    const newState = { isDebug, mode: isDebug ? 'debug' : 'livechat-url' } as OpenBotDialogState;
×
301

302
    if (isDebug && !this.state.botUrl.startsWith('http')) {
×
303
      newState.botUrl = '';
×
304
    }
305

306
    this.setState(newState);
×
307
  };
308

309
  private onBotUrlChange = (botUrl: string) => {
×
310
    const newState = { botUrl } as OpenBotDialogState;
×
311
    newState.mode = botUrl.startsWith('http') ? 'livechat-url' : 'livechat';
×
312
    this.setState({ botUrl });
×
313
  };
314

315
  private onSubmit = () => {
×
316
    this.props.openBot(this.state);
×
317
  };
318

319
  private get browseButton(): React.ReactNode {
320
    if (!this.state.isAzureGov && !this.state.isDebug) {
×
321
      return (
×
322
        <div className={openBotStyles.browseButton}>
323
          <input accept=".bot" id="openBotBrowse" name="botUrl" onChange={this.onInputChange} type="file" />
324
          <label htmlFor="openBotBrowse" aria-hidden="true">
325
            Browse
326
          </label>
327
        </div>
328
      );
329
    }
330
    return null;
×
331
  }
332
}
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