Add simple e2e integration tests using vscode-debugadapter-testsupport
This commit is contained in:
Родитель
f900b374f1
Коммит
5be6ff9532
|
@ -2,7 +2,7 @@
|
|||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"debugServer": 4712,
|
||||
// "debugServer": 4712,
|
||||
"name": "launch as server",
|
||||
"type": "node2",
|
||||
"request": "launch",
|
||||
|
@ -12,9 +12,10 @@
|
|||
"stopOnEntry": false,
|
||||
"args": [ "--server=4712" ],
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/out"
|
||||
"outFiles": ["${workspaceRoot}/out/**/*.js"]
|
||||
},
|
||||
{
|
||||
// "debugServer": 4712,
|
||||
"name": "test",
|
||||
"type": "node2",
|
||||
"request": "launch",
|
||||
|
@ -23,11 +24,10 @@
|
|||
"args": [
|
||||
"-u", "tdd",
|
||||
"--colors",
|
||||
"out/test/**.test.js"
|
||||
"out/test/**/*.test.js"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/out"
|
||||
"outFiles": ["${workspaceRoot}/out/**/*.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"files.exclude": {
|
||||
".git": true,
|
||||
"bin": true,
|
||||
"node_modules": false
|
||||
"node_modules": false,
|
||||
"testapp/.vscode/chrome": true
|
||||
},
|
||||
"search.exclude": {
|
||||
".git": true,
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
"args": ["run", "watch"],
|
||||
"isBuildCommand": true,
|
||||
"isWatching": true
|
||||
},
|
||||
{
|
||||
"taskName": "watch:test",
|
||||
"args": ["run", "watch:test"],
|
||||
"isWatching": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
lib/**
|
||||
typings/**
|
||||
testapp/**
|
||||
testdata/**
|
||||
test/**
|
||||
*.vsix
|
||||
gulpfile.js
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"tslint": "^3.15.1",
|
||||
"typemoq": "^0.3.3",
|
||||
"typescript": "^2.0.3",
|
||||
"vscode-debugadapter-testsupport": "^1.15.0",
|
||||
"webpack": "^2.2.0-rc.1",
|
||||
"webpack-fail-plugin": "^1.0.5"
|
||||
},
|
||||
|
@ -47,7 +48,7 @@
|
|||
"build:test": "tsc -p test/tsconfig.json",
|
||||
"watch": "webpack -w",
|
||||
"watch:test": "tsc -p test/tsconfig.json -w",
|
||||
"test": "mocha -u tdd -c 'out/test/**/*.test.js'",
|
||||
"test": "mocha --timeout 20000 -s 2000 -u tdd --colors --reporter out/test/int/loggingReporter.js ./out/test/**/*.test.js",
|
||||
"lint": "tslint -t verbose 'src/**/*.ts' 'test/**/*.ts'",
|
||||
"vscode:prepublish": "gulp verify-no-linked-modules"
|
||||
},
|
||||
|
|
|
@ -11,7 +11,8 @@ const EXTENSION_NAME = 'debugger-for-chrome';
|
|||
const targetFilter = target => target && (!target.type || target.type === 'page');
|
||||
|
||||
// Injected by webpack
|
||||
declare const VERSION: string;
|
||||
declare let VERSION: string;
|
||||
let versionWithDefault = typeof VERSION === 'undefined' ? 'unspecified' : VERSION; // Not built with webpack for tests
|
||||
|
||||
// non-.txt file types can't be uploaded to github
|
||||
// also note that __dirname here is ...out/
|
||||
|
@ -30,4 +31,4 @@ ChromeDebugSession.run(ChromeDebugSession.getSession(
|
|||
sourceMapTransformer: BaseSourceMapTransformer,
|
||||
}));
|
||||
|
||||
logger.log(EXTENSION_NAME + ': ' + VERSION);
|
||||
logger.log(EXTENSION_NAME + ': ' + versionWithDefault);
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
|
||||
import {DebugClient} from 'vscode-debugadapter-testsupport';
|
||||
import {DebugProtocol} from 'vscode-debugprotocol';
|
||||
|
||||
import * as testUtils from './testUtils';
|
||||
import * as testSetup from './testSetup';
|
||||
|
||||
const DATA_ROOT = testSetup.DATA_ROOT;
|
||||
const THREAD_ID = testUtils.THREAD_ID;
|
||||
|
||||
suite('Chrome Debug Adapter etc', () => {
|
||||
let dc: DebugClient;
|
||||
setup(() => {
|
||||
return testSetup.setup()
|
||||
.then(_dc => dc = _dc);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
return testSetup.teardown();
|
||||
});
|
||||
|
||||
suite('basic', () => {
|
||||
test('unknown request should produce error', done => {
|
||||
dc.send('illegal_request').then(() => {
|
||||
done(new Error('does not report error on unknown request'));
|
||||
}).catch(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('initialize', () => {
|
||||
test('should return supported features', () => {
|
||||
return dc.initializeRequest().then(response => {
|
||||
assert.equal(response.body.supportsConfigurationDoneRequest, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('launch', () => {
|
||||
test('should stop on debugger statement', () => {
|
||||
const testProjectRoot = `${path.join(DATA_ROOT, 'intervalDebugger')}`;
|
||||
const launchFile = path.join(testProjectRoot, 'index.html');
|
||||
const breakFile = path.join(testProjectRoot, 'app.js');
|
||||
const DEBUGGER_LINE = 2;
|
||||
|
||||
return Promise.all([
|
||||
dc.configurationSequence(),
|
||||
dc.launch({ file: launchFile }),
|
||||
dc.assertStoppedLocation('debugger statement', { path: breakFile, line: DEBUGGER_LINE } )
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as mocha from 'mocha';
|
||||
import * as events from 'events';
|
||||
|
||||
class LoggingReporter extends mocha.reporters.Spec {
|
||||
static logEE = new events.EventEmitter();
|
||||
|
||||
private testLogs: string[];
|
||||
private inTest = false;
|
||||
|
||||
constructor(runner: any) {
|
||||
super(runner);
|
||||
|
||||
LoggingReporter.logEE.on('log', msg => {
|
||||
if (this.inTest) {
|
||||
this.testLogs.push(msg);
|
||||
}
|
||||
});
|
||||
|
||||
runner.on('test', test => {
|
||||
this.inTest = true;
|
||||
this.testLogs = [];
|
||||
});
|
||||
|
||||
runner.on('pass', test => {
|
||||
this.inTest = false;
|
||||
});
|
||||
|
||||
runner.on('fail', test => {
|
||||
this.inTest = false;
|
||||
this.testLogs.forEach(msg => {
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
console.log(new Date().toISOString().split(/[TZ]/)[1] + ' Finished');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export = LoggingReporter;
|
|
@ -0,0 +1,103 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import {DebugProtocol} from 'vscode-debugprotocol';
|
||||
import {DebugClient} from 'vscode-debugadapter-testsupport';
|
||||
|
||||
// ES6 default export...
|
||||
const LoggingReporter = require('./loggingReporter');
|
||||
|
||||
const DEBUG_ADAPTER = './out/src/chromeDebug.js';
|
||||
|
||||
let dc: DebugClient;
|
||||
|
||||
let unhandledAdapterErrors: string[];
|
||||
const origTest = test;
|
||||
const checkLogTest = (expectation: string, assertion?: ActionFunction, testFn: Function = origTest): Mocha.ITest => {
|
||||
// Hack to always check logs after a test runs, can simplify after this issue:
|
||||
// https://github.com/mochajs/mocha/issues/1635
|
||||
if (!assertion) {
|
||||
return origTest(expectation, assertion);
|
||||
}
|
||||
|
||||
function runTest(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const maybeP = assertion(resolve);
|
||||
if (maybeP && maybeP.then) {
|
||||
maybeP.then(resolve, reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return testFn(expectation, done => {
|
||||
runTest()
|
||||
.then(() => {
|
||||
// If any unhandled errors were logged, then ensure the test fails
|
||||
if (unhandledAdapterErrors.length) {
|
||||
const errStr = unhandledAdapterErrors.length === 1 ? unhandledAdapterErrors[0] :
|
||||
JSON.stringify(unhandledAdapterErrors);
|
||||
throw new Error(errStr);
|
||||
}
|
||||
})
|
||||
.then(done, done)
|
||||
});
|
||||
};
|
||||
(<Mocha.ITestDefinition>checkLogTest).only = (expectation, assertion) => checkLogTest(expectation, assertion, origTest.only);
|
||||
(<Mocha.ITestDefinition>checkLogTest).skip = test.skip;
|
||||
test = (<any>checkLogTest);
|
||||
|
||||
function log(e: DebugProtocol.OutputEvent) {
|
||||
// Skip telemetry events
|
||||
if (e.body.category === 'telemetry') return;
|
||||
|
||||
const timestamp = new Date().toISOString().split(/[TZ]/)[1];
|
||||
const outputBody = e.body.output ? e.body.output.trim() : 'variablesReference: ' + e.body.variablesReference;
|
||||
const msg = ` ${timestamp} ${outputBody}`;
|
||||
LoggingReporter.logEE.emit('log', msg);
|
||||
|
||||
if (msg.indexOf('********') >= 0) unhandledAdapterErrors.push(msg);
|
||||
};
|
||||
|
||||
function patchLaunchArgFns(): void {
|
||||
function patchLaunchArgs(launchArgs) {
|
||||
launchArgs.verboseDiagnosticLogging = true;
|
||||
launchArgs.userDataDir = `/tmp/chrome-${Math.random()}/`;
|
||||
}
|
||||
|
||||
const origLaunch = dc.launch;
|
||||
dc.launch = (launchArgs: any) => {
|
||||
patchLaunchArgs(launchArgs);
|
||||
return origLaunch.call(dc, launchArgs);
|
||||
};
|
||||
|
||||
const origHitBreakpoint = dc.hitBreakpoint;
|
||||
dc.hitBreakpoint = (...args) => {
|
||||
const launchArgs = args[0];
|
||||
patchLaunchArgs(launchArgs);
|
||||
return origHitBreakpoint.apply(dc, args);
|
||||
};
|
||||
}
|
||||
|
||||
export const lowercaseDriveLetterDirname = __dirname.charAt(0).toLowerCase() + __dirname.substr(1);
|
||||
export const PROJECT_ROOT = path.join(lowercaseDriveLetterDirname, '../../../');
|
||||
export const DATA_ROOT = path.join(PROJECT_ROOT, 'testdata/');
|
||||
|
||||
export function setup(port?: number) {
|
||||
unhandledAdapterErrors = [];
|
||||
dc = new DebugClient('node', DEBUG_ADAPTER, 'chrome');
|
||||
patchLaunchArgFns();
|
||||
dc.addListener('output', log);
|
||||
|
||||
return dc.start(port)
|
||||
.then(() => dc);
|
||||
}
|
||||
|
||||
export function teardown() {
|
||||
dc.removeListener('output', log);
|
||||
return dc.stop();
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
|
||||
import {DebugClient} from 'vscode-debugadapter-testsupport';
|
||||
import {DebugProtocol} from 'vscode-debugprotocol';
|
||||
|
||||
export const THREAD_ID = 1;
|
||||
|
||||
export function waitForEvent(dc: DebugClient, eventType: string): Promise<DebugProtocol.Event> {
|
||||
return dc.waitForEvent(eventType, 2e3);
|
||||
}
|
||||
|
||||
export function setBreakpointOnStart(dc: DebugClient, bps: DebugProtocol.SourceBreakpoint[], program: string, expLine?: number, expCol?: number, expVerified = true): Promise<void> {
|
||||
return waitForEvent(dc, 'initialized')
|
||||
.then(event => setBreakpoint(dc, bps, program, expLine, expCol, expVerified))
|
||||
.then(() => dc.configurationDoneRequest())
|
||||
.then(() => { });
|
||||
}
|
||||
|
||||
export function setBreakpoint(dc: DebugClient, bps: DebugProtocol.SourceBreakpoint[], program: string, expLine?: number, expCol?: number, expVerified = true): Promise<void> {
|
||||
return dc.setBreakpointsRequest({
|
||||
breakpoints: bps,
|
||||
source: { path: program }
|
||||
}).then(response => {
|
||||
const bp = response.body.breakpoints[0];
|
||||
|
||||
if (typeof expVerified === 'boolean') assert.equal(bp.verified, expVerified, 'breakpoint verification mismatch: verified');
|
||||
if (typeof expLine === 'number') assert.equal(bp.line, expLine, 'breakpoint verification mismatch: line');
|
||||
if (typeof expCol === 'number') assert.equal(bp.column, expCol, 'breakpoint verification mismatch: column');
|
||||
})
|
||||
}
|
|
@ -6,5 +6,9 @@
|
|||
"target": "ES6",
|
||||
"sourceMap": true,
|
||||
"outDir": "../out"
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"../src/**/*.ts",
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
function debuggerStatement() {
|
||||
debugger;
|
||||
}
|
||||
|
||||
setInterval(debuggerStatement, 100);
|
|
@ -0,0 +1,8 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="app.js"></script>
|
||||
<body>
|
||||
<h1>Hello, world!</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -11,6 +11,7 @@
|
|||
"node_modules",
|
||||
"!node_modules/@types",
|
||||
"testapp/",
|
||||
"testdata/",
|
||||
"test/"
|
||||
]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче