Skip to content

Commit a7bb28a

Browse files
authored
VSCODE-164: Display connection status in overview (#183)
1 parent 52efbbd commit a7bb28a

File tree

16 files changed

+574
-81
lines changed

16 files changed

+574
-81
lines changed

src/connectionController.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { StorageController, StorageVariables } from './storage';
1212
import { SavedConnection, StorageScope } from './storage/storageController';
1313
import TelemetryController from './telemetry/telemetryController';
1414
import { ext } from './extensionConstants';
15+
import { CONNECTION_STATUS } from './views/webview-app/extension-app-message-constants';
1516

1617
const { name, version } = require('../package.json');
1718
const log = createLogger('connection controller');
@@ -708,6 +709,22 @@ export default class ConnectionController {
708709
return this._activeConnectionModel;
709710
}
710711

712+
public getConnectionStatus(): CONNECTION_STATUS {
713+
if (this.isCurrentlyConnected()) {
714+
if (this.isDisconnecting()) {
715+
return CONNECTION_STATUS.DISCONNECTING;
716+
}
717+
718+
return CONNECTION_STATUS.CONNECTED;
719+
}
720+
721+
if (this.isConnecting()) {
722+
return CONNECTION_STATUS.CONNECTING;
723+
}
724+
725+
return CONNECTION_STATUS.DISCONNECTED;
726+
}
727+
711728
public getConnectionStatusStringForConnection(connectionId: string): string {
712729
if (
713730
this.getActiveConnectionId() === connectionId

src/test/suite/connectionController.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ suite('Connection Controller Test Suite', function () {
119119
succesfullyConnected === true,
120120
'Expected a successful (true) connection response.'
121121
);
122+
assert(testConnectionController.getConnectionStatus() === 'CONNECTED');
122123

123124
const successfullyDisconnected = await testConnectionController.disconnect();
124125

@@ -129,6 +130,7 @@ suite('Connection Controller Test Suite', function () {
129130
const connectionModel = testConnectionController.getActiveConnectionModel();
130131
const dataService = testConnectionController.getActiveDataService();
131132

133+
assert(testConnectionController.getConnectionStatus() === 'DISCONNECTED');
132134
assert(
133135
successfullyDisconnected === true,
134136
'Expected a successful (true) disconnect response.'
@@ -891,6 +893,7 @@ suite('Connection Controller Test Suite', function () {
891893
await sleep(250);
892894

893895
assert(testConnectionController.isConnecting());
896+
assert(testConnectionController.getConnectionStatus() === 'CONNECTING');
894897

895898
await testConnectionController.removeSavedConnection(connectionId);
896899

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import assert from 'assert';
22
import * as React from 'react';
3-
import { shallow } from 'enzyme';
3+
import { mount, shallow } from 'enzyme';
4+
import * as sinon from 'sinon';
5+
import { Provider } from 'react-redux';
6+
import { createStore } from 'redux';
47

58
import {
6-
App
9+
initialState,
10+
rootReducer
11+
} from '../../../../../views/webview-app/store/store';
12+
import App, {
13+
App as NotConnectedApp
714
} from '../../../../../views/webview-app/components/app';
815
import ConnectionForm from '../../../../../views/webview-app/components/connect-form/connection-form';
916
import OverviewPage from '../../../../../views/webview-app/components/overview-page/overview-page';
10-
import { WEBVIEW_VIEWS } from '../../../../../views/webview-app/extension-app-message-constants';
17+
import { CONNECTION_STATUS, MESSAGE_TYPES, WEBVIEW_VIEWS } from '../../../../../views/webview-app/extension-app-message-constants';
1118

1219
describe('App Component Test Suite', () => {
1320
describe('when passed currentView=CONNECT', () => {
1421
test('it shows a connection form', () => {
15-
const wrapper = shallow(<App
22+
const wrapper = shallow(<NotConnectedApp
1623
currentView={WEBVIEW_VIEWS.CONNECT}
1724
onConnectedEvent={() => { }}
1825
onFilePickerEvent={() => { }}
26+
setConnectionStatus={() => { }}
1927
/>);
2028
assert(wrapper.find(ConnectionForm).exists());
2129
assert(!wrapper.find(OverviewPage).exists());
@@ -24,13 +32,71 @@ describe('App Component Test Suite', () => {
2432

2533
describe('when passed currentView=CONNECT', () => {
2634
test('it shows a connection form', () => {
27-
const wrapper = shallow(<App
35+
const wrapper = shallow(<NotConnectedApp
2836
currentView={WEBVIEW_VIEWS.OVERVIEW}
2937
onConnectedEvent={() => { }}
3038
onFilePickerEvent={() => { }}
39+
setConnectionStatus={() => { }}
3140
/>);
3241
assert(wrapper.find(OverviewPage).exists());
3342
assert(!wrapper.find(ConnectionForm).exists());
3443
});
3544
});
45+
46+
describe('when the extension sends a connection status message', () => {
47+
let fakeVscodeWindowPostMessage;
48+
let wrapper;
49+
let store;
50+
let fakeOnEventFunction;
51+
let fakeAddEventListener;
52+
53+
beforeEach(() => {
54+
fakeVscodeWindowPostMessage = sinon.fake.returns(null);
55+
fakeAddEventListener = (eventName, eventFn) => {
56+
if (eventName === 'message') {
57+
fakeOnEventFunction = eventFn;
58+
}
59+
};
60+
61+
sinon.replace(
62+
(global as any).vscodeFake,
63+
'postMessage',
64+
fakeVscodeWindowPostMessage
65+
);
66+
67+
sinon.replace(
68+
window,
69+
'addEventListener',
70+
fakeAddEventListener
71+
);
72+
73+
store = createStore(rootReducer, initialState);
74+
75+
wrapper = mount(
76+
<Provider
77+
store={store}
78+
>
79+
<App />
80+
</Provider>
81+
);
82+
});
83+
84+
afterEach(() => {
85+
sinon.restore();
86+
});
87+
88+
test('it updates the connectionStatus in the store', () => {
89+
assert(store.getState().connectionStatus === CONNECTION_STATUS.LOADING);
90+
assert(store.getState().activeConnectionName === '');
91+
fakeOnEventFunction({
92+
data: {
93+
command: MESSAGE_TYPES.CONNECTION_STATUS_MESSAGE,
94+
connectionStatus: CONNECTION_STATUS.CONNECTED,
95+
activeConnectionName: 'Nice connection'
96+
}
97+
});
98+
assert(store.getState().connectionStatus === CONNECTION_STATUS.CONNECTED);
99+
assert(store.getState().activeConnectionName === 'Nice connection');
100+
});
101+
});
36102
});
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import assert from 'assert';
2+
import * as React from 'react';
3+
import { mount, shallow } from 'enzyme';
4+
import * as sinon from 'sinon';
5+
import { createStore } from 'redux';
6+
import { Provider } from 'react-redux';
7+
8+
import {
9+
rootReducer
10+
} from '../../../../../../views/webview-app/store/store';
11+
import {
12+
ConnectionStatus
13+
} from '../../../../../../views/webview-app/components/connection-status/connection-status';
14+
import { CONNECTION_STATUS } from '../../../../../../views/webview-app/extension-app-message-constants';
15+
16+
describe('Connection Status Component Test Suite', () => {
17+
describe('connected connection status', () => {
18+
test('it shows that it is connected to the connection name', () => {
19+
const wrapper = shallow(<ConnectionStatus
20+
activeConnectionName="Active connection name"
21+
connectionStatus={CONNECTION_STATUS.CONNECTED}
22+
onClickCreatePlayground={() => {}}
23+
requestConnectionStatus={() => {}}
24+
/>);
25+
assert(wrapper.text().includes('Connected to:'));
26+
assert(wrapper.text().includes('Active connection name'));
27+
});
28+
29+
test('it shows a create playground button', () => {
30+
const wrapper = shallow(<ConnectionStatus
31+
activeConnectionName="Active connection name"
32+
connectionStatus={CONNECTION_STATUS.CONNECTED}
33+
onClickCreatePlayground={() => {}}
34+
requestConnectionStatus={() => {}}
35+
/>);
36+
assert(wrapper.find('button').exists());
37+
});
38+
});
39+
40+
describe('disconnected', () => {
41+
test('it shows a disconnect message', () => {
42+
const wrapper = shallow(<ConnectionStatus
43+
activeConnectionName=""
44+
connectionStatus={CONNECTION_STATUS.DISCONNECTED}
45+
onClickCreatePlayground={() => {}}
46+
requestConnectionStatus={() => {}}
47+
/>);
48+
assert(wrapper.text().includes('Not connected'));
49+
});
50+
51+
test('it does not show a create playground button', () => {
52+
const wrapper = shallow(<ConnectionStatus
53+
activeConnectionName=""
54+
connectionStatus={CONNECTION_STATUS.DISCONNECTED}
55+
onClickCreatePlayground={() => {}}
56+
requestConnectionStatus={() => {}}
57+
/>);
58+
assert(wrapper.find('button').exists() === false);
59+
});
60+
});
61+
62+
describe('connecting', () => {
63+
test('it shows a connecting message', () => {
64+
const wrapper = shallow(<ConnectionStatus
65+
activeConnectionName=""
66+
connectionStatus={CONNECTION_STATUS.CONNECTING}
67+
onClickCreatePlayground={() => {}}
68+
requestConnectionStatus={() => {}}
69+
/>);
70+
assert(wrapper.text().includes('Connecting...'));
71+
});
72+
});
73+
74+
describe('disconnecting', () => {
75+
test('it shows a connecting message', () => {
76+
const wrapper = shallow(<ConnectionStatus
77+
activeConnectionName=""
78+
connectionStatus={CONNECTION_STATUS.DISCONNECTING}
79+
onClickCreatePlayground={() => {}}
80+
requestConnectionStatus={() => {}}
81+
/>);
82+
assert(wrapper.text().includes('Disconnecting...'));
83+
});
84+
});
85+
86+
describe('loading', () => {
87+
test('it shows a loading message', () => {
88+
const wrapper = shallow(<ConnectionStatus
89+
activeConnectionName=""
90+
connectionStatus={CONNECTION_STATUS.LOADING}
91+
onClickCreatePlayground={() => {}}
92+
requestConnectionStatus={() => {}}
93+
/>);
94+
assert(wrapper.text().includes('Loading...'));
95+
});
96+
});
97+
});

src/test/suite/views/webviewController.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,4 +543,113 @@ suite('Connect Form View Test Suite', () => {
543543
done();
544544
}, 50);
545545
});
546+
547+
test('webview returns the connection status on a connection status request', (done) => {
548+
const testExtensionContext = new TestExtensionContext();
549+
const testStorageController = new StorageController(testExtensionContext);
550+
const testTelemetryController = new TelemetryController(
551+
testStorageController,
552+
testExtensionContext
553+
);
554+
const testConnectionController = new ConnectionController(
555+
new StatusView(testExtensionContext),
556+
testStorageController,
557+
testTelemetryController
558+
);
559+
let messageRecieved: any;
560+
const fakeWebview = {
561+
html: '',
562+
postMessage: (message: any): void => {
563+
assert(message.command === 'CONNECTION_STATUS_MESSAGE');
564+
assert(message.connectionStatus === 'DISCONNECTED');
565+
assert(message.activeConnectionName === '');
566+
567+
done();
568+
},
569+
onDidReceiveMessage: (callback): void => {
570+
messageRecieved = callback;
571+
},
572+
asWebviewUri: sinon.fake.returns('')
573+
};
574+
const fakeVSCodeCreateWebviewPanel = sinon.fake.returns({
575+
webview: fakeWebview
576+
});
577+
578+
sinon.replace(
579+
vscode.window,
580+
'createWebviewPanel',
581+
fakeVSCodeCreateWebviewPanel
582+
);
583+
584+
const testWebviewController = new WebviewController(
585+
testConnectionController,
586+
testTelemetryController
587+
);
588+
589+
testWebviewController.showOverviewPage(
590+
mdbTestExtension.testExtensionContext
591+
);
592+
593+
// Mock a connection status request call.
594+
messageRecieved({
595+
command: MESSAGE_TYPES.GET_CONNECTION_STATUS
596+
});
597+
});
598+
599+
test('webview returns the connection status on a connection status request', (done) => {
600+
const testExtensionContext = new TestExtensionContext();
601+
const testStorageController = new StorageController(testExtensionContext);
602+
const testTelemetryController = new TelemetryController(
603+
testStorageController,
604+
testExtensionContext
605+
);
606+
const testConnectionController = new ConnectionController(
607+
new StatusView(testExtensionContext),
608+
testStorageController,
609+
testTelemetryController
610+
);
611+
let messageRecieved: any;
612+
const fakeWebview = {
613+
html: '',
614+
postMessage: (message: any): void => {
615+
assert(message.command === 'CONNECTION_STATUS_MESSAGE');
616+
assert(message.connectionStatus === 'CONNECTED');
617+
assert(message.activeConnectionName === 'localhost:27018');
618+
testConnectionController.disconnect();
619+
620+
done();
621+
},
622+
onDidReceiveMessage: (callback): void => {
623+
messageRecieved = callback;
624+
},
625+
asWebviewUri: sinon.fake.returns('')
626+
};
627+
const fakeVSCodeCreateWebviewPanel = sinon.fake.returns({
628+
webview: fakeWebview
629+
});
630+
631+
sinon.replace(
632+
vscode.window,
633+
'createWebviewPanel',
634+
fakeVSCodeCreateWebviewPanel
635+
);
636+
637+
const testWebviewController = new WebviewController(
638+
testConnectionController,
639+
testTelemetryController
640+
);
641+
642+
testWebviewController.showOverviewPage(
643+
mdbTestExtension.testExtensionContext
644+
);
645+
646+
testConnectionController.addNewConnectionStringAndConnect(
647+
TEST_DATABASE_URI
648+
).then(() => {
649+
// Mock a connection status request call.
650+
messageRecieved({
651+
command: MESSAGE_TYPES.GET_CONNECTION_STATUS
652+
});
653+
});
654+
});
546655
});

0 commit comments

Comments
 (0)