This commit is contained in:
Rob Lourens 2015-11-03 14:17:50 -08:00
Родитель 3eaaa467b0
Коммит d5c2724a3b
6 изменённых файлов: 221 добавлений и 53 удалений

Просмотреть файл

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

73
webkit/consoleHelper.ts Normal file
Просмотреть файл

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

12
webkit/webKitProtocol.d.ts поставляемый
Просмотреть файл

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