Skip to content

Commit 1c71d91

Browse files
authored
chore(connections): Move expensive beforeEach test setup into before operation (#2591)
1 parent 4b4f01d commit 1c71d91

File tree

5 files changed

+101
-89
lines changed

5 files changed

+101
-89
lines changed

package-lock.json

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

packages/connections/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@
8383
"chai": "^4.3.4",
8484
"depcheck": "^1.4.1",
8585
"eslint": "^7.25.0",
86+
"get-port": "^5.1.1",
8687
"mocha": "^8.4.0",
8788
"mongodb-data-service": "^21.12.0",
8889
"mongodb-runner": "^4.8.3",
8990
"nyc": "^15.1.0",
9091
"prettier": "2.3.2",
9192
"rimraf": "^3.0.2",
9293
"sinon": "^9.2.3",
93-
"storage-mixin": "^4.7.0",
9494
"xvfb-maybe": "^0.2.1"
9595
}
9696
}

packages/connections/src/components/connections.spec.tsx

Lines changed: 79 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// eslint-disable-next-line @typescript-eslint/no-var-requires
2-
const { TestBackend } = require('storage-mixin');
3-
41
import React from 'react';
52
import {
63
cleanup,
@@ -10,63 +7,43 @@ import {
107
fireEvent,
118
} from '@testing-library/react';
129
import { expect } from 'chai';
13-
import fs from 'fs';
14-
import path from 'path';
15-
import os from 'os';
10+
import { ConnectionInfo } from 'mongodb-data-service';
1611
import { v4 as uuid } from 'uuid';
17-
import { convertConnectionInfoToModel } from 'mongodb-data-service';
1812
import sinon from 'sinon';
13+
import getPort from 'get-port';
1914

2015
import Connections from './connections';
21-
22-
function getConnectionFilePath(tmpDir: string, id: string): string {
23-
const connectionsDir = path.join(tmpDir, 'Connections');
24-
const filePath = path.join(connectionsDir, `${id}.json`);
25-
return filePath;
26-
}
27-
28-
// TODO: In typescript 4.5 we can just use Awaited and remove this.
29-
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
30-
31-
function writeFakeConnection(
32-
tmpDir: string,
33-
legacyConnection: Partial<
34-
Awaited<ReturnType<typeof convertConnectionInfoToModel>>
35-
> & { _id: string }
36-
) {
37-
const filePath = getConnectionFilePath(tmpDir, legacyConnection._id);
38-
const connectionsDir = path.dirname(filePath);
39-
fs.mkdirSync(connectionsDir, { recursive: true });
40-
fs.writeFileSync(filePath, JSON.stringify(legacyConnection));
16+
import { ConnectionStore } from '../stores/connections-store';
17+
18+
function getMockConnectionStorage(
19+
mockConnections: ConnectionInfo[]
20+
): ConnectionStore {
21+
return {
22+
loadAll: () => {
23+
return Promise.resolve(mockConnections);
24+
},
25+
};
4126
}
4227

4328
describe('Connections Component', function () {
44-
let tmpDir: string;
4529
let onConnectedSpy;
4630

4731
beforeEach(function () {
4832
onConnectedSpy = sinon.spy();
49-
50-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'connections-tests'));
51-
TestBackend.enable(tmpDir);
5233
});
5334

5435
afterEach(function () {
55-
TestBackend.disable();
56-
try {
57-
fs.rmdirSync(tmpDir, { recursive: true });
58-
} catch (e) {
59-
/* */
60-
}
36+
cleanup();
6137
});
6238

6339
describe('when rendered', function () {
6440
beforeEach(function () {
65-
render(<Connections onConnected={onConnectedSpy} />);
66-
});
67-
68-
afterEach(function () {
69-
cleanup();
41+
render(
42+
<Connections
43+
onConnected={onConnectedSpy}
44+
connectionStorage={getMockConnectionStorage([])}
45+
/>
46+
);
7047
});
7148

7249
it('renders the connect button from the connect-form', function () {
@@ -103,20 +80,25 @@ describe('Connections Component', function () {
10380

10481
beforeEach(async function () {
10582
savedConnectionId = uuid();
106-
writeFakeConnection(tmpDir, {
107-
_id: savedConnectionId,
108-
port: 27018,
109-
});
11083

111-
render(<Connections onConnected={onConnectedSpy} />);
84+
render(
85+
<Connections
86+
onConnected={onConnectedSpy}
87+
connectionStorage={getMockConnectionStorage([
88+
{
89+
id: savedConnectionId,
90+
connectionOptions: {
91+
connectionString:
92+
'mongodb://localhost:27018/?readPreference=primary&ssl=false',
93+
},
94+
},
95+
])}
96+
/>
97+
);
11298

11399
await waitFor(() => expect(screen.queryByRole('listitem')).to.be.visible);
114100
});
115101

116-
afterEach(function () {
117-
cleanup();
118-
});
119-
120102
it('should render the saved connections', function () {
121103
const listItems = screen.getAllByRole('listitem');
122104
expect(listItems.length).to.equal(1);
@@ -149,8 +131,16 @@ describe('Connections Component', function () {
149131
const connectButton = screen.getByText('Connect');
150132
fireEvent.click(connectButton);
151133

134+
// Wait for the connecting... modal to hide.
152135
await waitFor(
153-
() => expect(screen.queryByTestId('connections-connected')).to.exist
136+
() =>
137+
expect(screen.queryByTestId('cancel-connection-attempt-button')).to
138+
.not.exist
139+
);
140+
await waitFor(
141+
() =>
142+
expect(screen.queryByTestId('connections-disconnected')).to.not
143+
.exist
154144
);
155145
});
156146

@@ -179,29 +169,37 @@ describe('Connections Component', function () {
179169
describe('connecting to a connection that is not succeeding', function () {
180170
let savedConnectableId: string;
181171
let savedUnconnectableId: string;
172+
let inactiveTestPort: number;
173+
174+
before(async function () {
175+
inactiveTestPort = await getPort();
176+
});
182177

183178
beforeEach(async function () {
184179
savedConnectableId = uuid();
185180
savedUnconnectableId = uuid();
186-
writeFakeConnection(tmpDir, {
187-
_id: savedConnectableId,
188-
port: 27018,
189-
});
190-
writeFakeConnection(
191-
tmpDir,
192-
await convertConnectionInfoToModel({
193-
id: savedUnconnectableId,
194-
connectionOptions: {
195-
// Hopefully nothing is running on this port.
196-
// Times out in 5000ms.
197-
connectionString:
198-
'mongodb://localhost:28099/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000',
199-
},
200-
})
181+
render(
182+
<Connections
183+
onConnected={onConnectedSpy}
184+
connectionStorage={getMockConnectionStorage([
185+
{
186+
id: savedConnectableId,
187+
connectionOptions: {
188+
connectionString:
189+
'mongodb://localhost:27018/?readPreference=primary&ssl=false',
190+
},
191+
},
192+
{
193+
id: savedUnconnectableId,
194+
connectionOptions: {
195+
// Times out in 5000ms.
196+
connectionString: `mongodb://localhost:${inactiveTestPort}/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000`,
197+
},
198+
},
199+
])}
200+
/>
201201
);
202202

203-
render(<Connections onConnected={onConnectedSpy} />);
204-
205203
await waitFor(
206204
() =>
207205
expect(
@@ -219,7 +217,7 @@ describe('Connections Component', function () {
219217
// Wait for the connection to load in the form.
220218
await waitFor(() =>
221219
expect(screen.queryByRole('textbox').textContent).to.equal(
222-
'mongodb://localhost:28099/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000'
220+
`mongodb://localhost:${inactiveTestPort}/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000`
223221
)
224222
);
225223

@@ -229,15 +227,11 @@ describe('Connections Component', function () {
229227
// Wait for the connecting... modal to be shown.
230228
await waitFor(
231229
() =>
232-
expect(screen.queryByTestId('cancel-connection-attempt-button')).to
233-
.exist
230+
expect(screen.queryByTestId('cancel-connection-attempt-button')).to.be
231+
.visible
234232
);
235233
});
236234

237-
afterEach(function () {
238-
cleanup();
239-
});
240-
241235
describe('when the connection attempt is cancelled', function () {
242236
beforeEach(async function () {
243237
const cancelButton = screen.getByTestId(
@@ -283,8 +277,16 @@ describe('Connections Component', function () {
283277
const connectButton = screen.getByText('Connect');
284278
fireEvent.click(connectButton);
285279

280+
// Wait for the connecting... modal to hide.
281+
await waitFor(
282+
() =>
283+
expect(screen.queryByTestId('cancel-connection-attempt-button'))
284+
.to.not.exist
285+
);
286286
await waitFor(
287-
() => expect(screen.queryByTestId('connections-connected')).to.exist
287+
() =>
288+
expect(screen.queryByTestId('connections-disconnected')).to.not
289+
.exist
288290
);
289291
});
290292

packages/connections/src/components/connections.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import {
77
spacing,
88
} from '@mongodb-js/compass-components';
99
import ConnectForm from '@mongodb-js/connect-form';
10-
import { ConnectionInfo, DataService } from 'mongodb-data-service';
10+
import {
11+
ConnectionInfo,
12+
ConnectionStorage,
13+
DataService,
14+
} from 'mongodb-data-service';
1115

1216
import ResizableSidebar from './resizeable-sidebar';
1317
import FormHelp from './form-help/form-help';
1418
import Connecting from './connecting/connecting';
15-
import { useConnections } from '../stores/connections-store';
19+
import { ConnectionStore, useConnections } from '../stores/connections-store';
1620

1721
const connectStyles = css({
1822
position: 'absolute',
@@ -51,11 +55,13 @@ const formContainerStyles = css({
5155

5256
function Connections({
5357
onConnected,
58+
connectionStorage = new ConnectionStorage(),
5459
}: {
5560
onConnected: (
5661
connectionInfo: ConnectionInfo,
5762
dataService: DataService
5863
) => Promise<void>;
64+
connectionStorage?: ConnectionStore;
5965
}): React.ReactElement {
6066
const [
6167
{
@@ -72,7 +78,7 @@ function Connections({
7278
createNewConnection,
7379
setActiveConnectionById,
7480
},
75-
] = useConnections(onConnected);
81+
] = useConnections(onConnected, connectionStorage);
7682

7783
return (
7884
<div

packages/connections/src/stores/connections-store.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { v4 as uuidv4 } from 'uuid';
22
import {
33
ConnectionInfo,
4-
ConnectionStorage,
54
DataService,
65
getConnectionTitle,
76
} from 'mongodb-data-service';
@@ -21,6 +20,10 @@ export function createNewConnectionInfo(): ConnectionInfo {
2120
};
2221
}
2322

23+
export interface ConnectionStore {
24+
loadAll: () => Promise<ConnectionInfo[]>;
25+
}
26+
2427
type State = {
2528
activeConnectionId?: string;
2629
activeConnectionInfo: ConnectionInfo;
@@ -126,10 +129,10 @@ async function loadConnections(
126129
dispatch: React.Dispatch<{
127130
type: 'set-connections';
128131
connections: ConnectionInfo[];
129-
}>
132+
}>,
133+
connectionStorage: ConnectionStore
130134
) {
131135
try {
132-
const connectionStorage = new ConnectionStorage();
133136
const loadedConnections = await connectionStorage.loadAll();
134137

135138
dispatch({
@@ -145,7 +148,8 @@ export function useConnections(
145148
onConnected: (
146149
connectionInfo: ConnectionInfo,
147150
dataService: DataService
148-
) => Promise<void>
151+
) => Promise<void>,
152+
connectionStorage: ConnectionStore
149153
): [
150154
State,
151155
{
@@ -155,7 +159,7 @@ export function useConnections(
155159
setActiveConnectionById(newConnectionId?: string | undefined): void;
156160
}
157161
] {
158-
const [state, dispatch] = useReducer(
162+
const [state, dispatch]: [State, React.Dispatch<Action>] = useReducer(
159163
connectionsReducer,
160164
defaultConnectionsState()
161165
);
@@ -181,7 +185,7 @@ export function useConnections(
181185

182186
useEffect(() => {
183187
// Load connections after first render.
184-
void loadConnections(dispatch);
188+
void loadConnections(dispatch, connectionStorage);
185189

186190
return () => {
187191
// When unmounting, clean up any current connection attempts that have

0 commit comments

Comments
 (0)