Skip to content

Commit 996882c

Browse files
feat(connection-form): connects the new connection form to the connection controller VSCODE-489 (#623)
1 parent 0c43c17 commit 996882c

File tree

11 files changed

+373
-35
lines changed

11 files changed

+373
-35
lines changed

src/test/suite/views/webview-app/legacy/components/form/form-actions.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ describe('Connect Form Actions Component Test Suite', () => {
4848
wrapper.find('#connectButton').simulate('click');
4949
assert(fakeVscodeWindowPostMessage.called);
5050
assert(
51-
fakeVscodeWindowPostMessage.firstCall.args[0].command === 'CONNECT'
51+
fakeVscodeWindowPostMessage.firstCall.args[0].command ===
52+
'LEGACY_CONNECT'
5253
);
5354
assert.deepStrictEqual(
5455
fakeVscodeWindowPostMessage.firstCall.args[0].connectionModel,

src/test/suite/views/webview-app/overview-page.test.tsx

Lines changed: 120 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import React from 'react';
22
import { expect } from 'chai';
3-
import { cleanup, render, screen } from '@testing-library/react';
3+
import Sinon from 'sinon';
4+
import { cleanup, render, screen, act } from '@testing-library/react';
45

56
import OverviewPage from '../../../../views/webview-app/overview-page';
7+
import vscode from '../../../../views/webview-app/vscode-api';
8+
import { MESSAGE_TYPES } from '../../../../views/webview-app/extension-app-message-constants';
9+
10+
const connectionFormTestId = 'connection-form-modal';
611

712
describe('OverviewPage test suite', function () {
8-
afterEach(cleanup);
13+
afterEach(() => {
14+
cleanup();
15+
Sinon.restore();
16+
});
917
test('it should render OverviewPage', function () {
1018
render(<OverviewPage />);
1119
expect(
@@ -28,13 +36,117 @@ describe('OverviewPage test suite', function () {
2836
expect(screen.queryByText('Product overview')).to.be.null;
2937
});
3038

31-
test('it renders the new connection form when opened', function () {
32-
render(<OverviewPage />);
39+
describe('Connection Form', function () {
40+
test('it is able to open and close the new connection form', function () {
41+
render(<OverviewPage />);
42+
43+
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
44+
45+
screen.getByText('Open form').click();
46+
expect(screen.getByTestId(connectionFormTestId)).to.exist;
47+
48+
screen.getByLabelText('Close modal').click();
49+
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
50+
});
51+
52+
it('should send connect request to webview controller when clicked on Connect button', function () {
53+
const postMessageSpy = Sinon.spy(vscode, 'postMessage');
54+
55+
render(<OverviewPage />);
56+
screen.getByText('Open form').click();
57+
58+
expect(screen.getByDisplayValue('mongodb://localhost:27017/')).to.not.be
59+
.null;
60+
screen.getByTestId('connect-button').click();
61+
const argsWithoutConnectId = postMessageSpy.lastCall.args[0] as any;
62+
expect(argsWithoutConnectId.command).to.equal(MESSAGE_TYPES.CONNECT);
63+
expect(
64+
argsWithoutConnectId.connectionInfo.connectionOptions.connectionString
65+
).to.equal('mongodb://localhost:27017');
66+
});
67+
68+
it('should display error message returned from connection attempt', function () {
69+
render(<OverviewPage />);
70+
const postMessageSpy = Sinon.spy(vscode, 'postMessage');
71+
screen.getByText('Open form').click();
72+
screen.getByTestId('connect-button').click();
73+
const connectionAttemptId = (postMessageSpy.lastCall.args[0] as any)
74+
.connectionAttemptId;
75+
76+
act(() => {
77+
window.dispatchEvent(
78+
new MessageEvent('message', {
79+
data: {
80+
command: MESSAGE_TYPES.CONNECT_RESULT,
81+
connectionAttemptId,
82+
connectionSuccess: false,
83+
connectionMessage: 'server not found',
84+
},
85+
})
86+
);
87+
});
88+
expect(screen.queryByTestId('connection-error-summary')).to.not.be.null;
89+
});
90+
91+
it('should close the connection modal when connected successfully', function () {
92+
render(<OverviewPage />);
93+
const postMessageSpy = Sinon.spy(vscode, 'postMessage');
94+
screen.getByText('Open form').click();
95+
screen.getByTestId('connect-button').click();
96+
const connectionAttemptId = (postMessageSpy.lastCall.args[0] as any)
97+
.connectionAttemptId;
98+
99+
act(() => {
100+
window.dispatchEvent(
101+
new MessageEvent('message', {
102+
data: {
103+
command: MESSAGE_TYPES.CONNECT_RESULT,
104+
connectionAttemptId,
105+
connectionSuccess: true,
106+
connectionMessage: '',
107+
},
108+
})
109+
);
110+
});
111+
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
112+
});
113+
114+
it('should not display results from other connection attempts', function () {
115+
render(<OverviewPage />);
116+
screen.getByText('Open form').click();
117+
screen.getByTestId('connect-button').click();
33118

34-
const connectionFormTestId = 'connection-form-modal';
35-
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
119+
act(() => {
120+
window.dispatchEvent(
121+
new MessageEvent('message', {
122+
data: {
123+
command: MESSAGE_TYPES.CONNECT_RESULT,
124+
connectionAttemptId: 1, // different from the attempt id generated by our click
125+
connectionSuccess: true,
126+
connectionMessage: '',
127+
},
128+
})
129+
);
130+
});
131+
// won't be closed because the connect result message is ignored
132+
expect(screen.queryByTestId(connectionFormTestId)).to.exist;
36133

37-
screen.getByText('Open form').click();
38-
expect(screen.getByTestId(connectionFormTestId)).to.exist;
134+
act(() => {
135+
window.dispatchEvent(
136+
new MessageEvent('message', {
137+
data: {
138+
command: MESSAGE_TYPES.CONNECT_RESULT,
139+
connectionAttemptId: 2, // different from the attempt id generated by our click
140+
connectionSuccess: false,
141+
connectionMessage: 'something bad happened',
142+
},
143+
})
144+
);
145+
});
146+
expect(screen.queryByTestId(connectionFormTestId)).to.exist;
147+
// won't show an error message because the connect result is ignored.
148+
expect(screen.queryByTestId('connection-error-summary')).to.not.be
149+
.undefined;
150+
});
39151
});
40152
});

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

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ suite('Webview Test Suite', () => {
127127
);
128128
});
129129

130-
test('web view listens for a connect message and adds the connection', (done) => {
130+
// TODO: VSCODE-491 - Remove this test case entirely when getting rid of legacy
131+
test('web view listens for a legacy connect message and adds the connection', (done) => {
131132
const extensionContextStub = new ExtensionContextStub();
132133
const testStorageController = new StorageController(extensionContextStub);
133134
const testTelemetryService = new TelemetryService(
@@ -191,7 +192,7 @@ suite('Webview Test Suite', () => {
191192

192193
// Mock a connection call.
193194
messageReceived({
194-
command: MESSAGE_TYPES.CONNECT,
195+
command: MESSAGE_TYPES.LEGACY_CONNECT,
195196
connectionModel: {
196197
port: 27088,
197198
hostname: 'localhost',
@@ -200,6 +201,81 @@ suite('Webview Test Suite', () => {
200201
});
201202
});
202203

204+
test('web view listens for a connect message and adds the connection', (done) => {
205+
const extensionContextStub = new ExtensionContextStub();
206+
const testStorageController = new StorageController(extensionContextStub);
207+
const testTelemetryService = new TelemetryService(
208+
testStorageController,
209+
extensionContextStub
210+
);
211+
const testConnectionController = new ConnectionController({
212+
statusView: new StatusView(extensionContextStub),
213+
storageController: testStorageController,
214+
telemetryService: testTelemetryService,
215+
});
216+
let messageReceivedSet = false;
217+
let messageReceived;
218+
219+
sandbox.stub(testTelemetryService, 'trackNewConnection');
220+
221+
const fakeWebview = {
222+
html: '',
223+
postMessage: async (): Promise<void> => {
224+
assert(testConnectionController.isCurrentlyConnected());
225+
assert(
226+
testConnectionController.getActiveConnectionName() ===
227+
'localhost:27088'
228+
);
229+
230+
await testConnectionController.disconnect();
231+
done();
232+
},
233+
onDidReceiveMessage: (callback): void => {
234+
messageReceived = callback;
235+
messageReceivedSet = true;
236+
},
237+
asWebviewUri: sandbox.fake.returns(''),
238+
};
239+
240+
const fakeVSCodeCreateWebviewPanel = sandbox.fake.returns({
241+
webview: fakeWebview,
242+
onDidDispose: sandbox.fake.returns(''),
243+
});
244+
245+
sandbox.replace(
246+
vscode.window,
247+
'createWebviewPanel',
248+
fakeVSCodeCreateWebviewPanel
249+
);
250+
251+
const testWebviewController = new WebviewController({
252+
connectionController: testConnectionController,
253+
storageController: testStorageController,
254+
telemetryService: testTelemetryService,
255+
});
256+
257+
void testWebviewController.openWebview(
258+
mdbTestExtension.extensionContextStub
259+
);
260+
261+
assert(
262+
messageReceivedSet,
263+
'Ensure it starts listening for messages from the webview.'
264+
);
265+
266+
// Mock a connection call.
267+
messageReceived({
268+
command: MESSAGE_TYPES.CONNECT,
269+
connectionInfo: {
270+
id: 2,
271+
connectionOptions: {
272+
connectionString: 'mongodb://localhost:27088',
273+
},
274+
},
275+
connectionAttemptId: 1,
276+
});
277+
});
278+
203279
test('web view sends a successful connect result on a successful connection', (done) => {
204280
const extensionContextStub = new ExtensionContextStub();
205281
const testStorageController = new StorageController(extensionContextStub);
@@ -264,7 +340,7 @@ suite('Webview Test Suite', () => {
264340

265341
// Mock a connection call.
266342
messageReceived({
267-
command: MESSAGE_TYPES.CONNECT,
343+
command: MESSAGE_TYPES.LEGACY_CONNECT,
268344
connectionModel: {
269345
port: 27088,
270346
hostname: 'localhost',
@@ -326,7 +402,7 @@ suite('Webview Test Suite', () => {
326402

327403
// Mock a connection call.
328404
messageReceived({
329-
command: MESSAGE_TYPES.CONNECT,
405+
command: MESSAGE_TYPES.LEGACY_CONNECT,
330406
connectionModel: {
331407
port: 2700002, // Bad port number.
332408
hostname: 'localhost',
@@ -390,7 +466,7 @@ suite('Webview Test Suite', () => {
390466

391467
// Mock a connection call.
392468
messageReceived({
393-
command: MESSAGE_TYPES.CONNECT,
469+
command: MESSAGE_TYPES.LEGACY_CONNECT,
394470
connectionModel: {
395471
port: 27088,
396472
hostname: 'shouldfail',

src/views/webview-app/connection-form.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import CompassConnectionForm from '@mongodb-js/connection-form';
33
import { Modal, css, spacing } from '@mongodb-js/compass-components';
44
import { v4 as uuidv4 } from 'uuid';
5+
import type { ConnectionInfo } from 'mongodb-data-service-legacy';
56

67
const modalContentStyles = css({
78
// Override LeafyGreen width to accommodate the strict connection-form size.
@@ -27,10 +28,11 @@ function createNewConnectionInfo() {
2728
const initialConnectionInfo = createNewConnectionInfo();
2829

2930
const ConnectionForm: React.FunctionComponent<{
30-
onConnectClicked: (onConnectClicked: unknown) => void;
31+
onConnectClicked: (onConnectClicked: ConnectionInfo) => void;
3132
onClose: () => void;
3233
open: boolean;
33-
}> = ({ onConnectClicked, onClose, open }) => {
34+
connectionErrorMessage: string;
35+
}> = ({ connectionErrorMessage, onConnectClicked, onClose, open }) => {
3436
return (
3537
<Modal
3638
// Warning: This property may be removed in future
@@ -57,6 +59,7 @@ const ConnectionForm: React.FunctionComponent<{
5759
showKerberosAuth: false,
5860
showCSFLE: false,
5961
}}
62+
connectionErrorMessage={connectionErrorMessage}
6063
/>
6164
</div>
6265
</Modal>

src/views/webview-app/extension-app-message-constants.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type LegacyConnectionModel from './legacy/connection-model/legacy-connection-model';
22
import type { FilePickerActionTypes } from './legacy/store/actions';
3+
import type { ConnectionInfo } from 'mongodb-data-service-legacy';
34

45
export enum CONNECTION_STATUS {
56
LOADING = 'LOADING', // When the connection status has not yet been shared from the extension.
@@ -14,6 +15,7 @@ export const VSCODE_EXTENSION_SEGMENT_ANONYMOUS_ID =
1415

1516
export enum MESSAGE_TYPES {
1617
CONNECT = 'CONNECT',
18+
LEGACY_CONNECT = 'LEGACY_CONNECT',
1719
CONNECT_RESULT = 'CONNECT_RESULT',
1820
CONNECTION_STATUS_MESSAGE = 'CONNECTION_STATUS_MESSAGE',
1921
EXTENSION_LINK_CLICKED = 'EXTENSION_LINK_CLICKED',
@@ -43,6 +45,13 @@ export interface ConnectionStatusMessage extends BasicWebviewMessage {
4345

4446
export interface ConnectMessage extends BasicWebviewMessage {
4547
command: MESSAGE_TYPES.CONNECT;
48+
connectionInfo: ConnectionInfo;
49+
connectionAttemptId: string;
50+
}
51+
52+
// TODO: VSCODE-491 - Remove this entirely when getting rid of legacy
53+
export interface LegacyConnectMessage extends BasicWebviewMessage {
54+
command: MESSAGE_TYPES.LEGACY_CONNECT;
4655
connectionModel: LegacyConnectionModel;
4756
connectionAttemptId: string;
4857
}
@@ -97,6 +106,7 @@ export interface ThemeChangedMessage extends BasicWebviewMessage {
97106

98107
export type MESSAGE_FROM_WEBVIEW_TO_EXTENSION =
99108
| ConnectMessage
109+
| LegacyConnectMessage
100110
| CreateNewPlaygroundMessage
101111
| GetConnectionStatusMessage
102112
| LinkClickedMessage

src/views/webview-app/legacy/store/store.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,14 @@ const showFilePicker = (
5959
});
6060
};
6161

62-
const sendConnectToExtension = (
62+
// TODO: VSCODE-491 - Remove this entirely when getting rid of legacy
63+
const sendLegacyConnectToExtension = (
6364
connectionModel: LegacyConnectionModel
6465
): string => {
6566
const connectionAttemptId = uuidv4();
6667

6768
vscode.postMessage({
68-
command: MESSAGE_TYPES.CONNECT,
69+
command: MESSAGE_TYPES.LEGACY_CONNECT,
6970
connectionModel,
7071
connectionAttemptId,
7172
});
@@ -124,7 +125,9 @@ export const rootReducer = (
124125
isValid: true,
125126
isConnecting: true,
126127
isConnected: false,
127-
connectionAttemptId: sendConnectToExtension(state.currentConnection),
128+
connectionAttemptId: sendLegacyConnectToExtension(
129+
state.currentConnection
130+
),
128131
};
129132

130133
case ActionTypes.CREATE_NEW_PLAYGROUND:

0 commit comments

Comments
 (0)