Skip to content

Commit 638f3a9

Browse files
authored
Merge pull request #3577 from Shopify/ar/improve-test-isolation
Enhance test isolation with disposal and sandbox patterns
2 parents 8662998 + df4f582 commit 638f3a9

19 files changed

+273
-235
lines changed

vscode/src/test/suite/client.test.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ import {
2525
ShowMessageParams,
2626
MessageType,
2727
} from "vscode-languageclient/node";
28-
import { after, afterEach, before, setup } from "mocha";
28+
import { after, afterEach, before, beforeEach, setup } from "mocha";
2929

3030
import { Ruby, ManagerIdentifier } from "../../ruby";
3131
import Client from "../../client";
3232
import { WorkspaceChannel } from "../../workspaceChannel";
3333
import { MAJOR, MINOR } from "../rubyVersion";
3434

3535
import { FAKE_TELEMETRY, FakeLogger } from "./fakeTelemetry";
36-
import { createRubySymlinks } from "./helpers";
36+
import { createContext, createRubySymlinks } from "./helpers";
3737

3838
async function launchClient(workspaceUri: vscode.Uri) {
3939
const workspaceFolder: vscode.WorkspaceFolder = {
@@ -42,15 +42,7 @@ async function launchClient(workspaceUri: vscode.Uri) {
4242
index: 0,
4343
};
4444

45-
const context = {
46-
extensionMode: vscode.ExtensionMode.Test,
47-
subscriptions: [],
48-
workspaceState: {
49-
get: (_name: string) => undefined,
50-
update: (_name: string, _value: any) => Promise.resolve(),
51-
},
52-
extensionUri: vscode.Uri.file(path.join(workspaceUri.fsPath, "vscode")),
53-
} as unknown as vscode.ExtensionContext;
45+
const context = createContext();
5446
const fakeLogger = new FakeLogger();
5547
const outputChannel = new WorkspaceChannel("fake", fakeLogger as any);
5648

@@ -149,6 +141,7 @@ suite("Client", () => {
149141
"fake.rb",
150142
);
151143
let client: Client;
144+
let sandbox: sinon.SinonSandbox;
152145

153146
before(async function () {
154147
// 60000 should be plenty but we're seeing timeouts on Windows in CI
@@ -158,6 +151,10 @@ suite("Client", () => {
158151
client = await launchClient(workspaceUri);
159152
});
160153

154+
beforeEach(() => {
155+
sandbox = sinon.createSandbox();
156+
});
157+
161158
after(async function () {
162159
// eslint-disable-next-line no-invalid-this
163160
this.timeout(20000);
@@ -185,6 +182,7 @@ suite("Client", () => {
185182
});
186183

187184
afterEach(async () => {
185+
sandbox.restore();
188186
await client.sendNotification("textDocument/didClose", {
189187
textDocument: {
190188
uri: documentUri.toString(),
@@ -815,15 +813,14 @@ suite("Client", () => {
815813
},
816814
});
817815

818-
const stub = sinon.stub(vscode.commands, "executeCommand").resolves(null);
816+
const stub = sandbox.stub(vscode.commands, "executeCommand").resolves(null);
819817

820818
await client.sendRequest("textDocument/definition", {
821819
textDocument: {
822820
uri,
823821
},
824822
position: { line: 1, character: 5 },
825823
});
826-
stub.restore();
827824

828825
await client.sendNotification("textDocument/didClose", {
829826
textDocument: { uri },
@@ -870,7 +867,7 @@ suite("Client", () => {
870867
},
871868
});
872869

873-
const stub = sinon.stub(vscode.commands, "executeCommand").resolves(null);
870+
const stub = sandbox.stub(vscode.commands, "executeCommand").resolves(null);
874871

875872
await client.sendRequest("textDocument/signatureHelp", {
876873
textDocument: {
@@ -879,7 +876,6 @@ suite("Client", () => {
879876
position: { line: 1, character: 23 },
880877
context: {},
881878
});
882-
stub.restore();
883879

884880
await client.sendNotification("textDocument/didClose", {
885881
textDocument: { uri },
@@ -927,7 +923,7 @@ suite("Client", () => {
927923
},
928924
});
929925

930-
const stub = sinon.stub(vscode.commands, "executeCommand").resolves(null);
926+
const stub = sandbox.stub(vscode.commands, "executeCommand").resolves(null);
931927

932928
await client.sendRequest("textDocument/documentHighlight", {
933929
textDocument: {
@@ -936,7 +932,6 @@ suite("Client", () => {
936932
position: { line: 1, character: 4 },
937933
context: {},
938934
});
939-
stub.restore();
940935

941936
await client.sendNotification("textDocument/didClose", {
942937
textDocument: { uri },

vscode/src/test/suite/debugger.test.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import { LOG_CHANNEL, asyncExec } from "../../common";
1414
import { RUBY_VERSION } from "../rubyVersion";
1515

1616
import { FAKE_TELEMETRY } from "./fakeTelemetry";
17-
import { createRubySymlinks } from "./helpers";
17+
import { createContext, createRubySymlinks, FakeContext } from "./helpers";
1818

1919
suite("Debugger", () => {
20+
let context: FakeContext;
21+
2022
const original = vscode.workspace
2123
.getConfiguration("debug")
2224
.get("saveBeforeStart");
@@ -25,16 +27,19 @@ suite("Debugger", () => {
2527
await vscode.workspace
2628
.getConfiguration("debug")
2729
.update("saveBeforeStart", "none", true);
30+
31+
context = createContext();
2832
});
2933

3034
afterEach(async () => {
3135
await vscode.workspace
3236
.getConfiguration("debug")
3337
.update("saveBeforeStart", original, true);
38+
39+
context.dispose();
3440
});
3541

3642
test("Provide debug configurations returns the default configs", () => {
37-
const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;
3843
const debug = new Debugger(context, () => {
3944
return undefined;
4045
});
@@ -69,7 +74,6 @@ suite("Debugger", () => {
6974
});
7075

7176
test("Resolve configuration injects Ruby environment", async () => {
72-
const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;
7377
const ruby = {
7478
env: { bogus: "hello!", overrideMe: "oldValue" },
7579
} as unknown as Ruby;
@@ -105,7 +109,6 @@ suite("Debugger", () => {
105109
});
106110

107111
test("Resolve configuration injects Ruby environment and allows users custom environment", async () => {
108-
const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;
109112
const ruby = { env: { bogus: "hello!" } } as unknown as Ruby;
110113
const workspaceFolder = {
111114
name: "fake",
@@ -140,7 +143,6 @@ suite("Debugger", () => {
140143
fs.mkdirSync(path.join(tmpPath, ".ruby-lsp"));
141144
fs.writeFileSync(path.join(tmpPath, ".ruby-lsp", "Gemfile"), "hello!");
142145

143-
const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;
144146
const ruby = { env: { bogus: "hello!" } } as unknown as Ruby;
145147
const workspaceFolder = {
146148
name: "fake",
@@ -204,15 +206,6 @@ suite("Debugger", () => {
204206
'source "https://rubygems.org"\ngem "debug"',
205207
);
206208

207-
const extensionPath = path.dirname(path.dirname(path.dirname(__dirname)));
208-
const context = {
209-
subscriptions: [],
210-
workspaceState: {
211-
get: () => undefined,
212-
update: () => undefined,
213-
},
214-
extensionUri: vscode.Uri.file(extensionPath),
215-
} as unknown as vscode.ExtensionContext;
216209
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);
217210
const workspaceFolder: vscode.WorkspaceFolder = {
218211
uri: vscode.Uri.file(tmpPath),

vscode/src/test/suite/helpers.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,19 @@ export const LSP_WORKSPACE_FOLDER: vscode.WorkspaceFolder = {
7070
name: path.basename(LSP_WORKSPACE_PATH),
7171
index: 0,
7272
};
73-
export const CONTEXT = {
74-
extensionMode: vscode.ExtensionMode.Test,
75-
subscriptions: [],
76-
workspaceState: new FakeWorkspaceState(),
77-
extensionUri: vscode.Uri.joinPath(LSP_WORKSPACE_URI, "vscode"),
78-
} as unknown as vscode.ExtensionContext;
73+
74+
export type FakeContext = vscode.ExtensionContext & { dispose: () => void };
75+
76+
export function createContext() {
77+
const subscriptions: vscode.Disposable[] = [];
78+
79+
return {
80+
extensionMode: vscode.ExtensionMode.Test,
81+
subscriptions,
82+
workspaceState: new FakeWorkspaceState(),
83+
extensionUri: vscode.Uri.joinPath(LSP_WORKSPACE_URI, "vscode"),
84+
dispose: () => {
85+
subscriptions.forEach((subscription) => subscription.dispose());
86+
},
87+
} as unknown as FakeContext;
88+
}

vscode/src/test/suite/launch.test.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import os from "os";
66
import * as vscode from "vscode";
77
import { State } from "vscode-languageclient/node";
88
import sinon from "sinon";
9-
import { beforeEach } from "mocha";
9+
import { afterEach, beforeEach } from "mocha";
1010

1111
import { ManagerIdentifier, Ruby } from "../../ruby";
1212
import Client from "../../client";
1313
import { WorkspaceChannel } from "../../workspaceChannel";
1414
import * as common from "../../common";
1515

1616
import { FAKE_TELEMETRY, FakeLogger } from "./fakeTelemetry";
17-
import { createRubySymlinks } from "./helpers";
17+
import { createContext, createRubySymlinks, FakeContext } from "./helpers";
1818

1919
suite("Launch integrations", () => {
2020
const workspacePath = path.dirname(
@@ -27,15 +27,19 @@ suite("Launch integrations", () => {
2727
index: 0,
2828
};
2929

30-
const context = {
31-
extensionMode: vscode.ExtensionMode.Test,
32-
subscriptions: [],
33-
workspaceState: {
34-
get: (_name: string) => undefined,
35-
update: (_name: string, _value: any) => Promise.resolve(),
36-
},
37-
extensionUri: vscode.Uri.joinPath(workspaceUri, "vscode"),
38-
} as unknown as vscode.ExtensionContext;
30+
let context: FakeContext;
31+
let sandbox: sinon.SinonSandbox;
32+
33+
beforeEach(() => {
34+
sandbox = sinon.createSandbox();
35+
context = createContext();
36+
});
37+
38+
afterEach(() => {
39+
sandbox.restore();
40+
context.dispose();
41+
});
42+
3943
const fakeLogger = new FakeLogger();
4044
const outputChannel = new WorkspaceChannel("fake", fakeLogger as any);
4145

@@ -97,9 +101,8 @@ suite("Launch integrations", () => {
97101
});
98102

99103
test("with launcher mode enabled", async () => {
100-
const featureStub = sinon.stub(common, "featureEnabled").returns(true);
104+
sandbox.stub(common, "featureEnabled").returns(true);
101105
const client = await createClient();
102-
featureStub.restore();
103106

104107
await startClient(client);
105108

vscode/src/test/suite/rails.test.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "path";
44

55
import * as vscode from "vscode";
66
import sinon from "sinon";
7+
import { beforeEach, afterEach } from "mocha";
78

89
import { Rails } from "../../rails";
910
import { Workspace } from "../../workspace";
@@ -21,8 +22,18 @@ suite("Rails", () => {
2122
index: 0,
2223
};
2324

25+
let sandbox: sinon.SinonSandbox;
26+
27+
beforeEach(() => {
28+
sandbox = sinon.createSandbox();
29+
});
30+
31+
afterEach(() => {
32+
sandbox.restore();
33+
});
34+
2435
test("generate", async () => {
25-
const executeStub = sinon.stub();
36+
const executeStub = sandbox.stub();
2637
const workspace = {
2738
workspaceFolder,
2839
execute: executeStub.resolves({
@@ -31,8 +42,8 @@ suite("Rails", () => {
3142
}),
3243
} as unknown as Workspace;
3344

34-
const showDocumentStub = sinon.stub(vscode.window, "showTextDocument");
35-
const executeCommandStub = sinon.stub(vscode.commands, "executeCommand");
45+
const showDocumentStub = sandbox.stub(vscode.window, "showTextDocument");
46+
sandbox.stub(vscode.commands, "executeCommand");
3647

3748
const selectedWorkspace = undefined;
3849
const rails = new Rails(() => Promise.resolve(workspace));
@@ -64,12 +75,10 @@ suite("Rails", () => {
6475
{ preview: false },
6576
),
6677
);
67-
showDocumentStub.restore();
68-
executeCommandStub.restore();
6978
});
7079

7180
test("destroy", async () => {
72-
const executeStub = sinon.stub();
81+
const executeStub = sandbox.stub();
7382
const workspace = {
7483
workspaceFolder,
7584
execute: executeStub.resolves({
@@ -78,7 +87,7 @@ suite("Rails", () => {
7887
}),
7988
} as unknown as Workspace;
8089

81-
const executeCommandStub = sinon.stub(vscode.commands, "executeCommand");
90+
const executeCommandStub = sandbox.stub(vscode.commands, "executeCommand");
8291

8392
const selectedWorkspace = undefined;
8493
const rails = new Rails(() => Promise.resolve(workspace));
@@ -110,6 +119,5 @@ suite("Rails", () => {
110119
vscode.Uri.joinPath(workspaceUri, "app/models/user.rb"),
111120
),
112121
);
113-
executeCommandStub.restore();
114122
});
115123
});

0 commit comments

Comments
 (0)