From 3b2b34308af678aa1318dde502525761feeabcb6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 6 Jan 2019 22:56:17 -0800 Subject: [PATCH 1/2] Changes to how source maps are enabled and disabled in the extension --- build/webpack/webpack.debugadapter.config.js | 1 - build/webpack/webpack.debugadapter.config.ts | 2 +- build/webpack/webpack.extension.config.js | 11 +-- build/webpack/webpack.extension.config.ts | 13 +-- .../webpack.extension.sourceMaps.config.js | 59 ------------- .../webpack.extension.sourceMaps.config.ts | 65 --------------- gulpfile.js | 11 ++- news/2 Fixes/3905.md | 1 + package-lock.json | 9 -- package.json | 2 - src/client/extension.ts | 8 +- src/client/sourceMapSupport.ts | 43 ++++++++-- src/test/sourceMapSupport.unit.test.ts | 82 +++++++++++++++++-- 13 files changed, 133 insertions(+), 174 deletions(-) delete mode 100644 build/webpack/webpack.extension.sourceMaps.config.js delete mode 100644 build/webpack/webpack.extension.sourceMaps.config.ts create mode 100644 news/2 Fixes/3905.md diff --git a/build/webpack/webpack.debugadapter.config.js b/build/webpack/webpack.debugadapter.config.js index 82e48252ee5e..aba65b8d30e6 100644 --- a/build/webpack/webpack.debugadapter.config.js +++ b/build/webpack/webpack.debugadapter.config.js @@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const tsconfig_paths_webpack_plugin_1 = require("tsconfig-paths-webpack-plugin"); -const webpack_1 = require("webpack"); const constants_1 = require("../constants"); const common_1 = require("./common"); // tslint:disable-next-line:no-var-requires no-require-imports diff --git a/build/webpack/webpack.debugadapter.config.ts b/build/webpack/webpack.debugadapter.config.ts index 7f5b4f0ee728..23625e8c0c3d 100644 --- a/build/webpack/webpack.debugadapter.config.ts +++ b/build/webpack/webpack.debugadapter.config.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; -import { Configuration, ContextReplacementPlugin } from 'webpack'; +import { Configuration } from 'webpack'; import { ExtensionRootDir } from '../constants'; import { getDefaultPlugins } from './common'; diff --git a/build/webpack/webpack.extension.config.js b/build/webpack/webpack.extension.config.js index f30cb43bd08e..9f7a1e2d3899 100644 --- a/build/webpack/webpack.extension.config.js +++ b/build/webpack/webpack.extension.config.js @@ -4,12 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const tsconfig_paths_webpack_plugin_1 = require("tsconfig-paths-webpack-plugin"); -const webpack_1 = require("webpack"); const constants_1 = require("../constants"); const common_1 = require("./common"); // tslint:disable-next-line:no-var-requires no-require-imports -const WrapperPlugin = require('wrapper-webpack-plugin'); -// tslint:disable-next-line:no-var-requires no-require-imports const configFileName = path.join(constants_1.ExtensionRootDir, 'tsconfig.extension.json'); // Some modules will be pre-genearted and stored in out/.. dir and they'll be referenced via NormalModuleReplacementPlugin // We need to ensure they do not get bundled into the output (as they are large). @@ -60,13 +57,7 @@ const config = { ...existingModulesInOutDir ], plugins: [ - ...common_1.getDefaultPlugins('extension'), - new WrapperPlugin({ - test: /\extension.js$/, - // Import source map warning file only if source map is enabled. - // Minimize importing external files. - header: '(function(){if (require(\'vscode\').workspace.getConfiguration(\'python.diagnostics\', undefined).get(\'sourceMapsEnabled\', false)) {require(\'./sourceMapSupport\').default(require(\'vscode\'));}})();' - }) + ...common_1.getDefaultPlugins('extension') ], resolve: { extensions: ['.ts', '.js'], diff --git a/build/webpack/webpack.extension.config.ts b/build/webpack/webpack.extension.config.ts index 7d84edd037a9..4d9eba1ec88e 100644 --- a/build/webpack/webpack.extension.config.ts +++ b/build/webpack/webpack.extension.config.ts @@ -5,13 +5,10 @@ import * as path from 'path'; import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; -import { Configuration, ContextReplacementPlugin } from 'webpack'; +import { Configuration } from 'webpack'; import { ExtensionRootDir } from '../constants'; import { getDefaultPlugins, getListOfExistingModulesInOutDir } from './common'; -// tslint:disable-next-line:no-var-requires no-require-imports -const WrapperPlugin = require('wrapper-webpack-plugin'); - // tslint:disable-next-line:no-var-requires no-require-imports const configFileName = path.join(ExtensionRootDir, 'tsconfig.extension.json'); @@ -65,13 +62,7 @@ const config: Configuration = { ...existingModulesInOutDir ], plugins: [ - ...getDefaultPlugins('extension'), - new WrapperPlugin({ - test: /\extension.js$/, - // Import source map warning file only if source map is enabled. - // Minimize importing external files. - header: '(function(){if (require(\'vscode\').workspace.getConfiguration(\'python.diagnostics\', undefined).get(\'sourceMapsEnabled\', false)) {require(\'./sourceMapSupport\').default(require(\'vscode\'));}})();' - }) + ...getDefaultPlugins('extension') ], resolve: { extensions: ['.ts', '.js'], diff --git a/build/webpack/webpack.extension.sourceMaps.config.js b/build/webpack/webpack.extension.sourceMaps.config.js deleted file mode 100644 index 465aa24c12df..000000000000 --- a/build/webpack/webpack.extension.sourceMaps.config.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const tsconfig_paths_webpack_plugin_1 = require("tsconfig-paths-webpack-plugin"); -const constants_1 = require("../constants"); -const common_1 = require("./common"); -// tslint:disable-next-line:no-var-requires no-require-imports -const configFileName = path.join(constants_1.ExtensionRootDir, 'tsconfig.extension.json'); -// Some modules will be pre-genearted and stored in out/.. dir and they'll be referenced via NormalModuleReplacementPlugin -// We need to ensure they do not get bundled into the output (as they are large). -const existingModulesInOutDir = common_1.getListOfExistingModulesInOutDir(); -const config = { - mode: 'production', - target: 'node', - entry: { - sourceMapSupport: './src/client/sourceMapSupport.ts' - }, - devtool: 'source-map', - node: { - __dirname: false - }, - module: { - rules: [ - { - test: /\.ts$/, - exclude: /node_modules/, - use: [ - { - loader: 'ts-loader' - } - ] - } - ] - }, - externals: [ - 'vscode', - 'commonjs', - ...existingModulesInOutDir - ], - plugins: [ - ...common_1.getDefaultPlugins('dependencies') - ], - resolve: { - extensions: ['.ts', '.js'], - plugins: [ - new tsconfig_paths_webpack_plugin_1.TsconfigPathsPlugin({ configFile: configFileName }) - ] - }, - output: { - filename: '[name].js', - path: path.resolve(constants_1.ExtensionRootDir, 'out', 'client'), - libraryTarget: 'commonjs2', - devtoolModuleFilenameTemplate: '../../[resource-path]' - } -}; -// tslint:disable-next-line:no-default-export -exports.default = config; diff --git a/build/webpack/webpack.extension.sourceMaps.config.ts b/build/webpack/webpack.extension.sourceMaps.config.ts deleted file mode 100644 index 59c571c5466a..000000000000 --- a/build/webpack/webpack.extension.sourceMaps.config.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as path from 'path'; -import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; -import * as webpack from 'webpack'; -import { ExtensionRootDir } from '../constants'; -import { getDefaultPlugins, getListOfExistingModulesInOutDir } from './common'; - -// tslint:disable-next-line:no-var-requires no-require-imports -const configFileName = path.join(ExtensionRootDir, 'tsconfig.extension.json'); - -// Some modules will be pre-genearted and stored in out/.. dir and they'll be referenced via NormalModuleReplacementPlugin -// We need to ensure they do not get bundled into the output (as they are large). -const existingModulesInOutDir = getListOfExistingModulesInOutDir(); - -const config: webpack.Configuration = { - mode: 'production', - target: 'node', - entry: { - sourceMapSupport: './src/client/sourceMapSupport.ts' - }, - devtool: 'source-map', - node: { - __dirname: false - }, - module: { - rules: [ - { - test: /\.ts$/, - exclude: /node_modules/, - use: [ - { - loader: 'ts-loader' - } - ] - } - ] - }, - externals: [ - 'vscode', - 'commonjs', - ...existingModulesInOutDir - ], - plugins: [ - ...getDefaultPlugins('dependencies') - ], - resolve: { - extensions: ['.ts', '.js'], - plugins: [ - new TsconfigPathsPlugin({ configFile: configFileName }) - ] - }, - output: { - filename: '[name].js', - path: path.resolve(ExtensionRootDir, 'out', 'client'), - libraryTarget: 'commonjs2', - devtoolModuleFilenameTemplate: '../../[resource-path]' - } -}; - -// tslint:disable-next-line:no-default-export -export default config; diff --git a/gulpfile.js b/gulpfile.js index 53cf2ed36863..37e6898cc42a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -197,7 +197,16 @@ function getAllowedWarningsForWebPack(buildConfig) { throw new Error('Unknown WebPack Configuration'); } } -gulp.task('prePublishBundle', gulp.series('checkNativeDependencies', 'check-datascience-dependencies', 'compile', 'clean:cleanExceptTests', 'webpack')); +gulp.task('renameSourceMaps', async () => { + // By default souce maps will be disabled in the extenion. + // Users will need to use the command `python.enableSourceMapSupport` to enable source maps. + const extensionSourceMap = path.join(__dirname, 'out', 'client', 'extension.js.map'); + const debuggerSourceMap = path.join(__dirname, 'out', 'client', 'debugger', 'debugAdapter', 'main.js.map'); + await fs.rename(extensionSourceMap, `${extensionSourceMap}.disabled`); + await fs.rename(debuggerSourceMap, `${debuggerSourceMap}.disabled`); +}); + +gulp.task('prePublishBundle', gulp.series('checkNativeDependencies', 'check-datascience-dependencies', 'compile', 'clean:cleanExceptTests', 'webpack', 'renameSourceMaps')); gulp.task('prePublishNonBundle', gulp.series('checkNativeDependencies', 'check-datascience-dependencies', 'compile', 'compile-webviews')); const installPythonLibArgs = ['-m', 'pip', '--disable-pip-version-check', 'install', diff --git a/news/2 Fixes/3905.md b/news/2 Fixes/3905.md new file mode 100644 index 000000000000..e7b7bf06c377 --- /dev/null +++ b/news/2 Fixes/3905.md @@ -0,0 +1 @@ +Changes to how source maps are enabled and disabled in the extension. diff --git a/package-lock.json b/package-lock.json index e36ec2a2e558..04685d7a56a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18039,15 +18039,6 @@ } } }, - "wrapper-webpack-plugin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wrapper-webpack-plugin/-/wrapper-webpack-plugin-2.0.0.tgz", - "integrity": "sha512-HiykPJTuiaPiR9Q89sRbTjWJ9J/AkriPTbIYaAAW5ulfaK7p5GqK9cB+RWwFhfa17Sn5ehqJ2/qxF4XbQCDGvg==", - "dev": true, - "requires": { - "webpack-sources": "^1.1.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index cf3c2b0cfb9b..160b6771442a 100644 --- a/package.json +++ b/package.json @@ -1990,7 +1990,6 @@ "retyped-diff-match-patch-tsd-ambient": "^1.0.0-0", "rewiremock": "^3.13.0", "shortid": "^2.2.8", - "source-map-support": "^0.5.9", "style-loader": "^0.23.1", "styled-jsx": "^3.1.0", "svg-inline-loader": "^0.8.0", @@ -2015,7 +2014,6 @@ "webpack-fix-default-import-plugin": "^1.0.3", "webpack-merge": "^4.1.4", "webpack-node-externals": "^1.7.2", - "wrapper-webpack-plugin": "^2.0.0", "yargs": "^12.0.2" }, "__metadata": { diff --git a/src/client/extension.ts b/src/client/extension.ts index 4a6cbacf4699..57f7419782bf 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -1,10 +1,16 @@ 'use strict'; +// tslint:disable:no-var-requires no-require-imports + // This line should always be right on top. // tslint:disable-next-line:no-any if ((Reflect as any).metadata === undefined) { - // tslint:disable-next-line:no-require-imports no-var-requires require('reflect-metadata'); } + +// Initialize source maps (this must never be moved up nor further down). +import {initialize } from './sourceMapSupport'; +initialize(require('vscode')); + const durations: { [key: string]: number } = {}; import { StopWatch } from './common/utils/stopWatch'; // Do not move this line of code (used to measure extension load times). diff --git a/src/client/sourceMapSupport.ts b/src/client/sourceMapSupport.ts index 455ee72f8391..8349dea9b497 100644 --- a/src/client/sourceMapSupport.ts +++ b/src/client/sourceMapSupport.ts @@ -3,7 +3,13 @@ 'use strict'; +import * as fs from 'fs'; +import * as path from 'path'; +import { promisify } from 'util'; import { WorkspaceConfiguration } from 'vscode'; +import './common/extensions'; +import { EXTENSION_ROOT_DIR } from './constants'; + type VSCode = typeof import('vscode'); // tslint:disable:no-require-imports @@ -18,7 +24,7 @@ export class SourceMapSupport { if (!this.enabled) { return; } - this.initializeSourceMaps(); + await this.enableSourceMaps(true); const localize = require('./common/utils/localize') as typeof import('./common/utils/localize'); const disable = localize.Diagnostics.disableSourceMaps(); const selection = await this.vscode.window.showWarningMessage(localize.Diagnostics.warnSourceMaps(), disable); @@ -30,14 +36,39 @@ export class SourceMapSupport { return this.config.get(setting, false); } public async disable(): Promise { - await this.config.update(setting, false, this.vscode.ConfigurationTarget.Global); + if (this.enabled) { + await this.config.update(setting, false, this.vscode.ConfigurationTarget.Global); + } + await this.enableSourceMaps(false); + } + protected async enableSourceMaps(enable: boolean) { + const extensionSourceFile = path.join(EXTENSION_ROOT_DIR, 'out', 'client', 'extension.js'); + const debuggerSourceFile = path.join(EXTENSION_ROOT_DIR, 'out', 'client', 'debugger', 'debugAdapter', 'main.js'); + await Promise.all([this.enableSourceMap(enable, extensionSourceFile), this.enableSourceMap(enable, debuggerSourceFile)]); + } + protected async enableSourceMap(enable: boolean, sourceFile: string) { + const sourceMapFile = `${sourceFile}.map`; + const disabledSourceMapFile = `${sourceFile}.map.disabled`; + if (enable) { + await this.rename(disabledSourceMapFile, sourceMapFile); + } else { + await this.rename(sourceMapFile, disabledSourceMapFile); + } } - protected initializeSourceMaps() { - require('./node_modules/source-map-support').install(); + protected async rename(sourceFile: string, targetFile: string) { + const fsExists = promisify(fs.exists); + const fsRename = promisify(fs.rename); + if (await fsExists(targetFile)) { + return; + } + await fsRename(sourceFile, targetFile); } } -// tslint:disable-next-line:no-default-export -export default function initialize(vscode: VSCode) { +export function initialize(vscode: VSCode = require('vscode')) { + if (!vscode.workspace.getConfiguration('python.diagnostics', undefined).get('sourceMapsEnabled', false)) { + new SourceMapSupport(vscode).disable().ignoreErrors(); + return; + } new SourceMapSupport(vscode).initialize().catch(ex => { console.error('Failed to initialize source map support in extension'); }); diff --git a/src/test/sourceMapSupport.unit.test.ts b/src/test/sourceMapSupport.unit.test.ts index be33b5027f28..fe8642771184 100644 --- a/src/test/sourceMapSupport.unit.test.ts +++ b/src/test/sourceMapSupport.unit.test.ts @@ -3,12 +3,17 @@ 'use strict'; -// tslint:disable:no-any +// tslint:disable:no-any no-unused-expression chai-vague-errors no-unnecessary-override max-func-body-length max-classes-per-file import { expect } from 'chai'; -import { ConfigurationTarget } from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ConfigurationTarget, Disposable } from 'vscode'; +import { FileSystem } from '../client/common/platform/fileSystem'; +import { PlatformService } from '../client/common/platform/platformService'; import { Diagnostics } from '../client/common/utils/localize'; -import * as sourceMaps from '../client/sourceMapSupport'; +import { EXTENSION_ROOT_DIR } from '../client/constants'; +import { initialize, SourceMapSupport } from '../client/sourceMapSupport'; import { noop, sleep } from './core'; suite('Source Map Support', () => { @@ -47,17 +52,26 @@ suite('Source Map Support', () => { }; return { stubInfo, vscode }; } + + const disposables: Disposable[] = []; + teardown(() => { + disposables.forEach(disposable => { + try { + disposable.dispose(); + } catch { noop(); } + }); + }); test('Test message is not displayed when source maps are not enabled', async () => { const stub = createVSCStub(false); - sourceMaps.default(stub.vscode as any); + initialize(stub.vscode as any); await sleep(100); expect(stub.stubInfo.configValueRetrieved).to.be.equal(true, 'Config Value not retrieved'); expect(stub.stubInfo.messageDisplayed).to.be.equal(false, 'Message displayed'); }); test('Test message is not displayed when source maps are not enabled', async () => { const stub = createVSCStub(true); - const instance = new class extends sourceMaps.SourceMapSupport { - protected initializeSourceMaps() { + const instance = new class extends SourceMapSupport { + protected async enableSourceMaps(enable: boolean) { noop(); } }(stub.vscode as any); @@ -68,14 +82,66 @@ suite('Source Map Support', () => { }); test('Test message is not displayed when source maps are not enabled', async () => { const stub = createVSCStub(true, true); - const instance = new class extends sourceMaps.SourceMapSupport { - protected initializeSourceMaps() { + const instance = new class extends SourceMapSupport { + protected async enableSourceMaps(enable: boolean) { noop(); } }(stub.vscode as any); + await instance.initialize(); expect(stub.stubInfo.configValueRetrieved).to.be.equal(true, 'Config Value not retrieved'); expect(stub.stubInfo.messageDisplayed).to.be.equal(true, 'Message displayed'); expect(stub.stubInfo.configValueUpdated).to.be.equal(true, 'Config Value not updated'); }); + async function testRenamingFilesWhenEnablingDisablingSourceMaps(enableSourceMaps: boolean) { + const stub = createVSCStub(true, true); + const sourceFilesPassed: string[] = []; + const instance = new class extends SourceMapSupport { + public async enableSourceMaps(enable: boolean) { + return super.enableSourceMaps(enable); + } + public async enableSourceMap(enable: boolean, sourceFile: string) { + expect(enable).to.equal(enableSourceMaps); + sourceFilesPassed.push(sourceFile); + return Promise.resolve(); + } + }(stub.vscode as any); + + await instance.enableSourceMaps(enableSourceMaps); + const extensionSourceMap = path.join(EXTENSION_ROOT_DIR, 'out', 'client', 'extension.js'); + const debuggerSourceMap = path.join(EXTENSION_ROOT_DIR, 'out', 'client', 'debugger', 'debugAdapter', 'main.js'); + expect(sourceFilesPassed).to.deep.equal([extensionSourceMap, debuggerSourceMap]); + } + test('Rename extension and debugger source maps when enabling source maps', () => testRenamingFilesWhenEnablingDisablingSourceMaps(true)); + test('Rename extension and debugger source maps when disabling source maps', () => testRenamingFilesWhenEnablingDisablingSourceMaps(false)); + test('When disabling source maps, the map file is renamed and vice versa', async () => { + const fileSystem = new FileSystem(new PlatformService()); + const jsFile = await fileSystem.createTemporaryFile('.js'); + disposables.push(jsFile); + const mapFile = `${jsFile.filePath}.map`; + disposables.push({ + dispose: () => fs.unlinkSync(mapFile) + }); + await fileSystem.writeFile(mapFile, 'ABC'); + expect(await fileSystem.fileExists(mapFile)).to.be.true; + + const stub = createVSCStub(true, true); + const instance = new class extends SourceMapSupport { + public async enableSourceMap(enable: boolean, sourceFile: string) { + return super.enableSourceMap(enable, sourceFile); + } + }(stub.vscode as any); + + await instance.enableSourceMap(false, jsFile.filePath); + + expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); + expect(await fileSystem.fileExists(mapFile)).to.be.equal(false, 'Source map file not renamed'); + expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(true, 'Expected renamed file not found'); + + await instance.enableSourceMap(true, jsFile.filePath); + + expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); + expect(await fileSystem.fileExists(mapFile)).to.be.equal(true, 'Source map file not found'); + expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(false, 'Source map file not renamed'); + }); }); From e14a94ae3c06da537c143788e98cdc4c02e1e58d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 17 Jan 2019 20:27:37 -0800 Subject: [PATCH 2/2] Fixed tests --- src/test/sourceMapSupport.test.ts | 98 ++++++++++++++++++++++++++ src/test/sourceMapSupport.unit.test.ts | 33 --------- 2 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 src/test/sourceMapSupport.test.ts diff --git a/src/test/sourceMapSupport.test.ts b/src/test/sourceMapSupport.test.ts new file mode 100644 index 000000000000..67295bd7157d --- /dev/null +++ b/src/test/sourceMapSupport.test.ts @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-any no-unused-expression chai-vague-errors no-unnecessary-override max-func-body-length max-classes-per-file + +import { expect } from 'chai'; +import * as fs from 'fs'; +import { ConfigurationTarget, Disposable } from 'vscode'; +import { FileSystem } from '../client/common/platform/fileSystem'; +import { PlatformService } from '../client/common/platform/platformService'; +import { Diagnostics } from '../client/common/utils/localize'; +import { SourceMapSupport } from '../client/sourceMapSupport'; +import { noop } from './core'; + +suite('Source Map Support', () => { + function createVSCStub(isEnabled: boolean = false, selectDisableButton: boolean = false) { + const stubInfo = { + configValueRetrieved: false, + configValueUpdated: false, + messageDisplayed: false + }; + const vscode = { + workspace: { + getConfiguration: (setting: string, _defaultValue: any) => { + if (setting !== 'python.diagnostics') { + return; + } + return { + get: (prop: string) => { + stubInfo.configValueRetrieved = prop === 'sourceMapsEnabled'; + return isEnabled; + }, + update: (prop: string, value: boolean, scope: ConfigurationTarget) => { + if ( + prop === 'sourceMapsEnabled' && + value === false && + scope === ConfigurationTarget.Global + ) { + stubInfo.configValueUpdated = true; + } + } + }; + } + }, + window: { + showWarningMessage: () => { + stubInfo.messageDisplayed = true; + return Promise.resolve(selectDisableButton ? Diagnostics.disableSourceMaps() : undefined); + } + }, + ConfigurationTarget: ConfigurationTarget + }; + return { stubInfo, vscode }; + } + + const disposables: Disposable[] = []; + teardown(() => { + disposables.forEach(disposable => { + try { + disposable.dispose(); + } catch { + noop(); + } + }); + }); + test('When disabling source maps, the map file is renamed and vice versa', async () => { + const fileSystem = new FileSystem(new PlatformService()); + const jsFile = await fileSystem.createTemporaryFile('.js'); + disposables.push(jsFile); + const mapFile = `${jsFile.filePath}.map`; + disposables.push({ + dispose: () => fs.unlinkSync(mapFile) + }); + await fileSystem.writeFile(mapFile, 'ABC'); + expect(await fileSystem.fileExists(mapFile)).to.be.true; + + const stub = createVSCStub(true, true); + const instance = new class extends SourceMapSupport { + public async enableSourceMap(enable: boolean, sourceFile: string) { + return super.enableSourceMap(enable, sourceFile); + } + }(stub.vscode as any); + + await instance.enableSourceMap(false, jsFile.filePath); + + expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); + expect(await fileSystem.fileExists(mapFile)).to.be.equal(false, 'Source map file not renamed'); + expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(true, 'Expected renamed file not found'); + + await instance.enableSourceMap(true, jsFile.filePath); + + expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); + expect(await fileSystem.fileExists(mapFile)).to.be.equal(true, 'Source map file not found'); + expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(false, 'Source map file not renamed'); + }); +}); diff --git a/src/test/sourceMapSupport.unit.test.ts b/src/test/sourceMapSupport.unit.test.ts index fe8642771184..2deec33deb12 100644 --- a/src/test/sourceMapSupport.unit.test.ts +++ b/src/test/sourceMapSupport.unit.test.ts @@ -6,11 +6,8 @@ // tslint:disable:no-any no-unused-expression chai-vague-errors no-unnecessary-override max-func-body-length max-classes-per-file import { expect } from 'chai'; -import * as fs from 'fs'; import * as path from 'path'; import { ConfigurationTarget, Disposable } from 'vscode'; -import { FileSystem } from '../client/common/platform/fileSystem'; -import { PlatformService } from '../client/common/platform/platformService'; import { Diagnostics } from '../client/common/utils/localize'; import { EXTENSION_ROOT_DIR } from '../client/constants'; import { initialize, SourceMapSupport } from '../client/sourceMapSupport'; @@ -114,34 +111,4 @@ suite('Source Map Support', () => { } test('Rename extension and debugger source maps when enabling source maps', () => testRenamingFilesWhenEnablingDisablingSourceMaps(true)); test('Rename extension and debugger source maps when disabling source maps', () => testRenamingFilesWhenEnablingDisablingSourceMaps(false)); - test('When disabling source maps, the map file is renamed and vice versa', async () => { - const fileSystem = new FileSystem(new PlatformService()); - const jsFile = await fileSystem.createTemporaryFile('.js'); - disposables.push(jsFile); - const mapFile = `${jsFile.filePath}.map`; - disposables.push({ - dispose: () => fs.unlinkSync(mapFile) - }); - await fileSystem.writeFile(mapFile, 'ABC'); - expect(await fileSystem.fileExists(mapFile)).to.be.true; - - const stub = createVSCStub(true, true); - const instance = new class extends SourceMapSupport { - public async enableSourceMap(enable: boolean, sourceFile: string) { - return super.enableSourceMap(enable, sourceFile); - } - }(stub.vscode as any); - - await instance.enableSourceMap(false, jsFile.filePath); - - expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); - expect(await fileSystem.fileExists(mapFile)).to.be.equal(false, 'Source map file not renamed'); - expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(true, 'Expected renamed file not found'); - - await instance.enableSourceMap(true, jsFile.filePath); - - expect(await fileSystem.fileExists(jsFile.filePath)).to.be.equal(true, 'Source file does not exist'); - expect(await fileSystem.fileExists(mapFile)).to.be.equal(true, 'Source map file not found'); - expect(await fileSystem.fileExists(`${mapFile}.disabled`)).to.be.equal(false, 'Source map file not renamed'); - }); });