ã¯ããã«
å æ¥ç޹ä»ããèªäœã®Visual Studio Code(以äžVSCode)æ¡åŒµæ©èœã®éçºæã«åããŠTypeScriptãçšããæ¡åŒµæ©èœã®ãã¹ãç°å¢ãæ§ç¯ããã®ã§ãããVSCodeã®æ§æãããããããªããŸãŸã«åãçµã¿ãéåèŠåŽããã®ã§ãä»åã¯ããã解説ããŠã¿ãããšæããŸãã
ãã¹ãã®å®è¡æ¹æ³ã®çš®é¡
å
¬åŒã«ã¯ããã¹ãã®éçºæ¹æ³ã¯äžèšã«ããã¥ã¡ã³ãããããŸãã
Testing Extension | Visual Studio Code Extension API
VSCodeäžã§æ¡åŒµæ©èœããã¹ãããã«ã¯ãã³ãŒãã£ã³ã°ã«äœ¿çšããŠããVSCodeãšã¯å¥ã«VSCodeãç«ã¡äžãããã®VSCodeäžã§æ¡åŒµæ©èœãå©ããã¹ãããã°ã©ã ãåãããŸãã
ãã®å¥ã®VSCodeã®ããšããã€ã³ã¹ã¿ã³ã¹ããšåŒã³ãŸãã
ãã¹ãããã°ã©ã çšã®VSCodeã€ã³ã¹ã¿ã³ã¹ãç«ã¡äžããã«ã¯ãå®éã«ã³ãã³ãããã³ãããªã©ããVSCodeãç«ã¡äžããæ¹æ³ããããŸãããä»ã«ãvscode-testã¢ãžã¥ãŒã«ïŒä»¥äžvscode-testïŒãšããnpmããã±ãŒãžãèªã¿èŸŒã¿ãããããåããã»ã¹ã§VSCodeã€ã³ã¹ã¿ã³ã¹ãçæããã¹ããè¡ããšããæ¹æ³ããããä»åŸäž»æµã«ãªã£ãŠããããã§ãïŒäžèšã®ãªã¹ãã®ãã¡2-1ïŒã
ãã æ¬çš¿ã§ã¯ãåèã«ããæ¡åŒµæ©èœã«ãªãã£ãŠãvscode-testã䜿ããªãæ¹æ³ ã解説ããŸãã
vscode-testã䜿ããªãæ¹æ³- ã³ãã³ãããã³ããã§ã
code ïœãã®ããã«VSCodeã®å®è¡ãã¡ã€ã«ãæå®ããŠãVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããã
ãã£ãšããªãŒãœããã¯ã¹ãªæ¹æ³ã§ãã
ããã§ãã¡ãã¡ãªãã·ã§ã³ãæå®ããã®ãé¢åãªã®ã§ã次ã®1-2ã®æ¹æ³ããããŸãã launch.jsonã«ãã©ã¡ãŒã¿ãèšå ¥ããŠã䜿çšäžã®VSCodeã€ã³ã¹ã¿ã³ã¹ããGUIãæäœãããã¹ãçšã®VSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããã
1-1ã§ãã¡ãã¡ã³ãã³ãããã³ããã§æå®ããŠãããªãã·ã§ã³ããlaunch.jsonã«èšè¿°ããŠVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããããšãåºæ¥ãŸãã
ïŒæ¬çš¿ã§è§£èª¬ããŸãïŒ
- ã³ãã³ãããã³ããã§ã
vscode-testãäœ¿ãæ¹æ³- Nodeã®ããã°ã©ã ãšããŠTypeScriptãèšè¿°ãå®è¡ãããïŒäŸïŒ
runTest.tsïŒ
ãã®äžã§vscode-testã®runTests颿°ãåŒã³åºãã
ïŒããã§VSCodeã€ã³ã¹ã¿ã³ã¹ãåããã»ã¹ã§èµ·åããããšãã§ããŸãããã®runTest.tsã®ããšããTest ScriptããšåŒã³ãŸããïŒ
ã³ãã³ãããã³ããã§ãnode ./out/test/runTest.jsãã®ããã«ãNodeããã°ã©ã ãšããŠæå®ããããšã§èµ·åã§ããŸãã
ãã ããåŸè¿°ããŸããè²ã ãšå¶éããããŸããïŒåç §ïŒrunTest.jsã§ãã¹ããããšãã®æ³šæïŒ
å ¬åŒããã¥ã¡ã³ãã§ããã®æ¹æ³ãäž»ã«è§£èª¬ããŠããŸãããªã¢ãŒããããã®ã³ã°ãMicrosoftãéå¶ããAzureDevOpsäžã§CIãåããããã«ãªãã®ãçç±ã®ããã§ãïŒïŒ
Continuous Integration | Visual Studio Code Extension API
- Nodeã®ããã°ã©ã ãšããŠTypeScriptãèšè¿°ãå®è¡ãããïŒäŸïŒ
ãã¹ãç°å¢ã®æ§ç¯æ¹æ³
ç¹°ãè¿ãã«ãªããŸãããæ¬çš¿ã§ã¯ launch.jsonã«ãã©ã¡ãŒã¿ãèšå
¥ããŠã䜿çšäžã®VSCodeã€ã³ã¹ã¿ã³ã¹ããGUIãæäœãããã¹ãçšã®VSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åãã ãã¹ãæ¹æ³ã«ã€ããŠè§£èª¬ããŸãã
ãã¹ãããã°ã©ã ã¯TypeScriptã§èšè¿°ããŸãã
ç¹ã«JavaScriptã§ããããšãæèããªãéãã¯ãTypeScriptããã°ã©ã ããšç§°ããŠèª¬æããŸãã
å¿ èŠãªnpmããã±ãŒãž
ãã¹ããå®è¡ããã«ã¯ãéçºç°å¢ã®node_modulesãã©ã«ãå
ã«ãnpmããã±ãŒãžãã€ã³ã¹ããŒã«ããå¿
èŠããããŸãã
| npmããã±ãŒãž | æŠèŠ |
|---|---|
| mocha | ãã¹ããã¬ãŒã ã¯ãŒã¯ã®ããã±ãŒãž |
| istanbul | ã«ãã¬ããžæž¬å®ã«äœ¿çšããŸãã |
| remap-istanbul | ã«ãã¬ããžãšTypeScriptãã¡ã€ã«ãšã®ãããã³ã°ã«äœ¿çšããŸãã |
ä»ã«ããBDDçšã®ã¢ãµãŒãèšæ³ãæäŸããchaiã¢ãžã¥ãŒã«ãªã©ããããŸãããä»åã¯å²æããŸããïŒassertã¢ãžã¥ãŒã«ã䜿ããŸããïŒ
ã€ã³ã¹ããŒã«æ¹æ³
解説ã«äœ¿çšããç°å¢ã¯ãå
¬åŒã®ããã¥ã¡ã³ãã§ã解説ããŠãããyeomanã§çæãããã³ãã¬ãŒãã䜿çšããŸãã
Your First Extension | Visual Studio Code Extension API
äžèšãµã€ãã®æé ã«åŸã£ãŠãVSCodeã®æ¡åŒµæ©èœã®ãã³ãã¬ãŒããçæããŸãã
å
·äœçã«ã¯ãäžèšã®æé ã«ãªããŸãã
ã³ãã³ãããã³ããã§äžèšãå®è¡ã
yo
ãã®åŸã¯å¯Ÿè©±åœ¢åŒã§çæãããã³ãã¬ãŒãã®å 容ãèãããã®ã§é©åœã«å ¥åããŸãã
What would you like to do?âãcodeããéžæ
What type of extension do you want to create?âãNew Extension (TypeScript)ããéžæ
What's the name of your extension? âãsampleããå
¥å
What's the identifier of your extension? (sample) âãã®ãŸãŸEnter
What's the description of your extension?âãã®ãŸãŸEnter
Initialize a git repository? (Y/n)âãYããå
¥å
Which package manager to use?âãnpmããéžæ
çæçŽåŸã¯ãäžèšã®ãã¡ã€ã«æ§æã«ãªããŸãã

