Skip to content

Commit

Permalink
Merge pull request #6174 from dibarbet/jesting
Browse files Browse the repository at this point in the history
Update option changes toast and switch their tests to jest
  • Loading branch information
dibarbet authored Aug 23, 2023
2 parents fc6c9d3 + f7ea716 commit 0d2c3fe
Show file tree
Hide file tree
Showing 45 changed files with 21,173 additions and 13,631 deletions.
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"orta.vscode-jest"
]
}
15 changes: 15 additions & 0 deletions __mocks__/vscode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscodeAdapter from '../src/vscodeAdapter';
import { getFakeVsCode } from '../test/unitTests/fakes';

// This module creates a manual mock for the vscode module for running in unit tests.
// Jest will automatically pick this up as it is in the __mocks__ directory next to node_modules.

// We can consider switching to an actual jest mock (instead of this manual fake) once we entirely
// remove the old test framework (mocha/chai).
const vscode: vscodeAdapter.vscode = getFakeVsCode();
module.exports = vscode;
17 changes: 17 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,25 @@ stages:
matrix:
linux:
demandsName: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open
windows:
demandsName: ImageOverride -equals 1es-windows-2022-open
pool:
name: NetCore-Public
demands: $(demandsName)
steps:
- template: azure-pipelines/test.yml

- stage: Test_Omnisharp
displayName: Test Omnisharp
dependsOn: []
jobs:
- job: Test
strategy:
matrix:
linux:
demandsName: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open
pool:
name: NetCore-Public
demands: $(demandsName)
steps:
- template: azure-pipelines/test-omnisharp.yml
26 changes: 26 additions & 0 deletions azure-pipelines/test-omnisharp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
steps:
- checkout: self
clean: true
submodules: true
fetchTags: false
fetchDepth: 1

- template: prereqs.yml

- pwsh: |
if ($IsLinux) {
Write-Host "Activating screen emulation"
/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
$env:DISPLAY=':99.0'
Write-Host "Now running tests"
}
npm run omnisharptest
displayName: 🧪 Run unit and integration tests

- task: PublishPipelineArtifact@1
condition: failed()
displayName: 'Upload integration test logs'
inputs:
targetPath: '$(Build.SourcesDirectory)/.vscode-test/user-data/logs'
artifactName: 'VSCode Test Logs ($(Agent.JobName)-$(System.JobAttempt))'
2 changes: 1 addition & 1 deletion azure-pipelines/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ steps:
Write-Host "Now running tests"
}
npm run omnisharptest
npm run test
displayName: 🧪 Run unit and integration tests

- task: PublishPipelineArtifact@1
Expand Down
21 changes: 21 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { Config } from 'jest';

const config: Config = {
verbose: true,
preset: 'ts-jest',
testEnvironment: 'node',
transformIgnorePatterns: ['/dist/.+\\.js'],
// We need to explicity ignore the out directory for modules - otherwise we'll get duplicate vscode module,
// the TS version from the __mocks__ directory and the compiled js version from the out directory.
modulePathIgnorePatterns: ['out'],
// Specify jest to only run tests in jest folders.
// We also have to include the __mocks__ folder. That folder must be next to node_modules so we can't move it,
// but if we specify roots, jest won't automatically pick it up. So we have to specify it here.
roots: ['<rootDir>/test/unitTests', '<rootDir>/omnisharptest/omnisharpJestTests', '<rootDir>/__mocks__'],
};

export default config;
6 changes: 5 additions & 1 deletion l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,15 @@
"View Debug Docs": "View Debug Docs",
"Ignore": "Ignore",
"Run and Debug: A valid browser is not installed": "Run and Debug: A valid browser is not installed",
"dotnet.server.useOmnisharp option has changed. Please reload the window to apply the change": "dotnet.server.useOmnisharp option has changed. Please reload the window to apply the change",
"Reload Window": "Reload Window",
"C# configuration has changed. Would you like to relaunch the Language Server with your changes?": "C# configuration has changed. Would you like to relaunch the Language Server with your changes?",
"Restart Language Server": "Restart Language Server",
"Your workspace has multiple Visual Studio Solution files; please select one to get full IntelliSense.": "Your workspace has multiple Visual Studio Solution files; please select one to get full IntelliSense.",
"Choose": "Choose",
"Choose and set default": "Choose and set default",
"Do not load any": "Do not load any",
"Restart Language Server": "Restart Language Server",
"C# configuration has changed. Would you like to reload the window to apply your changes?": "C# configuration has changed. Would you like to reload the window to apply your changes?",
"pipeArgs must be a string or a string array type": "pipeArgs must be a string or a string array type",
"Name not defined in current configuration.": "Name not defined in current configuration.",
"Configuration \"{0}\" in launch.json does not have a {1} argument with {2} for remote process listing.": "Configuration \"{0}\" in launch.json does not have a {1} argument with {2} for remote process listing.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
ProjectConfiguration,
TelemetryErrorEvent,
} from '../../../src/omnisharp/loggingEvents';
import { getNullTelemetryReporter } from '../../omnisharpUnitTests/testAssets/fakes';
import { getNullTelemetryReporter } from '../../../test/unitTests/fakes';
import { Package } from '../../../src/packageManager/package';
import { PackageError } from '../../../src/packageManager/packageError';
import { isNotNull } from '../../testUtil';
Expand Down
164 changes: 164 additions & 0 deletions omnisharptest/omnisharpJestTests/optionChangeObserver.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { timeout } from 'rxjs/operators';
import { from as observableFrom, Subject, BehaviorSubject } from 'rxjs';
import { Options } from '../../src/shared/options';
import { registerOmnisharpOptionChanges } from '../../src/omnisharp/omnisharpOptionChanges';

