Skip to content

Commit

Permalink
feat(connection-form): connects the new connection form to the connec…
Browse files Browse the repository at this point in the history
…tion controller VSCODE-489 (#623)
  • Loading branch information
himanshusinghs authored Dec 12, 2023
1 parent 0c43c17 commit 996882c
Show file tree
Hide file tree
Showing 11 changed files with 373 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ describe('Connect Form Actions Component Test Suite', () => {
wrapper.find('#connectButton').simulate('click');
assert(fakeVscodeWindowPostMessage.called);
assert(
fakeVscodeWindowPostMessage.firstCall.args[0].command === 'CONNECT'
fakeVscodeWindowPostMessage.firstCall.args[0].command ===
'LEGACY_CONNECT'
);
assert.deepStrictEqual(
fakeVscodeWindowPostMessage.firstCall.args[0].connectionModel,
Expand Down
128 changes: 120 additions & 8 deletions src/test/suite/views/webview-app/overview-page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import React from 'react';
import { expect } from 'chai';
import { cleanup, render, screen } from '@testing-library/react';
import Sinon from 'sinon';
import { cleanup, render, screen, act } from '@testing-library/react';

import OverviewPage from '../../../../views/webview-app/overview-page';
import vscode from '../../../../views/webview-app/vscode-api';
import { MESSAGE_TYPES } from '../../../../views/webview-app/extension-app-message-constants';

const connectionFormTestId = 'connection-form-modal';

describe('OverviewPage test suite', function () {
afterEach(cleanup);
afterEach(() => {
cleanup();
Sinon.restore();
});
test('it should render OverviewPage', function () {
render(<OverviewPage />);
expect(
Expand All @@ -28,13 +36,117 @@ describe('OverviewPage test suite', function () {
expect(screen.queryByText('Product overview')).to.be.null;
});

test('it renders the new connection form when opened', function () {
render(<OverviewPage />);
describe('Connection Form', function () {
test('it is able to open and close the new connection form', function () {
render(<OverviewPage />);

expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;

screen.getByText('Open form').click();
expect(screen.getByTestId(connectionFormTestId)).to.exist;

screen.getByLabelText('Close modal').click();
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
});

it('should send connect request to webview controller when clicked on Connect button', function () {
const postMessageSpy = Sinon.spy(vscode, 'postMessage');

render(<OverviewPage />);
screen.getByText('Open form').click();

expect(screen.getByDisplayValue('mongodb://localhost:27017/')).to.not.be
.null;
screen.getByTestId('connect-button').click();
const argsWithoutConnectId = postMessageSpy.lastCall.args[0] as any;
expect(argsWithoutConnectId.command).to.equal(MESSAGE_TYPES.CONNECT);
expect(
argsWithoutConnectId.connectionInfo.connectionOptions.connectionString
).to.equal('mongodb://localhost:27017');
});

it('should display error message returned from connection attempt', function () {
render(<OverviewPage />);
const postMessageSpy = Sinon.spy(vscode, 'postMessage');
screen.getByText('Open form').click();
screen.getByTestId('connect-button').click();
const connectionAttemptId = (postMessageSpy.lastCall.args[0] as any)
.connectionAttemptId;

act(() => {
window.dispatchEvent(
new MessageEvent('message', {
data: {
command: MESSAGE_TYPES.CONNECT_RESULT,
connectionAttemptId,
connectionSuccess: false,
connectionMessage: 'server not found',
},
})
);
});
expect(screen.queryByTestId('connection-error-summary')).to.not.be.null;
});

it('should close the connection modal when connected successfully', function () {
render(<OverviewPage />);
const postMessageSpy = Sinon.spy(vscode, 'postMessage');
screen.getByText('Open form').click();
screen.getByTestId('connect-button').click();
const connectionAttemptId = (postMessageSpy.lastCall.args[0] as any)
.connectionAttemptId;

act(() => {
window.dispatchEvent(
new MessageEvent('message', {
data: {
command: MESSAGE_TYPES.CONNECT_RESULT,
connectionAttemptId,
connectionSuccess: true,
connectionMessage: '',
},
})
);
});
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
});

it('should not display results from other connection attempts', function () {
render(<OverviewPage />);
screen.getByText('Open form').click();
screen.getByTestId('connect-button').click();

const connectionFormTestId = 'connection-form-modal';
expect(screen.queryByTestId(connectionFormTestId)).to.not.exist;
act(() => {
window.dispatchEvent(
new MessageEvent('message', {
data: {
command: MESSAGE_TYPES.CONNECT_RESULT,
connectionAttemptId: 1, // different from the attempt id generated by our click
connectionSuccess: true,
connectionMessage: '',
},
})
);
});
// won't be closed because the connect result message is ignored
expect(screen.queryByTestId(connectionFormTestId)).to.exist;

screen.getByText('Open form').click();
expect(screen.getByTestId(connectionFormTestId)).to.exist;
act(() => {
window.dispatchEvent(
new MessageEvent('message', {
data: {
command: MESSAGE_TYPES.CONNECT_RESULT,
connectionAttemptId: 2, // different from the attempt id generated by our click
connectionSuccess: false,
connectionMessage: 'something bad happened',
},
})
);
});
expect(screen.queryByTestId(connectionFormTestId)).to.exist;
// won't show an error message because the connect result is ignored.
expect(screen.queryByTestId('connection-error-summary')).to.not.be
.undefined;
});
});
});
86 changes: 81 additions & 5 deletions src/test/suite/views/webviewController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ suite('Webview Test Suite', () => {
);
});

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

// Mock a connection call.
messageReceived({
command: MESSAGE_TYPES.CONNECT,
command: MESSAGE_TYPES.LEGACY_CONNECT,
connectionModel: {
port: 27088,
hostname: 'localhost',
Expand All @@ -200,6 +201,81 @@ suite('Webview Test Suite', () => {
});
});

test('web view listens for a connect message and adds the connection', (done) => {
const extensionContextStub = new ExtensionContextStub();
const testStorageController = new StorageController(extensionContextStub);
const testTelemetryService = new TelemetryService(
testStorageController,
extensionContextStub
);
const testConnectionController = new ConnectionController({
statusView: new StatusView(extensionContextStub),
storageController: testStorageController,
telemetryService: testTelemetryService,
});
let messageReceivedSet = false;
let messageReceived;

sandbox.stub(testTelemetryService, 'trackNewConnection');

const fakeWebview = {
html: '',
postMessage: async (): Promise<void> => {
assert(testConnectionController.isCurrentlyConnected());
assert(
testConnectionController.getActiveConnectionName() ===
'localhost:27088'
);

await testConnectionController.disconnect();
done();
},
onDidReceiveMessage: (callback): void => {
messageReceived = callback;
messageReceivedSet = true;
},
asWebviewUri: sandbox.fake.returns(''),
};

const fakeVSCodeCreateWebviewPanel = sandbox.fake.returns({
webview: fakeWebview,
onDidDispose: sandbox.fake.returns(''),
});

sandbox.replace(
vscode.window,
'createWebviewPanel',
fakeVSCodeCreateWebviewPanel
);

const testWebviewController = new WebviewController({
connectionController: testConnectionController,
storageController: testStorageController,
telemetryService: testTelemetryService,
});

void testWebviewController.openWebview(
mdbTestExtension.extensionContextStub
);

assert(
messageReceivedSet,
'Ensure it starts listening for messages from the webview.'
);

// Mock a connection call.
messageReceived({
command: MESSAGE_TYPES.CONNECT,
connectionInfo: {
id: 2,
connectionOptions: {
connectionString: 'mongodb://localhost:27088',
},
},
connectionAttemptId: 1,
});
});

test('web view sends a successful connect result on a successful connection', (done) => {
const extensionContextStub = new ExtensionContextStub();
const testStorageController = new StorageController(extensionContextStub);
Expand Down Expand Up @@ -264,7 +340,7 @@ suite('Webview Test Suite', () => {

// Mock a connection call.
messageReceived({
command: MESSAGE_TYPES.CONNECT,
command: MESSAGE_TYPES.LEGACY_CONNECT,
connectionModel: {
port: 27088,
hostname: 'localhost',
Expand Down Expand Up @@ -326,7 +402,7 @@ suite('Webview Test Suite', () => {

// Mock a connection call.
messageReceived({
command: MESSAGE_TYPES.CONNECT,
command: MESSAGE_TYPES.LEGACY_CONNECT,
connectionModel: {
port: 2700002, // Bad port number.
hostname: 'localhost',
Expand Down Expand Up @@ -390,7 +466,7 @@ suite('Webview Test Suite', () => {

// Mock a connection call.
messageReceived({
command: MESSAGE_TYPES.CONNECT,
command: MESSAGE_TYPES.LEGACY_CONNECT,
connectionModel: {
port: 27088,
hostname: 'shouldfail',
Expand Down
7 changes: 5 additions & 2 deletions src/views/webview-app/connection-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import CompassConnectionForm from '@mongodb-js/connection-form';
import { Modal, css, spacing } from '@mongodb-js/compass-components';
import { v4 as uuidv4 } from 'uuid';
import type { ConnectionInfo } from 'mongodb-data-service-legacy';

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

const ConnectionForm: React.FunctionComponent<{
onConnectClicked: (onConnectClicked: unknown) => void;
onConnectClicked: (onConnectClicked: ConnectionInfo) => void;
onClose: () => void;
open: boolean;
}> = ({ onConnectClicked, onClose, open }) => {
connectionErrorMessage: string;
}> = ({ connectionErrorMessage, onConnectClicked, onClose, open }) => {
return (
<Modal
// Warning: This property may be removed in future
Expand All @@ -57,6 +59,7 @@ const ConnectionForm: React.FunctionComponent<{
showKerberosAuth: false,
showCSFLE: false,
}}
connectionErrorMessage={connectionErrorMessage}
/>
</div>
</Modal>
Expand Down
10 changes: 10 additions & 0 deletions src/views/webview-app/extension-app-message-constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type LegacyConnectionModel from './legacy/connection-model/legacy-connection-model';
import type { FilePickerActionTypes } from './legacy/store/actions';
import type { ConnectionInfo } from 'mongodb-data-service-legacy';

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

export enum MESSAGE_TYPES {
CONNECT = 'CONNECT',
LEGACY_CONNECT = 'LEGACY_CONNECT',
CONNECT_RESULT = 'CONNECT_RESULT',
CONNECTION_STATUS_MESSAGE = 'CONNECTION_STATUS_MESSAGE',
EXTENSION_LINK_CLICKED = 'EXTENSION_LINK_CLICKED',
Expand Down Expand Up @@ -43,6 +45,13 @@ export interface ConnectionStatusMessage extends BasicWebviewMessage {

export interface ConnectMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.CONNECT;
connectionInfo: ConnectionInfo;
connectionAttemptId: string;
}

// TODO: VSCODE-491 - Remove this entirely when getting rid of legacy
export interface LegacyConnectMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.LEGACY_CONNECT;
connectionModel: LegacyConnectionModel;
connectionAttemptId: string;
}
Expand Down Expand Up @@ -97,6 +106,7 @@ export interface ThemeChangedMessage extends BasicWebviewMessage {

export type MESSAGE_FROM_WEBVIEW_TO_EXTENSION =
| ConnectMessage
| LegacyConnectMessage
| CreateNewPlaygroundMessage
| GetConnectionStatusMessage
| LinkClickedMessage
Expand Down
9 changes: 6 additions & 3 deletions src/views/webview-app/legacy/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ const showFilePicker = (
});
};

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

vscode.postMessage({
command: MESSAGE_TYPES.CONNECT,
command: MESSAGE_TYPES.LEGACY_CONNECT,
connectionModel,
connectionAttemptId,
});
Expand Down Expand Up @@ -124,7 +125,9 @@ export const rootReducer = (
isValid: true,
isConnecting: true,
isConnected: false,
connectionAttemptId: sendConnectToExtension(state.currentConnection),
connectionAttemptId: sendLegacyConnectToExtension(
state.currentConnection
),
};

case ActionTypes.CREATE_NEW_PLAYGROUND:
Expand Down
Loading

0 comments on commit 996882c

Please sign in to comment.