ãã³ãã¬ãŒãçæçŽåŸã§ã¯ã«ãã¬ããžæž¬å®ãªã©ãè¡ããªãããã以éã®æé ã§èª¿æŽããŠãããŸãã
調æŽåã®å
容ã¯ãæåŸã®ãèæ³šïŒãã³ãã¬ãŒãçæçŽåŸã®èª¿æŽåã®ãœãŒã¹äžèЧïŒãã«èšèŒããŠããŸãã
ãã®äœæ¥ã¯äžèšãµã€ããåèã«ããŸããã
VSCode extension code coverage · Roman Peshkov
extension.test.tsãã¡ã€ã«ã®èª¿æŽ
ïŒèª¿æŽåã®extension.test.tsã¯ãã¡ãïŒ
æ¡åŒµæ©èœã®æ¬äœã§ããextension.tsããã¹ããããã¹ãããã°ã©ã ã§ãã
äžèšã®ããã«èª¿æŽããŸãã
import * as assert from 'assert';
import * as mocha from 'mocha'; //â
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
import * as myExtension from '../../extension'; //â¡
describe('Extension Test Suite', () => { //â
before(() => {
vscode.window.showInformationMessage('Start all tests.');
});
it('Sample test', () => { //â
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});
â ãã³ãã¬ãŒããã®ãŸãŸã§ã¯ãmochaã®TDDèšæ³ãšBDDèšæ³ãæ··ãã£ãŠããŠç¡é§ã«è€éã«ãªã£ãŠããã®ã§ãBDDèšæ³ã«çµ±äžããŸãã
â¡extension.tsã®èªã¿èŸŒã¿ãã³ã¡ã³ãã¢ãŠããããŠããã®ã§ãæå¹åããŸãã
ãªãããã®ãã¹ãããã°ã©ã ã¯ãããŸã§ãµã³ãã«ã®ãããextension.tsã®åŠçãåŒã³åºããŠããŸããã
å®è¡ãããšããšããããassertãæåããããã¹ããïŒä»¶æåããŸããããšããã¡ãã»ãŒãžã衚瀺ãããŸãã
index.tsãã¡ã€ã«ã®èª¿æŽ
ïŒèª¿æŽåã®index.tsã¯ãã¡ãïŒ
GUIããèµ·åãããindex.tsã¯ãTest RunnerããšåŒã°ãããã¹ãããã°ã©ã ãåŒã³åºã圹å²ãæã£ãŠããŸãã
ãããäžèšã®ããã«æžãæããŸãã
'use strict';
declare var global: any;
/* tslint:disable no-require-imports */
import * as fs from 'fs';
import * as glob from 'glob';
import * as paths from 'path';
const istanbul = require('istanbul');
const Mocha = require('mocha');
const remapIstanbul = require('remap-istanbul');
// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY
// Since we are not running in a tty environment, we just implementt he method statically
const tty = require('tty');
if (!tty.getWindowSize) {
tty.getWindowSize = (): number[] => {
return [80, 75];
};
}
let mocha = new Mocha({
ui: 'bdd',
color: true, //â
});
function configure(mochaOpts: any): void {
mocha = new Mocha(mochaOpts);
}
exports.configure = configure;
function _mkDirIfExists(dir: string): void {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
}
function _readCoverOptions(testsRoot: string): ITestRunnerOptions | undefined {
const coverConfigPath = paths.join(testsRoot, '..', '..', '..', 'coverconfig.json'); //â¡
if (fs.existsSync(coverConfigPath)) {
const configContent = fs.readFileSync(coverConfigPath, 'utf-8');
return JSON.parse(configContent);
}
return undefined;
}
function run(testsRoot: string, clb: any): any {
// Read configuration for the coverage file
const coverOptions = _readCoverOptions(testsRoot);
if (coverOptions && coverOptions.enabled) {
// Setup coverage pre-test, including post-test hook to report
const coverageRunner = new CoverageRunner(coverOptions, testsRoot);
coverageRunner.setupCoverage();
}
// Glob test files
glob('**/**.test.js', { cwd: testsRoot }, (error, files): any => {
if (error) {
return clb(error);
}
try {
// Fill into Mocha
files.forEach((f): Mocha => mocha.addFile(paths.join(testsRoot, f)));
// Run the tests
let failureCount = 0;
mocha.run()
.on('fail', () => failureCount++)
.on('end', () => clb(undefined, failureCount)
);
} catch (error) {
return clb(error);
}
});
}
exports.run = run;
interface ITestRunnerOptions {
enabled?: boolean;
relativeCoverageDir: string;
relativeSourcePath: string;
ignorePatterns: string[];
includePid?: boolean;
reports?: string[];
verbose?: boolean;
}
class CoverageRunner {
private coverageVar: string = '$$cov_' + new Date().getTime() + '$$';
private transformer: any = undefined;
private matchFn: any = undefined;
private instrumenter: any = undefined;
constructor(private options: ITestRunnerOptions, private testsRoot: string) {
if (!options.relativeSourcePath) {
return;
}
}
public setupCoverage(): void {
// Set up Code Coverage, hooking require so that instrumented code is returned
const self = this;
self.instrumenter = new istanbul.Instrumenter({ coverageVariable: self.coverageVar });
const sourceRoot = paths.join(self.testsRoot, self.options.relativeSourcePath);
// Glob source files
const srcFiles = glob.sync('**/**.js', {
cwd: sourceRoot,
ignore: self.options.ignorePatterns,
});
// Create a match function - taken from the run-with-cover.js in istanbul.
const decache = require('decache');
const fileMap: any = {};
srcFiles.forEach((file) => {
const fullPath = paths.join(sourceRoot, file);
fileMap[fullPath] = true;
// On Windows, extension is loaded pre-test hooks and this mean we lose
// our chance to hook the Require call. In order to instrument the code
// we have to decache the JS file so on next load it gets instrumented.
// This doesn't impact tests, but is a concern if we had some integration
// tests that relied on VSCode accessing our module since there could be
// some shared global state that we lose.
decache(fullPath);
});
self.matchFn = (file: string): boolean => fileMap[file];
self.matchFn.files = Object.keys(fileMap);
// Hook up to the Require function so that when this is called, if any of our source files
// are required, the instrumented version is pulled in instead. These instrumented versions
// write to a global coverage variable with hit counts whenever they are accessed
self.transformer = self.instrumenter.instrumentSync.bind(self.instrumenter);
const hookOpts = { verbose: false, extensions: ['.js'] };
istanbul.hook.hookRequire(self.matchFn, self.transformer, hookOpts);
// initialize the global variable to stop mocha from complaining about leaks
global[self.coverageVar] = {};
// Hook the process exit event to handle reporting
// Only report coverage if the process is exiting successfully
process.on('exit', (code: number) => {
self.reportCoverage();
process.exitCode = code;
});
}
/**
* Writes a coverage report.
* Note that as this is called in the process exit callback, all calls must be synchronous.
*
* @returns {void}
*
* @memberOf CoverageRunner
*/
public reportCoverage(): void {
const self = this;
istanbul.hook.unhookRequire();
let cov: any;
if (typeof global[self.coverageVar] === 'undefined' || Object.keys(global[self.coverageVar]).length === 0) {
console.error('No coverage information was collected, exit without writing coverage information');
return;
} else {
cov = global[self.coverageVar];
}
// TODO consider putting this under a conditional flag
// Files that are not touched by code ran by the test runner is manually instrumented, to
// illustrate the missing coverage.
self.matchFn.files.forEach((file: any) => {
if (cov[file]) {
return;
}
self.transformer(fs.readFileSync(file, 'utf-8'), file);
// When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in coverState.s,
// presumably to compensate for function hoisting. We need to reset this, as the function was not hoisted,
// as it was never loaded.
Object.keys(self.instrumenter.coverState.s).forEach((key) => {
self.instrumenter.coverState.s[key] = 0;
});
cov[file] = self.instrumenter.coverState;
});
// TODO Allow config of reporting directory with
const reportingDir = paths.join(self.testsRoot, self.options.relativeCoverageDir);
const includePid = self.options.includePid;
const pidExt = includePid ? ('-' + process.pid) : '';
const coverageFile = paths.resolve(reportingDir, 'coverage' + pidExt + '.json');
// yes, do this again since some test runners could clean the dir initially created
_mkDirIfExists(reportingDir);
fs.writeFileSync(coverageFile, JSON.stringify(cov), 'utf8');
const remappedCollector = remapIstanbul.remap(cov, {
warn: (warning: any) => {
// We expect some warnings as any JS file without a typescript mapping will cause this.
// By default, we'll skip printing these to the console as it clutters it up
if (self.options.verbose) {
console.warn(warning);
}
}
});
const reporter = new istanbul.Reporter(undefined, reportingDir);
const reportTypes = (self.options.reports instanceof Array) ? self.options.reports : ['lcov'];
reporter.addAll(reportTypes);
reporter.write(remappedCollector, true, () => {
console.log(`reports written to ${reportingDir}`);
});
}
}
å
ã®index.tsã¯é
äžã®åãã¹ãããã°ã©ã ãåŒã³åºãã ãã§ããããäžèšã®æ§ã«æžãæããããšã§ãistanbulãå©çšãã«ãã¬ããžèšæž¬ãã§ããããã«ãªããŸãã
åŒçšå
ã®ãµã€ãã«èšèŒã®ãã®ã¯ããã§ã«éæšå¥šã«ãªã£ãã³ãŒããå«ãŸããŠãããããäžéšæãå ããŠããŸããïŒâ ã®ç®æïŒ
41è¡ç®ã®â¡ã¯ããã®index.tsã䜿çšããèšå®ãã¡ã€ã«ïŒcoverconfig.jsonïŒãžã®ãã¹ã§ãã
ãã©ã³ã¹ãã€ã«ã§åºåãããindex.jsãèµ·ç¹ãšããçžå¯Ÿãã¹ãæå®ããŸãã
ãšãããããã®ãŸãŸã³ããããã°åãã¯ãã§ãã
coverconfig.jsonãã¡ã€ã«ã®äœæ
index.tsã§èªã¿èŸŒãèšå®ãã¡ã€ã«ã§ãã
éçºç°å¢ã®äœæ¥ãã©ã«ãã®çŽäžã«æ°èŠäœæããŸãã
{
"enabled": true,
"relativeSourcePath": "../..",
"relativeCoverageDir": "../../../coverage",
"ignorePatterns": [
"**/node_modules/**"
],
"includePid": false,
"reports": [
"html",
"lcov",
"text-summary"
],
"verbose": false
}
ã«ãã¬ããžæž¬å®çµæã®åºåå
ãªã©ãå®çŸ©ããŠããŸãã
ãã¡ããçžå¯Ÿãã¹ã§æå®ããå¿
èŠããããããåããã«ããã®ã§æ³šæããŠãã ããã
| åç§° | æŠèŠ |
|---|---|
relativeSourcePath | ãã¹ããããjsãã¡ã€ã«ãæ ŒçŽãããŠãããã©ã«ããæå®ããŸãã ãã©ã³ã¹ãã€ã«åŸã® index.jsã®ããå Žæãèµ·ç¹ã«ããŠãçžå¯Ÿãã¹ã§æå®ããŸãããã³ãã¬ãŒãã®ãã©ã«ãæ§æã ãšããã¹ãããã°ã©ã ãäžç·ã«ã«ãã¬ããžæž¬å®ããŠããŸããŸããããšããããã¯åãã®ã§ç®ãã€ã¶ããŸãã |
relativeCoverageDir | ã«ãã¬ããžæž¬å®çµæã®HTMLãã¡ã€ã«ã®åºåå
ãæå®ããŸãã ãã©ã³ã¹ãã€ã«åŸã® index.jsã®ããå Žæãèµ·ç¹ã«ããŠãçžå¯Ÿãã¹ã§æå®ããŸãããšãããããã®ãŸãŸã³ããããã°åãã¯ãã§ãã |
launch.jsonãã¡ã€ã«ã®èª¿æŽ
ïŒèª¿æŽåã®launch.jsonã¯ãã¡ãïŒlaunch.jsonãäžèšã®ããã«æžãæããŸãã
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: watch"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}
"name": "Run Extension"èŠçŽ ã®ãããã¯ã¯ä»å䜿ããŸããã"name": "Extension Tests"èŠçŽ ã®ãããã¯ã®--extensionTestsPathãã以äžã®ããã«æžãæããŠããŸãã
ãã®ãªãã·ã§ã³ã«ã¯ããã©ã³ã¹ãã€ã«åŸã®index.jsãæ ŒçŽããããã©ã«ããžã®ãã¹ãæå®ããŸããããã³ãã¬ãŒãçæçŽåŸã®ç¶æ
ã§ã¯æåŸã«indexãã€ããŠãããã©ã®ãã¿ãŒã³ã§ãããŸãVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããŸããã
ïŒä»ã®è§£èª¬ã§ããå
šéšãã®ãããªãã¹æå®ã«ãªã£ãŠãããã§ããã©ãã»ã»ã»ïŒ
--extensionTestsPath=${workspaceFolder}/out/test/suite/index
ãâ--extensionTestsPath=${workspaceFolder}/out/test/suite
package.jsonãã¡ã€ã«ã®èª¿æŽ
ïŒèª¿æŽåã®package.jsonã¯ãã¡ãïŒpackage.jsonãäžèšã®ããã«æžãæããŸãã
{
"name": "sample-ex-org",
"displayName": "sample_ex_org",
"description": "",
"version": "0.0.1",
"engines": {
"vscode": "^1.40.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:extension.helloWorld"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "extension.helloWorld",
"title": "Hello World"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/mocha": "^5.2.6",
"@types/node": "^10.12.21",
"decache": "^4.5.1",
"glob": "^7.1.6",
"istanbul": "^0.4.5",
"mocha": "^6.1.4",
"remap-istanbul": "^0.13.0",
"tslint": "^5.12.1",
"typescript": "^3.3.1",
"vscode": "^1.1.34"
}
}
vscode-testãçšããrunTest.tsããèµ·åãããã¿ãŒã³ã®ãã¹ãæ§æã«ãªã£ãŠãããããscriptsèŠçŽ ãã"test": "node ./out/test/runTest.js"ãåé€ããŸãã
ãŸããdevDependenciesèŠçŽ ã«å¿
èŠãªnpmããã±ãŒãžã远å ã»åé€ããŸããvscode-testã¢ãžã¥ãŒã«ã䜿çšããªãã®ã§åé€ãã代ããã«vscodeã¢ãžã¥ãŒã«ã远å ããŸãã
ãŸãããã«äŒŽãã@types/vscodeãåé€ããŸãã
npmããã±ãŒãžã®åã€ã³ã¹ããŒã«
package.jsonãç·šéããã®ã§ããã£ããnpm_modulesãã©ã«ããåé€ããŠãånpmããã±ãŒãžãã€ã³ã¹ããŒã«ãçŽããŸãã
npm install
runTest.tsãã¡ã€ã«ã®åé€
ä»åã¯äœ¿çšããªããããèŠåºãã®éãrunTest.tsãã¡ã€ã«ãåé€ããŸãã
TypeScriptã®ãã©ã³ã¹ãã€ã«
äžèšãå
šéšå®äºããããTypeScriptã®ãã©ã³ã¹ãã€ã«ãå®è¡ããŸãã
ãã§ã«outãã©ã«ããçæãããŠãããšãã¯ã念ã®çºãäžæŠåé€ããŠãã ããã
tsc -p ./
outãã©ã«ãã«å¿
èŠãªJavaScriptãã¡ã€ã«ãåºåãããŸãã
調æŽäœæ¥åŸã®ç¢ºèª
ããã§ãã¹ããå®è¡ããæºåãæŽããŸããã
æçµçã«ã¯äžèšã®ãããªæ§æã«ãªããŸãã

è¡ã£ãäœæ¥ããŸãšãããšã
launch.jsonãindex.tsãpackage.jsonãextension.test.tsãç·šécoverconfig.jsonãæ°èŠäœærunTest.tsãåé€node_modulesãã©ã«ããåé€ânpm installã§åçæ- TypeScriptã®ãã©ã³ã¹ãã€ã«ãå®è¡ãã
outãã©ã«ããçæ
ãšãªããŸããcoverageãã©ã«ãã¯ãã®åŸã®ãã¹ãå®è¡ã§çæãããŸãã
ãã¹ãã®å®è¡
ãããã°ãµã€ãããŒã®ããããã°éå§ããã¿ã³ã䜿çšããŸãã

ãã¹ããå®è¡ããã

ã«ãã¬ããžæž¬å®çµæãcoverageãã©ã«ãã«HTMLã§åºåãããŸãã

ããã§ã«ãã¬ããžæž¬å®ãå¯èœãªãã¹ãç°å¢ãæ§ç¯ã§ããŸããã
ããšã¯ãã¹ããextension.test.tsã«æžããŠãããŸãã
è£è¶³çãªè©±
runTest.tsãšã¯ïŒ
å
¬åŒããã¥ã¡ã³ãã§ã¯ããTest ScriptããšåŒã°ããŠããŸãã
æ¬çš¿ã§ã¯æ¬ãã¡ã€ã«ã¯åé€ããŸãããæè¿ã®VSCodeæ¡åŒµæ©èœã®ãã¹ãã¯runTest.tsã§vscode-testãçšããŠVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããŠãã¹ããå®è¡ããŸãã
ä»çµã¿ãšããŠã¯ãNodeã®child_process.spawnSync()颿°ã§VSCodeã®å®è¡ãã¡ã€ã«ã«èµ·åããããrunTest.tsã®åããã»ã¹ãšããŠVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããŸãã
ãã®ãšãããã«ãã³ãŒãå
ã§vscode-testã«çšæãããæ©èœã䜿ããšãä»»æã®ããŒãžã§ã³ã®VSCodeãããŠã³ããŒãããŠãããã®å®è¡ãã¡ã€ã«(Code.exe)ã«å¯ŸããŠèµ·åããããããšãåºæ¥ãŸãã
ããã«ãããããŒãžã§ã³ãæå®ããVSCodeäžã§ãã¹ããè¡ãããšãã§ããã¹ãç°å¢ãæè»ã«æ§ç¯ããããšãåºæ¥ãããã«ãªããŸãã
ãŸãããã«ãVSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããåã«ãä»ã®æ¡åŒµæ©èœã®ã€ã³ã¹ããŒã«ãæå®ããããšãã§ãããããç¹å®ã®æ¡åŒµæ©èœãã€ã³ã¹ããŒã«ãããVSCodeã€ã³ã¹ã¿ã³ã¹ãçæããããšãåºæ¥ãŸãã
ïŒã¡ãªã¿ã«ãããŠã³ããŒãããŠããVSCodeã¯ãéçºç°å¢ã®.vscode-testãã©ã«ãã«ä¿åãããŸãïŒ
launch.jsonãCLIã§åçŽã«èµ·åãã©ã¡ãŒã¿ãæå®ããŠå®è¡ããããããããæè»ã§çµ±åçãªãã¹ããè¡ããŸãã
runTest.tsã§ãã¹ããããšãã®æ³šæ
ãã®ããã«äŸ¿å©ãªvscode-testãçšãããã¹ãã§ãããå®ã¯å¶éããããåäžPCäžã§VSCodeã€ã³ã¹ã¿ã³ã¹ãèµ·åããããšãåºæ¥ãŸããã
æ¬æ«è»¢åã®ããã§ãããæ®éã«ãã¿ã¹ã¯ã®å®è¡ãããããã°ãµã€ãããŒããrunTest.tsãå®è¡ããããšãããšããRunning extension tests from the command line is currently only supported if no other instance of Code is running.ããšãšã©ãŒã衚瀺ãããèµ·åããããšãåºæ¥ãŸããã
ãšã©ãŒã¡ãã»ãŒãžã®éããCLIããã¯ç¢ºãã«èµ·åã§ããã®ã§ãããããã§ãä»ã«VSCodeãç«ã¡äžãã£ãŠãããšåããšã©ãŒã§èµ·åããããšãåºæ¥ãŸããã
ãã®ããããã¹ãå®è¡ããããã³ã«æ¯åãä»éçºã«äœ¿çšããŠããVSCodeãçµäºãããå¿
èŠããããŸãã
ããã解決ããæ¹æ³ãããã«ã¯ãããŸãã
VSCodeã«ã¯ãæšæºã®VSCode以å€ã«InsiderããŒãžã§ã³ãšãããã®ããããŸãã
ãã®ããŒãžã§ã³ããrunTest.tsã«ãã¿ã¹ã¯ã®å®è¡ãããããã°ãµã€ãããŒããèµ·åãããããšããã¹ããå®è¡ããããšãã§ããŸãã
runTest.tså
ã§ããŠã³ããŒãããVSCodeã¯æšæºã®VSCodeã®ãããInsiderããŒãžã§ã³ãšã¯éãã¢ããªãšèªèãããèµ·åãã§ããããã§ãã
æ¡åŒµæ©èœã®éçºã®ããã«ããããInsiderããŒãžã§ã³ãã€ã³ã¹ããŒã«ããŠäœ¿çšããªããšãããªããªããŠãããŸãã«æ¬æ«è»¢åãããŠã«ããã«ä¿¡ããããªãã®ã§ãããæ¬åœã«ãã®çè§£ã§ãã£ãŠããã®ã誰ãæããŠããã ãããšå¬ããã§ãã
ãããŸã§CIäžã§åããããšãæ³å®ããŠãããæ ã®å¶éãªãã§ãããã»ã»ã»ã
ãã¹ãèµ·åæã®TypeScriptã®ãã©ã³ã¹ãã€ã«
yeomanãããã³ãã¬ãŒãç°å¢ãçææã¯ããã¹ãå®è¡åã«èªåã§ãtsc -watch -p ./ããå®è¡ãããŸããlaunch.jsonå
ã®"preLaunchTask": "npm: watch"ã§package.jsonäžã®ã"watch": "tsc -watch -p ./"ããèµ·åãããããã§ãã
ãã ãããããã€ã³ã¯ãªã¡ã³ã¿ã«ã³ã³ãã€ã«ããŠããŠãããŸããã¹ããåããªããšããããã®ã§ããã¹ãèµ·ååã«ã¯æ®éã«ãtsc -p ./ããå®è¡ããã»ããç¡é£ã§ãã
ïŒããã«ãã¡ãã£ã¯ãã£ããããŸããâŠïŒ
ãã¹ãã«ãã¬ãŒã¯ãã€ã³ãã匵ããã
æ¬çš¿ã®æ¹æ³ã ãšããã¹ãããã°ã©ã (extension.test.tsã®æ¹)ã«ã¯ããã¬ãŒã¯ãã€ã³ããå¹ããŠãããŸããã
ããã§ãäžèšã®ããã«debuggerãä»èŸŒãããšã§ããã¹ãããã°ã©ã ãéäžã§æ¢ããŠã¹ãããå®è¡ããããšãåºæ¥ãããã«ãªããŸãã

ãåºå
žã
mocha ã§ debugger ãæ¢ãã
æ¬æ¥ã¯ãpackage.jsonã«ãªãã·ã§ã³ãæå®ããããããããªã®ã§ãããç§ã詊ãããšãã¯ããŸããããªãã£ãã®ã§ããã®ããæ¹ã®ã»ãã確å®ã§ãã
åŒçšå ã»åèäžèЧ
istanbulãçšããå
·äœçãªãã¹ãæ§æã®æ§ç¯æ¹æ³
VSCode extension code coverage · Roman Peshkov
ïŒå
¬åŒããã¥ã¡ã³ãïŒVSCodeã®æ¡åŒµæ©èœçšãã¹ãæ¹æ³ã®èª¬æ
Testing Extension | Visual Studio Code Extension API
ïŒå
¬åŒããã¥ã¡ã³ãïŒVSCodeã®ãããã°æ¹æ³ãæ¡åŒµæ©èœã®éçºã«ã¯ç¹åããŠããŸãããããªã¢ãŒããããã®ã³ã°ã®è©±ãšããèŒã£ãŠããŸãã
Debugging in Visual Studio Code
ïŒå
¬åŒããã¥ã¡ã³ãïŒlaunch.jsonã®åãã©ã¡ãŒã¿ã«æå®ãã倿°äžèЧããã¹æå®ã§è¿·åã«ãªã£ããšãã«âŠã
Visual Studio Code Variables Reference
runTest.tsã£ãŠäœè
ïŒãšããStack Overflowäžã®è³ªåã§ããrunTest.ts(Test Script)ãšindex.ts(Test Runner)ã®åœ¹å²ã®éãããå
¬åŒããã¥ã¡ã³ãã ãã§ã¯ããããããªããããstackoverflowã«ã質åãäžãã£ãŠããŸããã
javascript – runTest.ts class in vscode-test setup gets never used even in example project, what is it’s use? – Stack Overflow
vscode-testãVSCodeã€ã³ã¹ã¿ã³ã¹ãåããã»ã¹ã§çæããããã»ã¹ã«ã€ããŠåè
node ã® spawn ã«é¢ããŠèª¿ã¹ãŠã¿ã
node ã® spawn ã«é¢ããŠèª¿ã¹ãŠã¿ã ãã®ïŒ
èæ³šïŒãã³ãã¬ãŒãçæçŽåŸã®èª¿æŽåã®ãœãŒã¹äžèЧïŒ
ãã³ãã¬ãŒãçæçŽåŸã®extension.test.ts
調æŽåã®ãã³ãã¬ãŒãçæçŽåŸã®extension.test.tsã§ãã
import * as assert from 'assert';
import { before } from 'mocha';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../extension';
suite('Extension Test Suite', () => {
before(() => {
vscode.window.showInformationMessage('Start all tests.');
});
test('Sample test', () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});
調æŽåŸã®extension.test.tsãžæ»ãã
ãã³ãã¬ãŒãçæçŽåŸã®index.ts
調æŽåã®ãã³ãã¬ãŒãçæçŽåŸã®index.tsã§ãã
import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd',
});
mocha.useColors(true);
const testsRoot = path.resolve(__dirname, '..');
return new Promise((c, e) => {
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
if (err) {
return e(err);
}
// Add files to the test suite
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
e(err);
}
});
});
}
調æŽåŸã®index.tsãžæ»ãã
ãã³ãã¬ãŒãçæçŽåŸã®launch.json
調æŽåã®ãã³ãã¬ãŒãçæçŽåŸã®launch.jsonã§ãã
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: watch"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}
調æŽåŸã®launch.jsonãžæ»ãã
ãã³ãã¬ãŒãçæçŽåŸã®package.json
調æŽåã®ãã³ãã¬ãŒãçæçŽåŸã®package.jsonã§ãã
{
"name": "sample-ex-org",
"displayName": "sample_ex_org",
"description": "",
"version": "0.0.1",
"engines": {
"vscode": "^1.40.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:extension.helloWorld"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "extension.helloWorld",
"title": "Hello World"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/mocha": "^5.2.6",
"@types/node": "^10.12.21",
"@types/vscode": "^1.40.0",
"glob": "^7.1.4",
"mocha": "^6.1.4",
"typescript": "^3.3.1",
"tslint": "^5.12.1",
"vscode-test": "^1.0.2"
}
}
æ¬èšäºã¯ãããŒã ã¹ããªãã Advent Calendar 2019ã«æçš¿ããèšäºãå çã»åç·šéãããã®ã§ãã
TeamSpirit Advent Calendar Day13ïŒVisual Studio Codeæ¡åŒµæ©èœã®ãã¹ãç°å¢ãæ§ç¯ãã