import * as jestLib from '@jest/globals';
import * as vscode from 'vscode';
import { getVSCodeWithConfig, updateConfig } from '../../test/unitTests/fakes';

jestLib.describe('OmniSharpConfigChangeObserver', () => {
let doClickOk: () => void;
let doClickCancel: () => void;
let signalCommandDone: () => void;
let commandDone: Promise<void> | undefined;
let infoMessage: string | undefined;
let invokedCommand: string | undefined;
let optionObservable: Subject<Options>;

jestLib.beforeEach(() => {
resetMocks();
optionObservable = new BehaviorSubject<Options>(Options.Read(vscode));
infoMessage = undefined;
invokedCommand = undefined;
commandDone = new Promise<void>((resolve) => {
signalCommandDone = () => {
resolve();
};
});
registerOmnisharpOptionChanges(optionObservable);
});

[
{ config: 'omnisharp', section: 'path', value: 'somePath' },
{ config: 'omnisharp', section: 'waitForDebugger', value: true },
{ config: 'omnisharp', section: 'enableMsBuildLoadProjectsOnDemand', value: true },
{ config: 'omnisharp', section: 'useModernNet', value: false },
{ config: 'omnisharp', section: 'loggingLevel', value: 'verbose' },
].forEach((elem) => {
jestLib.describe(`When the ${elem.config} ${elem.section} changes`, () => {
jestLib.beforeEach(() => {
jestLib.expect(infoMessage).toBe(undefined);
jestLib.expect(invokedCommand).toBe(undefined);
updateConfig(vscode, elem.config, elem.section, elem.value);
optionObservable.next(Options.Read(vscode));
});

jestLib.test(`The information message is shown`, async () => {
jestLib
.expect(infoMessage)
.toEqual(
'C# configuration has changed. Would you like to relaunch the Language Server with your changes?'
);
});

jestLib.test(
'Given an information message if the user clicks cancel, the command is not executed',
async () => {
doClickCancel();
const from = observableFrom(commandDone!).pipe(timeout(1));
const fromPromise = from.toPromise();
await jestLib.expect(fromPromise).rejects.toThrow();
jestLib.expect(invokedCommand).toBe(undefined);
}
);

jestLib.test(
'Given an information message if the user clicks Reload, the command is executed',
async () => {
doClickOk();
await commandDone;
jestLib.expect(invokedCommand).toEqual('o.restart');
}
);
});
});

[{ config: 'dotnet', section: 'server.useOmnisharp', value: true }].forEach((elem) => {
jestLib.describe(`When the ${elem.config} ${elem.section} changes`, () => {
jestLib.beforeEach(() => {
jestLib.expect(infoMessage).toBe(undefined);
jestLib.expect(invokedCommand).toBe(undefined);
updateConfig(vscode, elem.config, elem.section, elem.value);
optionObservable.next(Options.Read(vscode));
});

jestLib.test(`The information message is shown`, async () => {
jestLib
.expect(infoMessage)
.toEqual(
'dotnet.server.useOmnisharp option has changed. Please reload the window to apply the change'
);
});

jestLib.test(
'Given an information message if the user clicks cancel, the command is not executed',
async () => {
doClickCancel();
const from = observableFrom(commandDone!).pipe(timeout(1));
const fromPromise = from.toPromise();
await jestLib.expect(fromPromise).rejects.toThrow();
jestLib.expect(invokedCommand).toBe(undefined);
}
);

jestLib.test(
'Given an information message if the user clicks Reload, the command is executed',
async () => {
doClickOk();
await commandDone;
jestLib.expect(invokedCommand).toEqual('workbench.action.reloadWindow');
}
);
});
});

[
{ config: 'csharp', section: 'disableCodeActions', value: true },
{ config: 'csharp', section: 'testsCodeLens.enabled', value: false },
{ config: 'omnisharp', section: 'referencesCodeLens.enabled', value: false },
{ config: 'csharp', section: 'format.enable', value: false },
{ config: 'omnisharp', section: 'useEditorFormattingSettings', value: false },
{ config: 'omnisharp', section: 'maxProjectResults', value: 1000 },
{ config: 'omnisharp', section: 'projectLoadTimeout', value: 1000 },
{ config: 'omnisharp', section: 'autoStart', value: false },
].forEach((elem) => {
jestLib.test(`Information Message is not shown on change in ${elem.config}.${elem.section}`, () => {
jestLib.expect(infoMessage).toBe(undefined);
jestLib.expect(invokedCommand).toBe(undefined);
updateConfig(vscode, elem.config, elem.section, elem.value);
optionObservable.next(Options.Read(vscode));
jestLib.expect(infoMessage).toBe(undefined);
});
});

function resetMocks() {
vscode.window.showInformationMessage = async <T>(message: string, ...items: T[]) => {
infoMessage = message;
return new Promise<T | undefined>((resolve) => {
doClickCancel = () => {
resolve(undefined);
};

doClickOk = () => {
resolve(items[0]);
};
});
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
vscode.commands.executeCommand = async (command: string, ..._: any[]) => {
invokedCommand = command;
signalCommandDone();
return undefined;
};

// This has to be replaced before every test to ensure that the config is reset.
getVSCodeWithConfig(vscode);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { getFakeVsCode } from '../testAssets/fakes';
import reportIssue from '../../../src/shared/reportIssue';
import { expect } from 'chai';
import { vscode } from '../../../src/vscodeAdapter';
Expand All @@ -12,6 +11,7 @@ import { FakeMonoResolver, fakeMonoInfo } from '../fakes/fakeMonoResolver';
import { FakeDotnetResolver } from '../fakes/fakeDotnetResolver';
import { DotnetInfo } from '../../../src/shared/utils/dotnetInfo';
import { getEmptyOptions } from '../fakes/fakeOptions';
import { getFakeVsCode } from '../../../test/unitTests/fakes';

suite(`${reportIssue.name}`, () => {
const vscodeVersion = 'myVersion';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { should, expect } from 'chai';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import { CsharpChannelObserver } from '../../../src/shared/observers/csharpChannelObserver';
import {
InstallationFailure,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { should, expect } from 'chai';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import { CsharpLoggerObserver } from '../../../src/shared/observers/csharpLoggerObserver';
import { PlatformInformation } from '../../../src/shared/platform';
import * as Event from '../../../src/omnisharp/loggingEvents';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { should, expect } from 'chai';
import { DotNetChannelObserver } from '../../../src/observers/dotnetChannelObserver';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import { CommandDotNetRestoreStart } from '../../../src/omnisharp/loggingEvents';

suite('DotnetChannelObserver', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { should, expect } from 'chai';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import { DotnetLoggerObserver } from '../../../src/observers/dotnetLoggerObserver';
import {
CommandDotNetRestoreProgress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { expect } from 'chai';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import {
BaseEvent,
DotNetTestsInClassDebugStart,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as chai from 'chai';
import * as chaiString from 'chai-string';
import { getNullChannel } from '../testAssets/fakes';
import { getNullChannel } from '../../../test/unitTests/fakes';
import {
EventWithMessage,
DotNetTestDebugWarning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import { expect, should } from 'chai';
import { vscode } from '../../../src/vscodeAdapter';
import { getFakeVsCode } from '../testAssets/fakes';

import { ErrorMessageObserver } from '../../../src/observers/errorMessageObserver';
import {
Expand All @@ -15,6 +14,7 @@ import {
EventWithMessage,
IntegrityCheckFailure,
} from '../../../src/omnisharp/loggingEvents';
import { getFakeVsCode } from '../../../test/unitTests/fakes';

suite('ErrorMessageObserver', () => {
suiteSetup(() => should());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { InformationMessageObserver } from '../../../src/observers/informationMessageObserver';
import OptionProvider from '../../../src/shared/observers/optionProvider';
import { expect, should } from 'chai';
import { getUnresolvedDependenices, updateConfig, getVSCodeWithConfig } from '../testAssets/fakes';
import { getUnresolvedDependenices, updateConfig, getVSCodeWithConfig } from '../../../test/unitTests/fakes';
import { Subject, from as observableFrom } from 'rxjs';
import { timeout } from 'rxjs/operators';
import { Options } from '../../../src/shared/options';
Expand Down
Loading

0 comments on commit 0d2c3fe

Please sign in to comment.