Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Commit cea9a86

Browse files
authored
WIP: Ngrok Debugger Implementation (#2032)
* Basic ngrok debugger setup * Code cleanup * Renaming functions, test names, using shorthands wherever applicable * Pr feedback addressed
1 parent d496897 commit cea9a86

67 files changed

Lines changed: 2839 additions & 85050 deletions

Some content is hidden

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

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ build/
1111
.vscode/*
1212
chrome-driver-log.txt
1313
.env
14+
/packages/**/package-lock.json

package-lock.json

Lines changed: 525 additions & 486 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/app/client/package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"lint:fix": "npm run lint -- --fix",
1616
"start": "run-s build:shared:dev webpackdevServer:dev",
1717
"webpackdevServer:dev": "webpack-dev-server --mode development --hot --inline --progress --colors --content-base ./public",
18-
"test": "jest"
18+
"test": "jest",
19+
"test:watch": "jest --watch"
1920
},
2021
"jest": {
2122
"setupTestFrameworkScriptFile": "../../../../testSetup.js",
@@ -51,8 +52,8 @@
5152
"@babel/preset-typescript": "^7.1.0",
5253
"@types/enzyme": "^3.1.10",
5354
"@types/jest": "24.0.13",
54-
"@types/react": "~16.3.2",
55-
"@types/react-dom": "^16.0.4",
55+
"@types/react": "16.9.17",
56+
"@types/react-dom": "16.9.4",
5657
"@types/request": "^2.47.0",
5758
"babel-eslint": "^10.0.1",
5859
"babel-jest": "24.8.0",
@@ -111,8 +112,8 @@
111112
"botframework-webchat-core": "4.7.1",
112113
"eslint-plugin-react": "^7.12.3",
113114
"markdown-it": "^8.4.2",
114-
"react": "^16.8.6",
115-
"react-dom": "^16.8.6",
115+
"react": "16.8.6",
116+
"react-dom": "16.8.6",
116117
"react-redux": "^5.0.7",
117118
"react-router-dom": "^4.2.2",
118119
"redux": "^3.7.2",

packages/app/client/src/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ export const CONTENT_TYPE_APP_SETTINGS = 'application/vnd.microsoft.bfemulator.d
3939
export const CONTENT_TYPE_WELCOME_PAGE = 'application/vnd.microsoft.bfemulator.document.welcome';
4040
export const CONTENT_TYPE_TRANSCRIPT = 'application/vnd.microsoft.bfemulator.document.transcript';
4141
export const CONTENT_TYPE_LIVE_CHAT = SharedConstants.ContentTypes.CONTENT_TYPE_LIVE_CHAT;
42+
export const CONTENT_TYPE_NGROK_DEBUGGER = SharedConstants.ContentTypes.CONTENT_TYPE_NGROK_DEBUGGER;
4243

4344
export const NAVBAR_BOT_EXPLORER = 'navbar.botExplorer';
4445
export const NAVBAR_SETTINGS = 'navbar.settings';
4546
export const NAVBAR_NOTIFICATIONS = 'navbar.notifications';
46-
export const NAVBAR_RESOURCES = 'navbar:resources';
47+
export const NAVBAR_RESOURCES = 'navbar.resources';
48+
export const NAVBAR_NGROK_DEBUGGER = 'navbar.ngrokDebugger';
4749

4850
export const EDITOR_KEY_PRIMARY = 'primary';
4951
export const EDITOR_KEY_SECONDARY = 'secondary';

packages/app/client/src/state/actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ export * from './savedBotUrlsActions';
3939
export * from './updateActions';
4040
export * from './userActions';
4141
export * from './windowStateActions';
42+
export * from './ngrokTunnelActions';
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
updateNewTunnelInfo,
36+
updateTunnelError,
37+
updateTunnelStatus,
38+
NgrokTunnelActions,
39+
TunnelInfo,
40+
TunnelError,
41+
TunnelStatus,
42+
} from './ngrokTunnelActions';
43+
44+
describe('Ngrok Tunnel Actions', () => {
45+
it('should create an update tunnel info action', () => {
46+
const payload: TunnelInfo = {
47+
publicUrl: 'https://d1a2bf16.ngrok.io',
48+
inspectUrl: 'http://127.0.0.1:4041',
49+
logPath: 'ngrok.log',
50+
postmanCollectionPath: 'postman.json',
51+
};
52+
const action = updateNewTunnelInfo(payload);
53+
expect(action.type).toBe(NgrokTunnelActions.setDetails);
54+
expect(action.payload).toEqual(payload);
55+
});
56+
57+
it('should create a update tunnel error action', () => {
58+
const payload: TunnelError = {
59+
statusCode: 402,
60+
errorMessage: 'Tunnel has expired',
61+
};
62+
const action = updateTunnelError(payload);
63+
expect(action.type).toBe(NgrokTunnelActions.updateOnError);
64+
expect(action.payload).toEqual(payload);
65+
});
66+
67+
it('should create a tunnel status update action', () => {
68+
const mockDate = new Date(1466424490000);
69+
jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any);
70+
const expectedStatus: TunnelStatus = TunnelStatus.Active;
71+
const action = updateTunnelStatus(expectedStatus);
72+
expect(action.type).toBe(NgrokTunnelActions.setStatus);
73+
expect(action.payload.timestamp).toBe(new Date().toLocaleString());
74+
expect(action.payload.status).toBe(expectedStatus);
75+
});
76+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
import { Action } from 'redux';
34+
35+
export enum NgrokTunnelActions {
36+
setDetails = 'NgrokTunnel/SET_DETAILS',
37+
updateOnError = 'NgrokTunnel/TUNNEL_ERROR',
38+
setStatus = 'NgrokTunnel/STATUS_CHECK',
39+
}
40+
41+
export enum TunnelStatus {
42+
Active,
43+
Inactive,
44+
Error,
45+
}
46+
47+
export interface TunnelInfo {
48+
publicUrl: string;
49+
inspectUrl: string;
50+
logPath: string;
51+
postmanCollectionPath: string;
52+
}
53+
54+
export interface TunnelError {
55+
statusCode: number;
56+
errorMessage: string;
57+
}
58+
59+
export interface TunnelStatusAndTimestamp {
60+
status: TunnelStatus;
61+
timestamp: string;
62+
}
63+
64+
export interface NgrokTunnelAction<T> extends Action {
65+
type: NgrokTunnelActions;
66+
payload: T;
67+
}
68+
69+
export type NgrokTunnelPayloadTypes = TunnelError | TunnelInfo | TunnelStatusAndTimestamp;
70+
71+
export function updateNewTunnelInfo(payload: TunnelInfo): NgrokTunnelAction<TunnelInfo> {
72+
return {
73+
type: NgrokTunnelActions.setDetails,
74+
payload,
75+
};
76+
}
77+
78+
export function updateTunnelStatus(tunnelStatus: TunnelStatus): NgrokTunnelAction<TunnelStatusAndTimestamp> {
79+
return {
80+
type: NgrokTunnelActions.setStatus,
81+
payload: {
82+
status: tunnelStatus,
83+
timestamp: new Date().toLocaleString(),
84+
},
85+
};
86+
}
87+
88+
export function updateTunnelError(payload: TunnelError): NgrokTunnelAction<TunnelError> {
89+
return {
90+
type: NgrokTunnelActions.updateOnError,
91+
payload,
92+
};
93+
}

packages/app/client/src/state/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ export * from './theme';
5151
export * from './update';
5252
export * from './users';
5353
export * from './windowState';
54+
export * from './ngrokTunnel';
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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+
TunnelStatus,
36+
NgrokTunnelAction,
37+
TunnelInfo,
38+
NgrokTunnelPayloadTypes,
39+
NgrokTunnelActions,
40+
TunnelError,
41+
TunnelStatusAndTs,
42+
} from '../actions/ngrokTunnelActions';
43+
44+
import { ngrokTunnel, NgrokTunnelState } from './ngrokTunnel';
45+
46+
describe('Ngrok Tunnel reducer', () => {
47+
const DEFAULT_STATE: NgrokTunnelState = {
48+
inspectUrl: 'http://127.0.0.1:4040',
49+
publicUrl: '',
50+
logPath: '',
51+
postmanCollectionPath: '',
52+
errors: {} as TunnelError,
53+
tunnelStatus: TunnelStatus.Inactive,
54+
lastTunnelStatusCheckTS: '',
55+
};
56+
57+
afterEach(() => {
58+
const emptyAction: NgrokTunnelAction<NgrokTunnelPayloadTypes> = {
59+
type: null,
60+
payload: null,
61+
};
62+
ngrokTunnel({ ...DEFAULT_STATE }, emptyAction);
63+
});
64+
65+
it('Tunnel info should be set from payload', () => {
66+
const payload: TunnelInfo = {
67+
publicUrl: 'https://abc.io/',
68+
inspectUrl: 'http://127.0.0.1:4003',
69+
logPath: 'logger.txt',
70+
postmanCollectionPath: 'postman.json',
71+
};
72+
const setDetailsAction: NgrokTunnelAction<NgrokTunnelPayloadTypes> = {
73+
type: NgrokTunnelActions.setDetails,
74+
payload,
75+
};
76+
const startingState = { ...DEFAULT_STATE };
77+
const endingState = ngrokTunnel(startingState, setDetailsAction);
78+
79+
expect(endingState.publicUrl).toBe(payload.publicUrl);
80+
expect(endingState.logPath).toBe(payload.logPath);
81+
expect(endingState.postmanCollectionPath).toBe(payload.postmanCollectionPath);
82+
expect(endingState.inspectUrl).toBe(payload.inspectUrl);
83+
});
84+
85+
it('Tunnel errors should be set from payload', () => {
86+
const payload: TunnelError = {
87+
statusCode: 422,
88+
errorMessage: '<h1>Too many connections<h1>',
89+
};
90+
const updateErrorAction: NgrokTunnelAction<NgrokTunnelPayloadTypes> = {
91+
type: NgrokTunnelActions.updateOnError,
92+
payload,
93+
};
94+
const startingState = { ...DEFAULT_STATE };
95+
const endingState = ngrokTunnel(startingState, updateErrorAction);
96+
97+
expect(endingState.publicUrl).toBe(DEFAULT_STATE.publicUrl);
98+
expect(endingState.errors.statusCode).toBe(payload.statusCode);
99+
expect(endingState.errors.errorMessage).toBe(payload.errorMessage);
100+
});
101+
102+
it('Tunnel status should be set from payload', () => {
103+
const payload: TunnelStatusAndTs = {
104+
status: TunnelStatus.Active,
105+
ts: '12/27/2019, 1:30:00 PM',
106+
};
107+
const nextPayload: TunnelStatusAndTs = {
108+
status: TunnelStatus.Error,
109+
ts: '12/27/2019, 1:33:00 PM',
110+
};
111+
const actions: NgrokTunnelAction<NgrokTunnelPayloadTypes>[] = [
112+
{
113+
type: NgrokTunnelActions.setStatus,
114+
payload,
115+
},
116+
{
117+
type: NgrokTunnelActions.setStatus,
118+
payload: nextPayload,
119+
},
120+
];
121+
const startingState = { ...DEFAULT_STATE };
122+
const transientState = ngrokTunnel(startingState, actions[0]);
123+
expect(transientState.tunnelStatus).toBe(payload.status);
124+
125+
const finalState = ngrokTunnel(startingState, actions[1]);
126+
expect(finalState.tunnelStatus).toBe(nextPayload.status);
127+
});
128+
});

0 commit comments

Comments
 (0)