More console APIs and tests
This commit is contained in:
Родитель
3eaaa467b0
Коммит
d5c2724a3b
|
@ -0,0 +1,97 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as sinon from 'sinon';
|
||||
import * as mockery from 'mockery';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import * as testUtils from '../testUtils';
|
||||
|
||||
import * as ConsoleHelper from '../../webkit/consoleHelper';
|
||||
|
||||
suite('ConsoleHelper', () => {
|
||||
function doAssert(message: WebKitProtocol.Console.Message, expectedText: string, expectedIsError = false): void {
|
||||
assert.deepEqual(ConsoleHelper.formatConsoleMessage(message), { text: expectedText, isError: expectedIsError });
|
||||
}
|
||||
|
||||
suite('console.log()', () => {
|
||||
test('handles simple log', () => {
|
||||
doAssert(Console.makeLog('Hello'), 'Hello');
|
||||
doAssert(Console.makeLog('Hello', 123, 'world!'), 'Hello 123 world!');
|
||||
});
|
||||
|
||||
test('handles basic format specifiers', () => {
|
||||
doAssert(Console.makeLog('%s, %d', 'test', 123), 'test, 123');
|
||||
});
|
||||
|
||||
test('handles numeric format specifiers correctly', () => {
|
||||
doAssert(Console.makeLog('%d %i %f', 1.9, 324, 9.4), '1 324 9.4');
|
||||
doAssert(Console.makeLog('%d %i %f', -19, -32.5, -9.4), '-19 -33 -9.4');
|
||||
doAssert(Console.makeLog('%d %i %f', 'not', 'a', 'number'), 'NaN NaN NaN');
|
||||
});
|
||||
|
||||
test('handles unmatched format specifiers', () => {
|
||||
doAssert(Console.makeLog('%s %s %s', 'test'), 'test %s %s');
|
||||
doAssert(Console.makeLog('%s %s end', 'test1', 'test2', 'test3'), 'test1 test2 end test3');
|
||||
});
|
||||
|
||||
test('weird cases', () => {
|
||||
doAssert(Console.makeLog('%s %s %s', null, undefined, 'test'), 'null undefined test');
|
||||
doAssert(Console.makeLog('test', null, undefined), 'test null undefined');
|
||||
});
|
||||
});
|
||||
|
||||
suite('console.assert()', () => {
|
||||
test(`Prints params and doesn't resolve format specifiers`, () => {
|
||||
doAssert(Console.makeAssert('Fail %s 123', 456), 'Assertion failed: Fail %s 123 456\nmyFn @/script/a.js:4', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Build the webkit notifications objects for various console APIs.
|
||||
*/
|
||||
namespace Console {
|
||||
/**
|
||||
* Make a mock message of any type.
|
||||
* @param type - The type of the message
|
||||
* @param params - The list of parameters passed to the log function
|
||||
* @param overrideProps - An object of props that the message should have. The rest are filled in with defaults.
|
||||
*/
|
||||
function makeMockMessage(type: string, params: any[], overrideProps?: any): WebKitProtocol.Console.Message {
|
||||
const message = {
|
||||
source: 'console-api',
|
||||
level: 'log',
|
||||
type,
|
||||
text: params[0],
|
||||
timestamp: Date.now(),
|
||||
line: 2,
|
||||
column: 13,
|
||||
url: 'file:///c:/page/script.js',
|
||||
executionContextId: 2,
|
||||
parameters: params.map(param => {
|
||||
return { type: typeof param, value: param }
|
||||
})
|
||||
};
|
||||
|
||||
if (overrideProps) {
|
||||
for (var propName in overrideProps) {
|
||||
if (overrideProps.hasOwnProperty(propName)) {
|
||||
message[propName] = overrideProps[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
export function makeLog(...params: any[]): WebKitProtocol.Console.Message {
|
||||
return makeMockMessage('log', params);
|
||||
}
|
||||
|
||||
export function makeAssert(...params: any[]): WebKitProtocol.Console.Message {
|
||||
const fakeStackTrace = [{ url: '/script/a.js', lineNumber: 4, functionName: 'myFn' }];
|
||||
return makeMockMessage('assert', params, { level: 'error', stackTrace: fakeStackTrace });
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ suite('WebKitDebugAdapter', () => {
|
|||
'../common/handles',
|
||||
'../common/v8Protocol',
|
||||
'./v8Protocol',
|
||||
'./consoleHelper',
|
||||
'events']);
|
||||
|
||||
mockery.registerMock('os', { platform: () => 'win32' });
|
||||
|
@ -245,6 +246,39 @@ suite('WebKitDebugAdapter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
suite('Console.onMessageAdded', () => {
|
||||
test('Fires an output event when a console message is added', () => {
|
||||
const testLog = 'Hello, world!';
|
||||
const wkda = instantiateWKDA();
|
||||
let outputEventFired = false;
|
||||
wkda.registerEventHandler((event: DebugProtocol.Event) => {
|
||||
if (event.event === 'output') {
|
||||
outputEventFired = true;
|
||||
assert.equal(event.body.text, testLog);
|
||||
} else {
|
||||
assert.fail('An unexpected event was fired');
|
||||
}
|
||||
});
|
||||
|
||||
DefaultMockWebKitConnection.EE.emit('Console.onMessageAdded', {
|
||||
message: {
|
||||
source: 'console-api',
|
||||
level: 'log',
|
||||
type: 'log',
|
||||
text: testLog,
|
||||
timestamp: Date.now(),
|
||||
line: 2,
|
||||
column: 13,
|
||||
url: 'file:///c:/page/script.js',
|
||||
executionContextId: 2,
|
||||
parameters: [
|
||||
{type: 'string', value: testLog }
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('setExceptionBreakpoints()', () => { });
|
||||
suite('stepping', () => { });
|
||||
suite('stackTrace()', () => { });
|
||||
|
|
|
@ -61,12 +61,14 @@ function evalDebugger() {
|
|||
function consoleAPIs() {
|
||||
console.time('timing');
|
||||
console.group('my group');
|
||||
console.log('hi');
|
||||
console.assert(1 == 2);
|
||||
console.log('hello', 'world!');
|
||||
console.error('with formatter: %s world!', 'hello');
|
||||
console.log('%d %i %f', -19, -32.5, -9.4);
|
||||
console.groupEnd();
|
||||
console.timeEnd('timing');
|
||||
console.trace();
|
||||
|
||||
console.dir({ a: 1, b: 2 });
|
||||
(<any>console).table([1, 2, 3]);
|
||||
console.assert(1 == 2, '1 is not 2');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as url from 'url';
|
||||
|
||||
export function formatConsoleMessage(m: WebKitProtocol.Console.Message): { text: string, isError: boolean } {
|
||||
let outputText: string;
|
||||
if (m.type === 'log') {
|
||||
outputText = resolveParams(m);
|
||||
} else if (m.type === 'assert') {
|
||||
outputText = 'Assertion failed';
|
||||
if (m.parameters && m.parameters.length) {
|
||||
outputText += ': ' + m.parameters.map(p => p.value).join(' ');
|
||||
}
|
||||
|
||||
outputText += '\n' + stackTraceToString(m.stackTrace);
|
||||
} else if (m.type === 'startGroup' || m.type === 'startGroupCollapsed') {
|
||||
outputText = '‹Start group›';
|
||||
if (m.text) {
|
||||
// Or wherever the label is
|
||||
outputText += ': ' + m.text;
|
||||
}
|
||||
} else if (m.type === 'endGroup') {
|
||||
outputText = '‹End group›'
|
||||
}
|
||||
|
||||
return { text: outputText, isError: m.level === 'error' };
|
||||
}
|
||||
|
||||
function resolveParams(m: WebKitProtocol.Console.Message): string {
|
||||
let text = m.text;
|
||||
if (!m.parameters || m.parameters.length === 1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
m.parameters.shift(); // The first param is 'text'
|
||||
|
||||
// Find all %s, %i, etc. Strip %
|
||||
let formatSpecifiers = text.match(/\%[sidfoOc]/g) || [];
|
||||
formatSpecifiers = formatSpecifiers.map(spec => spec[1]);
|
||||
|
||||
// Append all parameters, formatting properly if there's a format specifier
|
||||
m.parameters.forEach((param, i) => {
|
||||
let formatted: any;
|
||||
if (formatSpecifiers[i] === 's') {
|
||||
formatted = param.value;
|
||||
} else if (['i', 'd'].indexOf(formatSpecifiers[i]) >= 0) {
|
||||
formatted = Math.floor(+param.value);
|
||||
} else if (formatSpecifiers[i] === 'f') {
|
||||
formatted = +param.value;
|
||||
} else if (['o', 'O', 'c'].indexOf(formatSpecifiers[i]) >= 0) {
|
||||
// um
|
||||
formatted = param.value;
|
||||
}
|
||||
|
||||
// If this param had a format specifier, search and replace it with the formatted param.
|
||||
// Otherwise, append it to the end of the text
|
||||
if (formatSpecifiers[i]) {
|
||||
text = text.replace('%' + formatSpecifiers[i], formatted);
|
||||
} else {
|
||||
text += ' ' + param.value;
|
||||
}
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function stackTraceToString(stackTrace: WebKitProtocol.Console.StackTrace): string {
|
||||
return stackTrace
|
||||
.map(frame => `${frame.functionName} @${url.parse(frame.url).pathname}:${frame.lineNumber}`)
|
||||
.join('\n');
|
||||
}
|
|
@ -8,6 +8,7 @@ import {Handles} from '../common/handles';
|
|||
import {WebKitConnection} from './webKitConnection';
|
||||
import * as Utilities from './utilities';
|
||||
import {Logger} from './utilities';
|
||||
import {formatConsoleMessage} from './consoleHelper';
|
||||
|
||||
import {spawn, ChildProcess} from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
@ -213,53 +214,10 @@ export class WebKitDebugAdapter implements IDebugAdapter {
|
|||
}
|
||||
|
||||
private onConsoleMessage(params: WebKitProtocol.Console.MessageAddedParams): void {
|
||||
let outputText: string;
|
||||
const m = params.message;
|
||||
if (m.type === 'log') {
|
||||
outputText = this.resolveParams(m);
|
||||
} else if (m.type in ['startGroup', 'startGroupCollapsed']) {
|
||||
outputText = '<Start group>';
|
||||
if (m.text) {
|
||||
// Or wherever the label is
|
||||
outputText += ': ' + m.text;
|
||||
}
|
||||
} else if (m.type === 'endGroup') {
|
||||
outputText = '<End group>'
|
||||
}
|
||||
|
||||
this.fireEvent(new OutputEvent(outputText + '\n'));
|
||||
}
|
||||
|
||||
private resolveParams(m: WebKitProtocol.Console.Message): string {
|
||||
let text = m.text;
|
||||
if (!m.parameters || !m.parameters.length) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// Find all %s, %i, etc. Strip %
|
||||
const formatSpecifiers = text.match(/\%[sidfoOc]/g);
|
||||
formatSpecifiers.map(spec => spec[1]);
|
||||
|
||||
// Append all parameters, formatting properly if there's a format specifier
|
||||
m.parameters.forEach((param, i) => {
|
||||
let formatted: any;
|
||||
if (formatSpecifiers[i] === 's') {
|
||||
formatted = param;
|
||||
} else if (formatSpecifiers[i] in ['i', 'd']) {
|
||||
formatted = Math.floor(+param);
|
||||
} else if (formatSpecifiers[i] === 'f') {
|
||||
formatted = +param;
|
||||
} else if (formatSpecifiers[i] in ['o', 'O', 'c']) {
|
||||
// um
|
||||
formatted = param;
|
||||
} else {
|
||||
formatted = param;
|
||||
}
|
||||
|
||||
text += formatted;
|
||||
});
|
||||
|
||||
return text;
|
||||
const formattedMessage = formatConsoleMessage(params.message);
|
||||
this.fireEvent(new OutputEvent(
|
||||
formattedMessage.text + '\n',
|
||||
formattedMessage.isError ? 'stderr' : 'console'));
|
||||
}
|
||||
|
||||
public disconnect(): Promise<void> {
|
||||
|
|
|
@ -211,15 +211,19 @@ declare namespace WebKitProtocol {
|
|||
|
||||
// 'debug', 'error', 'log', 'warning'
|
||||
level: string;
|
||||
|
||||
// 'assert', 'clear', 'dir', 'dirxml', 'endGroup', 'log', 'profile', 'profileEnd',
|
||||
// 'startGroup', 'startGroupCollapsed', 'table', 'timing', 'trace'
|
||||
type?: string;
|
||||
|
||||
parameters?: Runtime.RemoteObject[];
|
||||
repeatCount?: string;
|
||||
stackTrace?: StackTrace;
|
||||
text: string;
|
||||
url?: string;
|
||||
|
||||
// 'assert', 'clear', 'dir', 'dirxml', 'endGroup', 'log', 'profile', 'profileEnd',
|
||||
// 'startGroup', 'startGroupCollapsed', 'table', 'timing', 'trace'
|
||||
type?: string;
|
||||
source?: string;
|
||||
timestamp?: number;
|
||||
executionContextId?: number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче