Add simple e2e integration tests using vscode-debugadapter-testsupport

This commit is contained in:
roblou 2016-12-20 14:01:53 -08:00
Родитель f900b374f1
Коммит 5be6ff9532
14 изменённых файлов: 280 добавлений и 10 удалений

10
.vscode/launch.json поставляемый
Просмотреть файл

@ -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"]
}
]
}

3
.vscode/settings.json поставляемый
Просмотреть файл

@ -4,7 +4,8 @@
"files.exclude": {
".git": true,
"bin": true,
"node_modules": false
"node_modules": false,
"testapp/.vscode/chrome": true
},
"search.exclude": {
".git": true,

5
.vscode/tasks.json поставляемый
Просмотреть файл

@ -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);

61
test/int/adapter.test.ts Normal file
Просмотреть файл

@ -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;

103
test/int/testSetup.ts Normal file
Просмотреть файл

@ -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();
}

35
test/int/testUtils.ts Normal file
Просмотреть файл

@ -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"
]
}

5
testdata/intervalDebugger/app.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
function debuggerStatement() {
debugger;
}
setInterval(debuggerStatement, 100);

8
testdata/intervalDebugger/index.html поставляемый Normal file
Просмотреть файл

@ -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/"
]
}