And, while I'm changing every single relative import path, move product source into src/
This commit is contained in:
Rob Lourens 2016-04-03 18:51:03 -07:00
Родитель 71db62a826
Коммит 34a973f91b
31 изменённых файлов: 1419 добавлений и 1339 удалений

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

@ -4,7 +4,7 @@
{
"name": "launch as server",
"type": "node",
"program": "${workspaceRoot}/out/webkit/webKitDebug.js",
"program": "${workspaceRoot}/out/src/chrome/chromeDebug.js",
"runtimeArgs": ["--harmony"],
"stopOnEntry": false,
"args": [ "--server=4712" ],

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

@ -8,7 +8,7 @@ To avoid a conflict, delete the installed extension at `~/.vscode/extensions/msj
### OS X/Linux
* `git clone` this repository
* Run `ln -s <path to repo> ~/.vscode/extensions/vsc-webkit`
* Run `ln -s <path to repo> ~/.vscode/extensions/vscode-chrome-debug`
* You could clone it to the extensions directory if you want, but working with hidden folders in OS X can be a pain.
### Then...
@ -26,8 +26,6 @@ There is a set of mocha tests which can be run with `gulp test` or with the `tes
See the project under testapp/ for a bunch of test scenarios crammed onto one page.
## Code
Some files were copied from [the Node adapter](https://github.com/Microsoft/vscode-node-debug) and I'll periodically check for fixes to port over.
* Files under common/
* adapter/sourceMaps/sourceMaps.ts
* adapter/sourceMaps/pathUtilities.ts
## Naming
Client: VS Code
Target: The debuggee, which implements the Chrome Debug Protocol

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

@ -109,4 +109,4 @@ General things to try if you're having issues:
* Check the console for warnings that this extension prints in some cases when it can't attach
* Ensure the code in Chrome matches the code in Code. Chrome may cache an old version.
* If you set a breakpoint in code that runs immediately when the page loads, you won't hit that breakpoint until you refresh the page.
* File a bug in this extension's [GitHub repo](https://github.com/Microsoft/vscode-webkit-debug). Set the "diagnosticLogging" field in your launch config and attach the logs when filing a bug.
* File a bug in this extension's [GitHub repo](https://github.com/Microsoft/vscode-chrome-debug). Set the "diagnosticLogging" field in your launch config and attach the logs when filing a bug.

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

@ -2,23 +2,27 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
var gulp = require('gulp');
var path = require('path');
var ts = require('gulp-typescript');
var log = require('gulp-util').log;
var typescript = require('typescript');
var sourcemaps = require('gulp-sourcemaps');
var mocha = require('gulp-mocha');
const gulp = require('gulp');
const path = require('path');
const ts = require('gulp-typescript');
const log = require('gulp-util').log;
const typescript = require('typescript');
const sourcemaps = require('gulp-sourcemaps');
const mocha = require('gulp-mocha');
const tslint = require('gulp-tslint');
var sources = [
'adapter',
'common',
const sources = [
'src',
'test',
'typings',
'webkit',
].map(function(tsFolder) { return tsFolder + '/**/*.ts'; });
var projectConfig = {
const lintSources = [
'test',
'src'
].map(function(tsFolder) { return tsFolder + '/**/*.ts'; });
const projectConfig = {
noImplicitAny: false,
target: 'ES5',
module: 'commonjs',
@ -41,19 +45,6 @@ gulp.task('watch', ['build'], function(cb) {
gulp.task('default', ['build']);
// Don't lint code from tsd or common, and whitelist my files under adapter
var lintSources = [
'test',
'webkit',
].map(function(tsFolder) { return tsFolder + '/**/*.ts'; });
lintSources = lintSources.concat([
'adapter/sourceMaps/sourceMapTransformer.ts',
'adapter/adapterProxy.ts',
'adapter/lineNumberTransformer.ts',
'adapter/pathTransformer.ts',
]);
var tslint = require('gulp-tslint');
gulp.task('tslint', function(){
return gulp.src(lintSources, { base: '.' })
.pipe(tslint())

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

@ -51,7 +51,7 @@
"typescriptreact"
]
},
"program": "./out/webkit/webkitDebug.js",
"program": "./out/src/chrome/chromeDebug.js",
"runtime": "node",
"initialConfigurations": [
{

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

@ -4,18 +4,35 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {IDebugAdapter, IDebugTransformer} from '../webkit/webKitAdapterInterfaces';
import * as utils from '../webkit/utilities';
import {IDebugAdapter, IDebugTransformer} from './chrome/debugAdapterInterfaces';
import * as utils from './utils';
export type EventHandler = (event: DebugProtocol.Event) => void;
/**
* Keeps a set of IDebugTransformers and an IDebugAdapter. Has one public method - dispatchRequest, which passes a request through each
* IDebugTransformer, then to the IDebugAdapter.
*/
export class AdapterProxy {
private static INTERNAL_EVENTS = ['scriptParsed', 'clearClientContext', 'clearTargetContext'];
public constructor(private _requestTransformers: IDebugTransformer[], private _debugAdapter: IDebugAdapter, private _eventHandler: EventHandler) {
private _requestTransformers: IDebugTransformer[];
private _debugAdapter: IDebugAdapter;
private _eventHandler: EventHandler;
public constructor(requestTransformers: IDebugTransformer[], debugAdapter: IDebugAdapter, eventHandler: EventHandler) {
this._requestTransformers = requestTransformers;
this._debugAdapter = debugAdapter;
this._eventHandler = eventHandler;
this._debugAdapter.registerEventHandler(event => this.onAdapterEvent(event));
}
/**
* Passes the request through all IDebugTransformers, then the IDebugAdapter. The request from the IDebugAdapter is passed through all the
* IDebugTransformers in reverse.
* Returns a Promise that resolves to the transformed response body.
*/
public dispatchRequest(request: DebugProtocol.Request): Promise<any> {
if (!(request.command in this._debugAdapter)) {
return utils.errP('unknowncommand');

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

@ -4,8 +4,8 @@
import * as WebSocket from 'ws';
import {EventEmitter} from 'events';
import * as utils from './utilities';
import {Logger, LogLevel} from './utilities';
import * as utils from '../utils';
import {Logger, LogLevel} from '../utils';
interface IMessageWithId {
id: number;
@ -120,9 +120,9 @@ class ResReqWebSocket extends EventEmitter {
}
/**
* Connects to a target supporting the webkit protocol and sends and receives messages
* Connects to a target supporting the Chrome Debug Protocol and sends and receives messages
*/
export class WebKitConnection {
export class ChromeConnection {
private _nextId = 1;
private _socket: ResReqWebSocket;
@ -191,67 +191,67 @@ export class WebKitConnection {
this._socket.close();
}
public debugger_setBreakpoint(location: WebKitProtocol.Debugger.Location, condition?: string): Promise<WebKitProtocol.Debugger.SetBreakpointResponse> {
return this.sendMessage('Debugger.setBreakpoint', <WebKitProtocol.Debugger.SetBreakpointParams>{ location, condition });
public debugger_setBreakpoint(location: Chrome.Debugger.Location, condition?: string): Promise<Chrome.Debugger.SetBreakpointResponse> {
return this.sendMessage('Debugger.setBreakpoint', <Chrome.Debugger.SetBreakpointParams>{ location, condition });
}
public debugger_setBreakpointByUrl(url: string, lineNumber: number, columnNumber: number): Promise<WebKitProtocol.Debugger.SetBreakpointByUrlResponse> {
return this.sendMessage('Debugger.setBreakpointByUrl', <WebKitProtocol.Debugger.SetBreakpointByUrlParams>{ url, lineNumber, columnNumber });
public debugger_setBreakpointByUrl(url: string, lineNumber: number, columnNumber: number): Promise<Chrome.Debugger.SetBreakpointByUrlResponse> {
return this.sendMessage('Debugger.setBreakpointByUrl', <Chrome.Debugger.SetBreakpointByUrlParams>{ url, lineNumber, columnNumber });
}
public debugger_removeBreakpoint(breakpointId: string): Promise<WebKitProtocol.Response> {
return this.sendMessage('Debugger.removeBreakpoint', <WebKitProtocol.Debugger.RemoveBreakpointParams>{ breakpointId });
public debugger_removeBreakpoint(breakpointId: string): Promise<Chrome.Response> {
return this.sendMessage('Debugger.removeBreakpoint', <Chrome.Debugger.RemoveBreakpointParams>{ breakpointId });
}
public debugger_stepOver(): Promise<WebKitProtocol.Response> {
public debugger_stepOver(): Promise<Chrome.Response> {
return this.sendMessage('Debugger.stepOver');
}
public debugger_stepIn(): Promise<WebKitProtocol.Response> {
public debugger_stepIn(): Promise<Chrome.Response> {
return this.sendMessage('Debugger.stepInto');
}
public debugger_stepOut(): Promise<WebKitProtocol.Response> {
public debugger_stepOut(): Promise<Chrome.Response> {
return this.sendMessage('Debugger.stepOut');
}
public debugger_resume(): Promise<WebKitProtocol.Response> {
public debugger_resume(): Promise<Chrome.Response> {
return this.sendMessage('Debugger.resume');
}
public debugger_pause(): Promise<WebKitProtocol.Response> {
public debugger_pause(): Promise<Chrome.Response> {
return this.sendMessage('Debugger.pause');
}
public debugger_evaluateOnCallFrame(callFrameId: string, expression: string, objectGroup = 'dummyObjectGroup', returnByValue?: boolean): Promise<WebKitProtocol.Debugger.EvaluateOnCallFrameResponse> {
return this.sendMessage('Debugger.evaluateOnCallFrame', <WebKitProtocol.Debugger.EvaluateOnCallFrameParams>{ callFrameId, expression, objectGroup, returnByValue });
public debugger_evaluateOnCallFrame(callFrameId: string, expression: string, objectGroup = 'dummyObjectGroup', returnByValue?: boolean): Promise<Chrome.Debugger.EvaluateOnCallFrameResponse> {
return this.sendMessage('Debugger.evaluateOnCallFrame', <Chrome.Debugger.EvaluateOnCallFrameParams>{ callFrameId, expression, objectGroup, returnByValue });
}
public debugger_setPauseOnExceptions(state: string): Promise<WebKitProtocol.Response> {
return this.sendMessage('Debugger.setPauseOnExceptions', <WebKitProtocol.Debugger.SetPauseOnExceptionsParams>{ state });
public debugger_setPauseOnExceptions(state: string): Promise<Chrome.Response> {
return this.sendMessage('Debugger.setPauseOnExceptions', <Chrome.Debugger.SetPauseOnExceptionsParams>{ state });
}
public debugger_getScriptSource(scriptId: WebKitProtocol.Debugger.ScriptId): Promise<WebKitProtocol.Debugger.GetScriptSourceResponse> {
return this.sendMessage('Debugger.getScriptSource', <WebKitProtocol.Debugger.GetScriptSourceParams>{ scriptId });
public debugger_getScriptSource(scriptId: Chrome.Debugger.ScriptId): Promise<Chrome.Debugger.GetScriptSourceResponse> {
return this.sendMessage('Debugger.getScriptSource', <Chrome.Debugger.GetScriptSourceParams>{ scriptId });
}
public runtime_getProperties(objectId: string, ownProperties: boolean, accessorPropertiesOnly: boolean): Promise<WebKitProtocol.Runtime.GetPropertiesResponse> {
return this.sendMessage('Runtime.getProperties', <WebKitProtocol.Runtime.GetPropertiesParams>{ objectId, ownProperties, accessorPropertiesOnly });
public runtime_getProperties(objectId: string, ownProperties: boolean, accessorPropertiesOnly: boolean): Promise<Chrome.Runtime.GetPropertiesResponse> {
return this.sendMessage('Runtime.getProperties', <Chrome.Runtime.GetPropertiesParams>{ objectId, ownProperties, accessorPropertiesOnly });
}
public runtime_evaluate(expression: string, objectGroup = 'dummyObjectGroup', contextId?: number, returnByValue = false): Promise<WebKitProtocol.Runtime.EvaluateResponse> {
return this.sendMessage('Runtime.evaluate', <WebKitProtocol.Runtime.EvaluateParams>{ expression, objectGroup, contextId, returnByValue });
public runtime_evaluate(expression: string, objectGroup = 'dummyObjectGroup', contextId?: number, returnByValue = false): Promise<Chrome.Runtime.EvaluateResponse> {
return this.sendMessage('Runtime.evaluate', <Chrome.Runtime.EvaluateParams>{ expression, objectGroup, contextId, returnByValue });
}
public page_setOverlayMessage(message: string): Promise<WebKitProtocol.Response> {
public page_setOverlayMessage(message: string): Promise<Chrome.Response> {
return this.sendMessage('Page.setOverlayMessage', { message });
}
public page_clearOverlayMessage(): Promise<WebKitProtocol.Response> {
public page_clearOverlayMessage(): Promise<Chrome.Response> {
return this.sendMessage('Page.setOverlayMessage');
}
private sendMessage(method: any, params?: any): Promise<WebKitProtocol.Response> {
private sendMessage(method: any, params?: any): Promise<Chrome.Response> {
return this._socket.sendMessage({
id: this._nextId++,
method,

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

@ -2,7 +2,7 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import {WebKitDebugSession} from './webKitDebugSession';
import {ChromeDebugSession} from './chromeDebugSession';
import {DebugSession} from 'vscode-debugadapter';
DebugSession.run(WebKitDebugSession);
DebugSession.run(ChromeDebugSession);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,4 +1,8 @@
declare namespace WebKitProtocol {
/**
* Chrome Debugging Protocol - documented at
* https://developer.chrome.com/devtools/docs/protocol/1.1/index
*/
declare namespace Chrome {
interface Notification {
method: string;
params: any;

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

@ -5,16 +5,16 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {DebugSession, ErrorDestination, OutputEvent} from 'vscode-debugadapter';
import {WebKitDebugAdapter} from './webKitDebugAdapter';
import * as utils from './utilities';
import {Logger} from './utilities';
import {ChromeDebugAdapter} from './chromeDebugAdapter';
import * as utils from '../utils';
import {Logger} from '../utils';
import {AdapterProxy} from '../adapter/adapterProxy';
import {LineNumberTransformer} from '../adapter/lineNumberTransformer';
import {PathTransformer} from '../adapter/pathTransformer';
import {SourceMapTransformer} from '../adapter/sourceMaps/sourceMapTransformer';
import {AdapterProxy} from '../adapterProxy';
import {LineNumberTransformer} from '../transformers/lineNumberTransformer';
import {PathTransformer} from '../transformers/pathTransformer';
import {SourceMapTransformer} from '../transformers/sourceMaps/sourceMapTransformer';
export class WebKitDebugSession extends DebugSession {
export class ChromeDebugSession extends DebugSession {
private _adapterProxy: AdapterProxy;
public constructor(targetLinesStartAt1: boolean, isServer: boolean = false) {
@ -31,7 +31,7 @@ export class WebKitDebugSession extends DebugSession {
new SourceMapTransformer(),
new PathTransformer()
],
new WebKitDebugAdapter(),
new ChromeDebugAdapter(),
event => this.sendEvent(event));
}
@ -115,29 +115,29 @@ export class WebKitDebugSession extends DebugSession {
*/
class Message implements DebugProtocol.ProtocolMessage {
seq: number;
type: string;
public seq: number;
public type: string;
public constructor(type: string) {
this.seq = 0;
this.type = type;
}
public constructor(type: string) {
this.seq = 0;
this.type = type;
}
}
class Response extends Message implements DebugProtocol.Response {
request_seq: number;
success: boolean;
command: string;
public request_seq: number;
public success: boolean;
public command: string;
public constructor(request: DebugProtocol.Request, message?: string) {
super('response');
this.request_seq = request.seq;
this.command = request.command;
if (message) {
this.success = false;
(<any>this).message = message;
} else {
this.success = true;
}
}
public constructor(request: DebugProtocol.Request, message?: string) {
super('response');
this.request_seq = request.seq;
this.command = request.command;
if (message) {
this.success = false;
(<any>this).message = message;
} else {
this.success = true;
}
}
}

94
src/chrome/chromeUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,94 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as url from 'url';
import * as path from 'path';
import * as Utils from '../utils';
/**
* Maps a url from target to an absolute local path.
* If not given an absolute path (with file: prefix), searches the current working directory for a matching file.
* http://localhost/scripts/code.js => d:/app/scripts/code.js
* file:///d:/scripts/code.js => d:/scripts/code.js
*/
export function targetUrlToClientPath(webRoot: string, aUrl: string): string {
if (!aUrl) {
return '';
}
aUrl = decodeURI(aUrl);
// If the url is an absolute path to a file that exists, return it without file:///.
// A remote absolute url (cordova) will still need the logic below.
if (aUrl.startsWith('file:///') && Utils.existsSync(aUrl.replace(/^file:\/\/\//, ''))) {
return Utils.canonicalizeUrl(aUrl);
}
// If we don't have the client workingDirectory for some reason, don't try to map the url to a client path
if (!webRoot) {
return '';
}
// Search the filesystem under the webRoot for the file that best matches the given url
let pathName = decodeURIComponent(url.parse(Utils.canonicalizeUrl(aUrl)).pathname);
if (!pathName || pathName === '/') {
return '';
}
// Dealing with the path portion of either a url or an absolute path to remote file.
// Need to force path.sep separator
pathName = pathName.replace(/\//g, path.sep);
const pathParts = pathName.split(path.sep);
while (pathParts.length > 0) {
const clientPath = path.join(webRoot, pathParts.join(path.sep));
if (Utils.existsSync(clientPath)) {
return Utils.canonicalizeUrl(clientPath);
}
pathParts.shift();
}
return '';
}
/**
* Convert a RemoteObject to a value+variableHandleRef for the client.
*/
export function remoteObjectToValue(object: Chrome.Runtime.RemoteObject, stringify = true): { value: string, variableHandleRef?: string } {
let value = '';
let variableHandleRef: string;
if (object) {
if (object.type === 'object') {
if (object.subtype === 'null') {
value = 'null';
} else {
// If it's a non-null object, create a variable reference so the client can ask for its props
variableHandleRef = object.objectId;
value = object.description;
}
} else if (object.type === 'undefined') {
value = 'undefined';
} else if (object.type === 'function') {
const firstBraceIdx = object.description.indexOf('{');
if (firstBraceIdx >= 0) {
value = object.description.substring(0, firstBraceIdx) + '{ … }';
} else {
const firstArrowIdx = object.description.indexOf('=>');
value = firstArrowIdx >= 0 ?
object.description.substring(0, firstArrowIdx + 2) + ' …' :
object.description;
}
} else {
// The value is a primitive value, or something that has a description (not object, primitive, or undefined). And force to be string
if (typeof object.value === 'undefined') {
value = object.description;
} else {
value = stringify ? JSON.stringify(object.value) : object.value;
}
}
}
return { value, variableHandleRef };
}

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

@ -3,9 +3,9 @@
*--------------------------------------------------------*/
import * as url from 'url';
import * as Utilities from './utilities';
import * as ChromeUtils from './chromeUtils';
export function formatConsoleMessage(m: WebKitProtocol.Console.Message): { text: string, isError: boolean } {
export function formatConsoleMessage(m: Chrome.Console.Message): { text: string, isError: boolean } {
let outputText: string;
if (m.type === 'log') {
outputText = resolveParams(m);
@ -37,7 +37,7 @@ export function formatConsoleMessage(m: WebKitProtocol.Console.Message): { text:
return { text: outputText, isError: m.level === 'error' };
}
function resolveParams(m: WebKitProtocol.Console.Message): string {
function resolveParams(m: Chrome.Console.Message): string {
if (!m.parameters || !m.parameters.length) {
return m.text;
}
@ -79,8 +79,8 @@ function resolveParams(m: WebKitProtocol.Console.Message): string {
return text;
}
function remoteObjectToString(obj: WebKitProtocol.Runtime.RemoteObject): string {
const result = Utilities.remoteObjectToValue(obj, /*stringify=*/false);
function remoteObjectToString(obj: Chrome.Runtime.RemoteObject): string {
const result = ChromeUtils.remoteObjectToValue(obj, /*stringify=*/false);
if (result.variableHandleRef) {
// The DebugProtocol console API doesn't support returning a variable reference, so do our best to
// build a useful string out of this object.
@ -111,7 +111,7 @@ function remoteObjectToString(obj: WebKitProtocol.Runtime.RemoteObject): string
}
}
function arrayRemoteObjToString(obj: WebKitProtocol.Runtime.RemoteObject): string {
function arrayRemoteObjToString(obj: Chrome.Runtime.RemoteObject): string {
if (obj.preview && obj.preview.properties) {
let props: string = obj.preview.properties
.map(prop => prop.value)
@ -127,7 +127,7 @@ function arrayRemoteObjToString(obj: WebKitProtocol.Runtime.RemoteObject): strin
}
}
function stackTraceToString(stackTrace: WebKitProtocol.Console.StackTrace): string {
function stackTraceToString(stackTrace: Chrome.Console.StackTrace): string {
return stackTrace
.map(frame => {
const fnName = frame.functionName || (frame.url ? '(anonymous)' : '(eval)');

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

@ -1,3 +1,11 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
/**
* This file contains extended forms of interfaces from vscode-debugprotocol
*/
import {DebugProtocol} from 'vscode-debugprotocol';
export interface ILaunchRequestArgs extends DebugProtocol.LaunchRequestArguments {

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

@ -4,7 +4,7 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {IBreakpoint, IDebugTransformer, ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../webkit/webKitAdapterInterfaces';
import {IDebugTransformer, ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../chrome/debugAdapterInterfaces';
/**
* Converts from 1 based lines on the client side to 0 based lines on the target side

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

@ -4,8 +4,9 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {ISetBreakpointsArgs, IDebugTransformer, ILaunchRequestArgs, IAttachRequestArgs, IStackTraceResponseBody} from '../webkit/webKitAdapterInterfaces';
import * as utils from '../webkit/utilities';
import {ISetBreakpointsArgs, IDebugTransformer, ILaunchRequestArgs, IAttachRequestArgs, IStackTraceResponseBody} from '../chrome/debugAdapterInterfaces';
import * as utils from '../utils';
import * as ChromeUtils from '../chrome/chromeUtils';
interface IPendingBreakpoint {
resolve: () => void;
@ -18,8 +19,8 @@ interface IPendingBreakpoint {
*/
export class PathTransformer implements IDebugTransformer {
private _webRoot: string;
private _clientPathToWebkitUrl = new Map<string, string>();
private _webkitUrlToClientPath = new Map<string, string>();
private _clientPathToTargetUrl = new Map<string, string>();
private _targetUrlToClientPath = new Map<string, string>();
private _pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
public launch(args: ILaunchRequestArgs): void {
@ -45,8 +46,8 @@ export class PathTransformer implements IDebugTransformer {
}
const url = utils.canonicalizeUrl(args.source.path);
if (this._clientPathToWebkitUrl.has(url)) {
args.source.path = this._clientPathToWebkitUrl.get(url);
if (this._clientPathToTargetUrl.has(url)) {
args.source.path = this._clientPathToTargetUrl.get(url);
utils.Logger.log(`Paths.setBP: Resolved ${url} to ${args.source.path}`);
resolve();
} else {
@ -62,20 +63,20 @@ export class PathTransformer implements IDebugTransformer {
}
public clearTargetContext(): void {
this._clientPathToWebkitUrl = new Map<string, string>();
this._webkitUrlToClientPath = new Map<string, string>();
this._clientPathToTargetUrl = new Map<string, string>();
this._targetUrlToClientPath = new Map<string, string>();
}
public scriptParsed(event: DebugProtocol.Event): void {
const webkitUrl: string = event.body.scriptUrl;
const clientPath = utils.webkitUrlToClientPath(this._webRoot, webkitUrl);
const targetUrl: string = event.body.scriptUrl;
const clientPath = ChromeUtils.targetUrlToClientPath(this._webRoot, targetUrl);
if (!clientPath) {
utils.Logger.log(`Paths.scriptParsed: could not resolve ${webkitUrl} to a file in the workspace. webRoot: ${this._webRoot}`);
utils.Logger.log(`Paths.scriptParsed: could not resolve ${targetUrl} to a file in the workspace. webRoot: ${this._webRoot}`);
} else {
utils.Logger.log(`Paths.scriptParsed: resolved ${webkitUrl} to ${clientPath}. webRoot: ${this._webRoot}`);
this._clientPathToWebkitUrl.set(clientPath, webkitUrl);
this._webkitUrlToClientPath.set(webkitUrl, clientPath);
utils.Logger.log(`Paths.scriptParsed: resolved ${targetUrl} to ${clientPath}. webRoot: ${this._webRoot}`);
this._clientPathToTargetUrl.set(clientPath, targetUrl);
this._targetUrlToClientPath.set(targetUrl, clientPath);
event.body.scriptUrl = clientPath;
}
@ -93,9 +94,9 @@ export class PathTransformer implements IDebugTransformer {
if (frame.source.path) {
// Try to resolve the url to a path in the workspace. If it's not in the workspace,
// just use the script.url as-is. It will be resolved or cleared by the SourceMapTransformer.
const clientPath = this._webkitUrlToClientPath.has(frame.source.path) ?
this._webkitUrlToClientPath.get(frame.source.path) :
utils.webkitUrlToClientPath(this._webRoot, frame.source.path);
const clientPath = this._targetUrlToClientPath.has(frame.source.path) ?
this._targetUrlToClientPath.get(frame.source.path) :
ChromeUtils.targetUrlToClientPath(this._webRoot, frame.source.path);
// Incoming stackFrames have sourceReference and path set. If the path was resolved to a file in the workspace,
// clear the sourceReference since it's not needed.

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

@ -2,12 +2,13 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
/* tslint:disable */
/* tslint:disable */
// TODO - This file was originally copied from vscode-node-debug and needs to be cleaned up to meet our guidelines.
import * as Path from 'path';
import * as URL from 'url';
import * as utils from '../../webkit/utilities';
import * as utils from '../../utils';
export function getPathRoot(p: string) {
if (p) {

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

@ -7,10 +7,10 @@ import * as path from 'path';
import {DebugProtocol} from 'vscode-debugprotocol';
import {IDebugTransformer, ISetBreakpointsArgs, ILaunchRequestArgs, IAttachRequestArgs,
ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../../webkit/webKitAdapterInterfaces';
ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../../chrome/debugAdapterInterfaces';
import {ISourceMaps, SourceMaps} from './sourceMaps';
import * as utils from '../../webkit/utilities';
import {Logger} from '../../webkit/utilities';
import * as utils from '../../utils';
import {Logger} from '../../utils';
interface IPendingBreakpoint {
resolve: () => void;

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

@ -9,8 +9,8 @@ import * as URL from 'url';
import * as FS from 'fs';
import {SourceMapConsumer} from 'source-map';
import * as PathUtils from './pathUtilities';
import * as utils from '../../webkit/utilities';
import {Logger} from '../../webkit/utilities';
import * as utils from '../../utils';
import {Logger} from '../../utils';
export interface MappingResult {

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

@ -202,53 +202,7 @@ export class Logger {
}
/**
* Maps a url from webkit to an absolute local path.
* If not given an absolute path (with file: prefix), searches the current working directory for a matching file.
* http://localhost/scripts/code.js => d:/app/scripts/code.js
* file:///d:/scripts/code.js => d:/scripts/code.js
*/
export function webkitUrlToClientPath(webRoot: string, aUrl: string): string {
if (!aUrl) {
return '';
}
aUrl = decodeURI(aUrl);
// If the url is an absolute path to a file that exists, return it without file:///.
// A remote absolute url (cordova) will still need the logic below.
if (aUrl.startsWith('file:///') && existsSync(aUrl.replace(/^file:\/\/\//, ''))) {
return canonicalizeUrl(aUrl);
}
// If we don't have the client workingDirectory for some reason, don't try to map the url to a client path
if (!webRoot) {
return '';
}
// Search the filesystem under the webRoot for the file that best matches the given url
let pathName = decodeURIComponent(url.parse(canonicalizeUrl(aUrl)).pathname);
if (!pathName || pathName === '/') {
return '';
}
// Dealing with the path portion of either a url or an absolute path to remote file.
// Need to force path.sep separator
pathName = pathName.replace(/\//g, path.sep);
const pathParts = pathName.split(path.sep);
while (pathParts.length > 0) {
const clientPath = path.join(webRoot, pathParts.join(path.sep));
if (existsSync(clientPath)) {
return canonicalizeUrl(clientPath);
}
pathParts.shift();
}
return '';
}
/**
* Modify a url either from the client or the webkit target to a common format for comparing.
* Modify a url either from the client or the target to a common format for comparing.
* The client can handle urls in this format too.
* file:///D:\\scripts\\code.js => d:/scripts/code.js
* file:///Users/me/project/code.js => /Users/me/project/code.js
@ -308,44 +262,6 @@ export function stripTrailingSlash(aPath: string): string {
.replace(/\\$/, '');
}
export function remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject, stringify = true): { value: string, variableHandleRef: string } {
let value = '';
let variableHandleRef: string;
if (object) {
if (object.type === 'object') {
if (object.subtype === 'null') {
value = 'null';
} else {
// If it's a non-null object, create a variable reference so the client can ask for its props
variableHandleRef = object.objectId;
value = object.description;
}
} else if (object.type === 'undefined') {
value = 'undefined';
} else if (object.type === 'function') {
const firstBraceIdx = object.description.indexOf('{');
if (firstBraceIdx >= 0) {
value = object.description.substring(0, firstBraceIdx) + '{ … }';
} else {
const firstArrowIdx = object.description.indexOf('=>');
value = firstArrowIdx >= 0 ?
object.description.substring(0, firstArrowIdx + 2) + ' …' :
object.description;
}
} else {
// The value is a primitive value, or something that has a description (not object, primitive, or undefined). And force to be string
if (typeof object.value === 'undefined') {
value = object.description;
} else {
value = stringify ? JSON.stringify(object.value) : object.value;
}
}
}
return { value, variableHandleRef };
}
/**
* A helper for returning a rejected promise with an Error object. Avoids double-wrapping an Error, which could happen
* when passing on a failure from a Promise error handler.

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

@ -4,45 +4,48 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {IStackTraceResponseBody, ISetBreakpointsResponseBody} from '../../webkit/webKitAdapterInterfaces';
import {ISetBreakpointsResponseBody} from '../../src/chrome/debugAdapterInterfaces';
import * as mockery from 'mockery';
import {EventEmitter} from 'events';
import * as assert from 'assert';
import * as testUtils from '../testUtils';
import * as utils from '../../webkit/utilities';
import * as utils from '../../src/utils';
/** Not mocked - use for type only */
import {WebKitDebugAdapter as _WebKitDebugAdapter} from '../../webkit/webKitDebugAdapter';
import {ChromeDebugAdapter as _ChromeDebugAdapter} from '../../src/chrome/chromeDebugAdapter';
const MODULE_UNDER_TEST = '../../webkit/webKitDebugAdapter';
suite('WebKitDebugAdapter', () => {
let mockWebKitConnection: Sinon.SinonMock;
const MODULE_UNDER_TEST = '../../src/chrome/chromeDebugAdapter';
suite('ChromeDebugAdapter', () => {
let mockChromeConnection: Sinon.SinonMock;
setup(() => {
testUtils.setupUnhandledRejectionListener();
mockery.enable({ useCleanCache: true, warnOnReplace: false });
mockery.registerAllowables([
MODULE_UNDER_TEST,
'./utilities']);
// Allow the common/ stuff - almost none of it is actually used but I can't get rid of the requires entirely
mockery.registerAllowables([
'../common/debugSession',
'../common/handles',
'../common/v8Protocol',
'./v8Protocol',
'../utils',
'./chromeUtils',
'./consoleHelper',
'events']);
// Allow vscode-debugadapter and dependencies - not complicated stuff
mockery.registerAllowables([
'vscode-debugadapter',
'./debugSession',
'./protocol',
'./messages',
'./handles'
]);
mockery.registerMock('os', { platform: () => 'win32' });
testUtils.registerEmptyMocks(['child_process', 'url', 'path', 'net', 'fs', 'http']);
mockWebKitConnection = testUtils.createRegisteredSinonMock('./webKitConnection', new DefaultMockWebKitConnection(), 'WebKitConnection');
mockChromeConnection = testUtils.createRegisteredSinonMock('./chromeConnection', new DefaultMockChromeConnection(), 'ChromeConnection');
});
teardown(() => {
DefaultMockWebKitConnection.EE.removeAllListeners();
DefaultMockChromeConnection.EE.removeAllListeners();
testUtils.removeUnhandledRejectionListener();
mockery.deregisterAll();
mockery.disable();
@ -68,7 +71,7 @@ suite('WebKitDebugAdapter', () => {
});
test('if unsuccessful, the promise is rejected and an initialized event is not fired', done => {
mockWebKitConnection.expects('attach').returns(utils.errP('Testing attach failed'));
mockChromeConnection.expects('attach').returns(utils.errP('Testing attach failed'));
const wkda = instantiateWKDA();
wkda.registerEventHandler((event: DebugProtocol.Event) => {
@ -91,19 +94,19 @@ suite('WebKitDebugAdapter', () => {
columnNumber = cols[i];
}
mockWebKitConnection.expects('debugger_setBreakpointByUrl')
mockChromeConnection.expects('debugger_setBreakpointByUrl')
.once()
.withArgs(FILE_NAME, lineNumber, columnNumber)
.returns(<WebKitProtocol.Debugger.SetBreakpointByUrlResponse>{ id: 0, result: { breakpointId: BP_ID + i, locations: [{ scriptId, lineNumber, columnNumber }] } });
.returns(<Chrome.Debugger.SetBreakpointByUrlResponse>{ id: 0, result: { breakpointId: BP_ID + i, locations: [{ scriptId, lineNumber, columnNumber }] } });
});
}
function expectRemoveBreakpoint(indicies: number[]): void {
indicies.forEach(i => {
mockWebKitConnection.expects('debugger_removeBreakpoint')
mockChromeConnection.expects('debugger_removeBreakpoint')
.once()
.withArgs(BP_ID + i)
.returns(<WebKitProtocol.Response>{ id: 0 });
.returns(<Chrome.Response>{ id: 0 });
});
}
@ -128,7 +131,7 @@ suite('WebKitDebugAdapter', () => {
return attach(wkda).then(() => {
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
mockWebKitConnection.verify();
mockChromeConnection.verify();
assert.deepEqual(response, makeExpectedResponse(lines, cols));
});
});
@ -142,7 +145,7 @@ suite('WebKitDebugAdapter', () => {
return attach(wkda).then(() => {
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
mockWebKitConnection.verify();
mockChromeConnection.verify();
assert.deepEqual(response, makeExpectedResponse(lines, cols));
});
});
@ -163,7 +166,7 @@ suite('WebKitDebugAdapter', () => {
expectSetBreakpoint(lines, cols);
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
mockWebKitConnection.verify();
mockChromeConnection.verify();
assert.deepEqual(response, makeExpectedResponse(lines, cols));
});
});
@ -184,7 +187,7 @@ suite('WebKitDebugAdapter', () => {
expectSetBreakpoint(lines, cols);
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
mockWebKitConnection.verify();
mockChromeConnection.verify();
assert.deepEqual(response, makeExpectedResponse(lines, cols));
});
});
@ -199,17 +202,17 @@ suite('WebKitDebugAdapter', () => {
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
expectRemoveBreakpoint([2, 3]);
DefaultMockWebKitConnection.EE.emit('Debugger.globalObjectCleared');
DefaultMockWebKitConnection.EE.emit('Debugger.scriptParsed', <WebKitProtocol.Debugger.Script>{ scriptId: 'afterRefreshScriptId', url: FILE_NAME });
DefaultMockWebKitConnection.EE.emit('Debugger.breakpointResolved', <WebKitProtocol.Debugger.BreakpointResolvedParams>{ breakpointId: BP_ID + 2, location: { scriptId: 'afterRefreshScriptId' } });
DefaultMockWebKitConnection.EE.emit('Debugger.breakpointResolved', <WebKitProtocol.Debugger.BreakpointResolvedParams>{ breakpointId: BP_ID + 3, location: { scriptId: 'afterRefreshScriptId' } });
DefaultMockChromeConnection.EE.emit('Debugger.globalObjectCleared');
DefaultMockChromeConnection.EE.emit('Debugger.scriptParsed', <Chrome.Debugger.Script>{ scriptId: 'afterRefreshScriptId', url: FILE_NAME });
DefaultMockChromeConnection.EE.emit('Debugger.breakpointResolved', <Chrome.Debugger.BreakpointResolvedParams>{ breakpointId: BP_ID + 2, location: { scriptId: 'afterRefreshScriptId' } });
DefaultMockChromeConnection.EE.emit('Debugger.breakpointResolved', <Chrome.Debugger.BreakpointResolvedParams>{ breakpointId: BP_ID + 3, location: { scriptId: 'afterRefreshScriptId' } });
lines.push(321);
cols.push(123);
expectSetBreakpoint(lines, cols, 'afterRefreshScriptId');
return wkda.setBreakpoints({ source: { path: FILE_NAME }, lines, cols });
}).then(response => {
mockWebKitConnection.verify();
mockChromeConnection.verify();
assert.deepEqual(response, makeExpectedResponse(lines, cols));
});
});
@ -259,7 +262,7 @@ suite('WebKitDebugAdapter', () => {
}
});
DefaultMockWebKitConnection.EE.emit('Console.onMessageAdded', {
DefaultMockChromeConnection.EE.emit('Console.onMessageAdded', {
message: {
source: 'console-api',
level: 'log',
@ -292,15 +295,15 @@ suite('WebKitDebugAdapter', () => {
suite('target close/error/detach', () => { });
});
function attach(wkda: _WebKitDebugAdapter): Promise<void> {
function attach(wkda: _ChromeDebugAdapter): Promise<void> {
return wkda.attach({ port: 9222 });
}
class DefaultMockWebKitConnection {
class DefaultMockChromeConnection {
public static EE = new EventEmitter();
public on(eventName: string, handler: (msg: any) => void): void {
DefaultMockWebKitConnection.EE.on(eventName, handler);
DefaultMockChromeConnection.EE.on(eventName, handler);
}
public attach(port: number): Promise<void> {
@ -308,7 +311,7 @@ class DefaultMockWebKitConnection {
}
}
function instantiateWKDA(): _WebKitDebugAdapter {
const WebKitDebugAdapter: typeof _WebKitDebugAdapter = require(MODULE_UNDER_TEST).WebKitDebugAdapter;
return new WebKitDebugAdapter();
function instantiateWKDA(): _ChromeDebugAdapter {
const ChromeDebugAdapter: typeof _ChromeDebugAdapter = require(MODULE_UNDER_TEST).ChromeDebugAdapter;
return new ChromeDebugAdapter();
}

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

@ -0,0 +1,156 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as mockery from 'mockery';
import * as assert from 'assert';
import * as _path from 'path';
import * as testUtils from '../testUtils';
/** ChromeUtils without mocks - use for type only */
import * as _ChromeUtils from '../../src/chrome/chromeUtils';
let path: typeof _path;
const MODULE_UNDER_TEST = '../../src/chrome/chromeUtils';
suite('ChromeUtils', () => {
function getChromeUtils(): typeof _ChromeUtils {
return require(MODULE_UNDER_TEST);
}
setup(() => {
testUtils.setupUnhandledRejectionListener();
mockery.enable({ useCleanCache: true, warnOnReplace: false });
testUtils.win32Mocks();
mockery.registerMock('fs', { statSync: () => { } });
mockery.registerMock('http', {});
path = require('path');
mockery.registerAllowables([
MODULE_UNDER_TEST,
'url',
'../utils']);
});
teardown(() => {
testUtils.removeUnhandledRejectionListener();
mockery.deregisterAll();
mockery.disable();
});
suite('targetUrlToClientPath()', () => {
const TEST_CLIENT_PATH = 'c:\\site\\scripts\\a.js';
const TEST_TARGET_LOCAL_URL = 'file:///' + TEST_CLIENT_PATH;
const TEST_TARGET_HTTP_URL = 'http://site.com/page/scripts/a.js';
const TEST_WEB_ROOT = 'c:\\site';
test('an empty string is returned for a missing url', () => {
assert.equal(getChromeUtils().targetUrlToClientPath('', ''), '');
});
test('an empty string is returned when the webRoot is missing', () => {
assert.equal(getChromeUtils().targetUrlToClientPath(null, TEST_TARGET_HTTP_URL), '');
});
test('a url without a path returns an empty string', () => {
assert.equal(getChromeUtils().targetUrlToClientPath(TEST_WEB_ROOT, 'http://site.com'), '');
});
test('it searches the disk for a path that exists, built from the url', () => {
const statSync = (aPath: string) => {
if (aPath !== TEST_CLIENT_PATH) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
assert.equal(getChromeUtils().targetUrlToClientPath(TEST_WEB_ROOT, TEST_TARGET_HTTP_URL), TEST_CLIENT_PATH);
});
test(`returns an empty string when it can't resolve a url`, () => {
const statSync = (aPath: string) => {
throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
assert.equal(getChromeUtils().targetUrlToClientPath(TEST_WEB_ROOT, TEST_TARGET_HTTP_URL), '');
});
test('file:/// urls are returned canonicalized', () => {
assert.equal(getChromeUtils().targetUrlToClientPath('', TEST_TARGET_LOCAL_URL), TEST_CLIENT_PATH);
});
test('uri encodings are fixed for file:/// paths', () => {
const clientPath = 'c:\\project\\path with spaces\\script.js';
assert.equal(getChromeUtils().targetUrlToClientPath(TEST_WEB_ROOT, 'file:///' + encodeURI(clientPath)), clientPath);
});
test('uri encodings are fixed in URLs', () => {
const pathSegment = 'path with spaces\\script.js';
const url = 'http:\\' + encodeURIComponent(pathSegment);
assert.equal(getChromeUtils().targetUrlToClientPath(TEST_WEB_ROOT, url), path.join(TEST_WEB_ROOT, pathSegment));
});
});
suite('remoteObjectToValue()', () => {
const TEST_OBJ_ID = 'objectId';
function testRemoteObjectToValue(obj: any, value: string, variableHandleRef?: string, stringify?: boolean): void {
const Utilities = getChromeUtils();
assert.deepEqual(Utilities.remoteObjectToValue(obj, stringify), { value, variableHandleRef });
}
test('bool', () => {
testRemoteObjectToValue({ type: 'boolean', value: true }, 'true');
});
test('string', () => {
let value = 'test string';
testRemoteObjectToValue({ type: 'string', value }, `"${value}"`);
testRemoteObjectToValue({ type: 'string', value }, `${value}`, undefined, /*stringify=*/false);
value = 'test string\r\nwith\nnewlines\n\n';
const expValue = 'test string\\r\\nwith\\nnewlines\\n\\n';
testRemoteObjectToValue({ type: 'string', value }, `"${expValue}"`);
});
test('number', () => {
testRemoteObjectToValue({ type: 'number', value: 1, description: '1' }, '1');
});
test('array', () => {
const description = 'Array[2]';
testRemoteObjectToValue({ type: 'object', description, objectId: TEST_OBJ_ID }, description, TEST_OBJ_ID);
});
test('regexp', () => {
const description = '/^asdf/g';
testRemoteObjectToValue({ type: 'object', description, objectId: TEST_OBJ_ID }, description, TEST_OBJ_ID);
});
test('symbol', () => {
const description = 'Symbol(s)';
testRemoteObjectToValue({ type: 'symbol', description, objectId: TEST_OBJ_ID }, description);
});
test('function', () => {
// ES6 arrow fn
testRemoteObjectToValue({ type: 'function', description: '() => {\n var x = 1;\n var y = 1;\n}', objectId: TEST_OBJ_ID }, '() => { … }');
// named fn
testRemoteObjectToValue({ type: 'function', description: 'function asdf() {\n var z = 5;\n}' }, 'function asdf() { … }');
// anonymous fn
testRemoteObjectToValue({ type: 'function', description: 'function () {\n var z = 5;\n}' }, 'function () { … }');
});
test('undefined', () => {
testRemoteObjectToValue({ type: 'undefined' }, 'undefined');
});
test('null', () => {
testRemoteObjectToValue({ type: 'object', subtype: 'null' }, 'null');
});
});
});

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

@ -5,7 +5,7 @@
import * as assert from 'assert';
import * as testUtils from '../testUtils';
import * as ConsoleHelper from '../../webkit/consoleHelper';
import * as ConsoleHelper from '../../src/chrome/consoleHelper';
suite('ConsoleHelper', () => {
setup(() => {
@ -16,7 +16,7 @@ suite('ConsoleHelper', () => {
testUtils.removeUnhandledRejectionListener();
});
function doAssert(message: WebKitProtocol.Console.Message, expectedText: string, expectedIsError = false): void {
function doAssert(message: Chrome.Console.Message, expectedText: string, expectedIsError = false): void {
assert.deepEqual(ConsoleHelper.formatConsoleMessage(message), { text: expectedText, isError: expectedIsError });
}
@ -61,7 +61,7 @@ suite('ConsoleHelper', () => {
});
/**
* Build the webkit notifications objects for various console APIs.
* Build the Chrome notifications objects for various console APIs.
*/
namespace Console {
/**
@ -70,7 +70,7 @@ namespace Console {
* @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 {
function makeMockMessage(type: string, params: any[], overrideProps?: any): Chrome.Console.Message {
const message = {
source: 'console-api',
level: 'log',
@ -102,16 +102,16 @@ namespace Console {
return message;
}
export function makeLog(...params: any[]): WebKitProtocol.Console.Message {
export function makeLog(...params: any[]): Chrome.Console.Message {
return makeMockMessage('log', params);
}
export function makeAssert(...params: any[]): WebKitProtocol.Console.Message {
export function makeAssert(...params: any[]): Chrome.Console.Message {
const fakeStackTrace = [{ url: '/script/a.js', lineNumber: 4, functionName: 'myFn' }];
return makeMockMessage('assert', params, { level: 'error', stackTrace: fakeStackTrace });
}
export function makeNetworkLog(text: string, url: string): WebKitProtocol.Console.Message {
export function makeNetworkLog(text: string, url: string): Chrome.Console.Message {
return makeMockMessage('log', [text], { source: 'network', url, level: 'error' });
}
}

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

@ -4,7 +4,7 @@
import {DebugProtocol} from 'vscode-debugprotocol';
import {IStackTraceResponseBody} from '../webkit/webKitAdapterInterfaces';
import {IStackTraceResponseBody} from '../src/chrome/debugAdapterInterfaces';
import * as path from 'path';
import * as sinon from 'sinon';

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

@ -5,7 +5,7 @@
import * as assert from 'assert';
import * as testUtils from '../testUtils';
import {AdapterProxy} from '../../adapter/adapterProxy';
import {AdapterProxy} from '../../src/adapterProxy';
suite('AdapterProxy', () => {
setup(() => {

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

@ -6,8 +6,8 @@ import * as assert from 'assert';
import {DebugProtocol} from 'vscode-debugprotocol';
import {ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../../webkit/webKitAdapterInterfaces';
import { LineNumberTransformer } from '../../adapter/lineNumberTransformer';
import {ISetBreakpointsResponseBody, IStackTraceResponseBody} from '../../src/chrome/debugAdapterInterfaces';
import { LineNumberTransformer } from '../../src/transformers/lineNumberTransformer';
import * as testUtils from '../testUtils';
function createTransformer(clientLinesStartAt1: boolean, targetLinesStartAt1: boolean): LineNumberTransformer {

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

@ -7,9 +7,9 @@ import * as mockery from 'mockery';
import * as testUtils from '../testUtils';
import {PathTransformer as _PathTransformer} from '../../adapter/pathTransformer';
import {PathTransformer as _PathTransformer} from '../../src/transformers/pathTransformer';
const MODULE_UNDER_TEST = '../../adapter/pathTransformer';
const MODULE_UNDER_TEST = '../../src/transformers/pathTransformer';
function createTransformer(): _PathTransformer {
return new (require(MODULE_UNDER_TEST).PathTransformer)();
}
@ -20,6 +20,7 @@ suite('PathTransformer', () => {
let utilsMock: Sinon.SinonMock;
let chromeUtilsMock: Sinon.SinonMock;
let transformer: _PathTransformer;
setup(() => {
@ -28,7 +29,8 @@ suite('PathTransformer', () => {
mockery.registerAllowables([MODULE_UNDER_TEST, 'path']);
// Mock the utils functions
utilsMock = testUtils.createRegisteredSinonMock('../webkit/utilities', testUtils.getDefaultUtilitiesMock());
utilsMock = testUtils.createRegisteredSinonMock('../utils', testUtils.getDefaultUtilitiesMock());
chromeUtilsMock = testUtils.createRegisteredSinonMock('../chrome/chromeUtils', testUtils.getDefaultUtilitiesMock());
transformer = createTransformer();
});
@ -48,7 +50,7 @@ suite('PathTransformer', () => {
});
test('resolves correctly when it can map the client script to the target script', () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.withExactArgs(/*webRoot=*/undefined, TARGET_URL).returns(CLIENT_PATH);
utilsMock.expects('canonicalizeUrl')
.returns(CLIENT_PATH);
@ -63,7 +65,7 @@ suite('PathTransformer', () => {
});
test(`doesn't resolve until it can map the client script to the target script`, () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.withExactArgs(/*webRoot=*/undefined, TARGET_URL).returns(CLIENT_PATH);
utilsMock.expects('canonicalizeUrl')
.twice()
@ -98,7 +100,7 @@ suite('PathTransformer', () => {
suite('scriptParsed', () => {
test('modifies args.source.path of the script parsed event when the file can be mapped', () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.withExactArgs(/*webRoot=*/undefined, TARGET_URL).returns(CLIENT_PATH);
const scriptParsedArgs = <any>{ body: { scriptUrl: TARGET_URL } };
@ -108,7 +110,7 @@ suite('PathTransformer', () => {
});
test(`doesn't modify args.source.path when the file can't be mapped`, () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.withExactArgs(/*webRoot=*/undefined, TARGET_URL).returns('');
const scriptParsedArgs = <any>{ body: { scriptUrl: TARGET_URL } };
@ -122,7 +124,7 @@ suite('PathTransformer', () => {
const RUNTIME_LINES = [2, 5, 8];
test('modifies the source path and clears sourceReference when the file can be mapped', () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.thrice()
.withExactArgs(undefined, TARGET_URL).returns(CLIENT_PATH);
@ -134,7 +136,7 @@ suite('PathTransformer', () => {
});
test(`doesn't modify the source path or clear the sourceReference when the file can't be mapped`, () => {
utilsMock.expects('webkitUrlToClientPath')
chromeUtilsMock.expects('targetUrlToClientPath')
.thrice()
.withExactArgs(undefined, TARGET_URL).returns('');

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

@ -7,9 +7,10 @@ import * as mockery from 'mockery';
import * as testUtils from '../../testUtils';
import {getAbsSourceRoot as _getAbsSourceRoot} from '../../../adapter/sourceMaps/pathUtilities';
// Don't use - imported without mocks for type
import {getAbsSourceRoot as _getAbsSourceRoot} from '../../../src/transformers/sourceMaps/pathUtilities';
const MODULE_UNDER_TEST = '../../../adapter/sourceMaps/pathUtilities';
const MODULE_UNDER_TEST = '../../../src/transformers/sourceMaps/pathUtilities';
suite('PathUtilities', () => {
setup(() => {
@ -17,7 +18,7 @@ suite('PathUtilities', () => {
// Set up mockery
mockery.enable({ warnOnReplace: false, useCleanCache: true });
mockery.registerAllowables([MODULE_UNDER_TEST, 'url', 'http', 'fs', '../../webkit/utilities']);
mockery.registerAllowables([MODULE_UNDER_TEST, 'url', 'http', 'fs', '../../utils']);
testUtils.win32Mocks();
});

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

@ -7,12 +7,12 @@ import {DebugProtocol} from 'vscode-debugprotocol';
import * as assert from 'assert';
import * as mockery from 'mockery';
import {ISetBreakpointsResponseBody, IStackTraceResponseBody,
ILaunchRequestArgs, ISetBreakpointsArgs, IBreakpoint} from '../../../webkit/webKitAdapterInterfaces';
import {ISetBreakpointsResponseBody,
ILaunchRequestArgs, ISetBreakpointsArgs, IBreakpoint} from '../../../src/chrome/debugAdapterInterfaces';
import * as testUtils from '../../testUtils';
import { ISourceMaps, MappingResult } from '../../../adapter/sourceMaps/sourceMaps';
import { ISourceMaps, MappingResult } from '../../../src/transformers/sourceMaps/sourceMaps';
const MODULE_UNDER_TEST = '../../../adapter/sourceMaps/sourceMapTransformer';
const MODULE_UNDER_TEST = '../../../src/transformers/sourceMaps/sourceMapTransformer';
const AUTHORED_PATH = 'c:/project/authored.ts';
const RUNTIME_PATH = 'c:/project/runtime.js';
const AUTHORED_LINES = [1, 2, 3];
@ -25,7 +25,7 @@ const RUNTIME_LINES2 = [78, 81];
const RUNTIME_COLS2 = [0, 1];
// Not mocked, use for type only
import {SourceMapTransformer as _SourceMapTransformer} from '../../../adapter/sourceMaps/sourceMapTransformer';
import {SourceMapTransformer as _SourceMapTransformer} from '../../../src/transformers/sourceMaps/sourceMapTransformer';
suite('SourceMapTransformer', () => {
let utilsMock: Sinon.SinonMock;
@ -36,7 +36,7 @@ suite('SourceMapTransformer', () => {
// Set up mockery
mockery.enable({ warnOnReplace: false, useCleanCache: true });
utilsMock = testUtils.createRegisteredSinonMock('../../webkit/utilities', testUtils.getDefaultUtilitiesMock());
utilsMock = testUtils.createRegisteredSinonMock('../../utils', testUtils.getDefaultUtilitiesMock());
mockery.registerAllowables([MODULE_UNDER_TEST, 'path']);
});
@ -112,7 +112,7 @@ suite('SourceMapTransformer', () => {
const args = createArgs(RUNTIME_PATH, RUNTIME_LINES);
const expected = createArgs(RUNTIME_PATH, RUNTIME_LINES);
return getTransformer(false).setBreakpoints(args, 0).then(() => {
return getTransformer(/*sourceMaps=*/false).setBreakpoints(args, 0).then(() => {
assert.deepEqual(args, expected);
});
});

322
test/utils.test.ts Normal file
Просмотреть файл

@ -0,0 +1,322 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as mockery from 'mockery';
import * as assert from 'assert';
import * as _path from 'path';
import * as testUtils from './testUtils';
/** Utils without mocks - use for type only */
import * as _Utils from '../src/utils';
let path: typeof _path;
const MODULE_UNDER_TEST = '../src/utils';
suite('Utils', () => {
function getUtils(): typeof _Utils {
return require(MODULE_UNDER_TEST);
}
setup(() => {
testUtils.setupUnhandledRejectionListener();
mockery.enable({ useCleanCache: true, warnOnReplace: false });
testUtils.win32Mocks();
mockery.registerMock('fs', { statSync: () => { } });
mockery.registerMock('http', {});
path = require('path');
mockery.registerAllowables([
'url', MODULE_UNDER_TEST]);
});
teardown(() => {
testUtils.removeUnhandledRejectionListener();
mockery.deregisterAll();
mockery.disable();
});
suite('getPlatform()/getBrowserPath()', () => {
test('osx', () => {
mockery.registerMock('os', { platform: () => 'darwin' });
const Utils = getUtils();
assert.equal(Utils.getPlatform(), Utils.Platform.OSX);
assert.equal(
Utils.getBrowserPath(),
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome');
});
test('win', () => {
// Overwrite the statSync mock to say the x86 path doesn't exist
const statSync = (aPath: string) => {
if (aPath.indexOf('(x86)') >= 0) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
const Utils = getUtils();
assert.equal(Utils.getPlatform(), Utils.Platform.Windows);
assert.equal(
Utils.getBrowserPath(),
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe');
});
test('winx86', () => {
const Utils = getUtils();
assert.equal(Utils.getPlatform(), Utils.Platform.Windows);
assert.equal(
Utils.getBrowserPath(),
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe');
});
test('linux', () => {
mockery.registerMock('os', { platform: () => 'linux' });
const Utils = getUtils();
assert.equal(Utils.getPlatform(), Utils.Platform.Linux);
assert.equal(
Utils.getBrowserPath(),
'/usr/bin/google-chrome');
});
test('freebsd (default to Linux for anything unknown)', () => {
mockery.registerMock('os', { platform: () => 'freebsd' });
const Utils = getUtils();
assert.equal(Utils.getPlatform(), Utils.Platform.Linux);
assert.equal(
Utils.getBrowserPath(),
'/usr/bin/google-chrome');
});
});
suite('existsSync()', () => {
test('it returns false when statSync throws', () => {
const statSync = (aPath: string) => {
if (aPath.indexOf('notfound') >= 0) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
const Utils = getUtils();
assert.equal(Utils.existsSync('exists'), true);
assert.equal(Utils.existsSync('thisfilenotfound'), false);
});
});
suite('reversedArr()', () => {
test('it does not modify the input array', () => {
let arr = [2, 4, 6];
getUtils().reversedArr(arr);
assert.deepEqual(arr, [2, 4, 6]);
arr = [1];
getUtils().reversedArr(arr);
assert.deepEqual(arr, [1]);
});
test('it reverses the array', () => {
assert.deepEqual(getUtils().reversedArr([1, 3, 5, 7]), [7, 5, 3, 1]);
assert.deepEqual(
getUtils().reversedArr([-1, 'hello', null, undefined, [1, 2]]),
[[1, 2], undefined, null, 'hello', -1]);
});
});
suite('promiseTimeout()', () => {
test('when given a promise it fails if the promise never resolves', () => {
return getUtils().promiseTimeout(new Promise(() => { }), 5).then(
() => assert.fail('This promise should fail'),
e => { }
);
});
test('when given a promise it succeeds if the promise resolves', () => {
return getUtils().promiseTimeout(Promise.resolve('test'), 5).then(
result => {
assert.equal(result, 'test');
},
e => assert.fail('This promise should pass')
);
});
test('when not given a promise it resolves', () => {
return getUtils().promiseTimeout(null, 5).then(
null,
() => assert.fail('This promise should pass')
);
});
});
suite('retryAsync()', () => {
test('when the function passes, it resolves with the value', () => {
return getUtils().retryAsync(() => Promise.resolve('pass'), /*timeoutMs=*/5).then(
result => {
assert.equal(result, 'pass');
},
e => {
assert.fail('This should have passed');
});
});
test('when the function fails, it rejects', () => {
return getUtils().retryAsync(() => getUtils().errP('fail'), /*timeoutMs=*/5)
.then(
() => assert.fail('This promise should fail'),
e => assert.equal(e.message, 'fail'));
});
});
suite('canonicalizeUrl()', () => {
function testCanUrl(inUrl: string, expectedUrl: string): void {
const Utils = getUtils();
assert.equal(Utils.canonicalizeUrl(inUrl), expectedUrl);
}
test('enforces path.sep slash', () => {
testCanUrl('c:\\thing\\file.js', 'c:\\thing\\file.js');
testCanUrl('c:/thing/file.js', 'c:\\thing\\file.js');
});
test('removes file:///', () => {
testCanUrl('file:///c:/file.js', 'c:\\file.js');
});
test('ensures local path starts with / on OSX', () => {
mockery.registerMock('os', { platform: () => 'darwin' });
testCanUrl('file:///Users/scripts/app.js', '/Users/scripts/app.js');
});
test('force lowercase drive letter on Win to match VS Code', () => {
// note default 'os' mock is win32
testCanUrl('file:///D:/FILE.js', 'd:\\FILE.js');
});
test('http:// url - no change', () => {
const url = 'http://site.com/My/Cool/Site/script.js?stuff';
testCanUrl(url, url);
});
test('strips trailing slash', () => {
testCanUrl('http://site.com/', 'http://site.com');
});
});
suite('fixDriveLetterAndSlashes', () => {
test('works for c:/... cases', () => {
assert.equal(getUtils().fixDriveLetterAndSlashes('C:/path/stuff'), 'c:\\path\\stuff');
assert.equal(getUtils().fixDriveLetterAndSlashes('c:/path\\stuff'), 'c:\\path\\stuff');
assert.equal(getUtils().fixDriveLetterAndSlashes('C:\\path'), 'c:\\path');
assert.equal(getUtils().fixDriveLetterAndSlashes('C:\\'), 'c:\\');
});
test('works for file:/// cases', () => {
assert.equal(getUtils().fixDriveLetterAndSlashes('file:///C:/path/stuff'), 'file:///c:\\path\\stuff');
assert.equal(getUtils().fixDriveLetterAndSlashes('file:///c:/path\\stuff'), 'file:///c:\\path\\stuff');
assert.equal(getUtils().fixDriveLetterAndSlashes('file:///C:\\path'), 'file:///c:\\path');
assert.equal(getUtils().fixDriveLetterAndSlashes('file:///C:\\'), 'file:///c:\\');
});
});
suite('getUrl', () => {
const URL = 'http://testsite.com/testfile';
const RESPONSE = 'response';
function registerMockHTTP(dataResponses: string[], error?: string): void {
mockery.registerMock('http', { get: (url, callback) => {
assert.equal(url, URL);
if (error) {
return { on:
(eventName, eventCallback) => {
if (eventName === 'error') {
eventCallback(error);
}
}};
} else {
callback({
statusCode: 200,
on: (eventName, eventCallback) => {
if (eventName === 'data') {
dataResponses.forEach(eventCallback);
} else if (eventName === 'end') {
setTimeout(eventCallback, 0);
}
}});
return { on: () => { }};
}
}});
}
test('combines chunks', () => {
// Create a mock http.get that provides data in two chunks
registerMockHTTP(['res', 'ponse']);
return getUtils().getURL(URL).then(response => {
assert.equal(response, RESPONSE);
});
});
test('rejects the promise on an error', () => {
registerMockHTTP(undefined, 'fail');
return getUtils().getURL(URL).then(
response => {
assert.fail('Should not be resolved');
},
e => {
assert.equal(e, 'fail');
});
});
});
suite('isURL', () => {
function assertIsURL(url: string): void {
assert(getUtils().isURL(url));
}
function assertNotURL(url: string): void {
assert(!getUtils().isURL(url));
}
test('returns true for URLs', () => {
assertIsURL('http://localhost');
assertIsURL('http://mysite.com');
assertIsURL('file:///c:/project/code.js');
assertIsURL('webpack:///webpack/webpackthing');
assertIsURL('https://a.b.c:123/asdf?fsda');
});
test('returns false for not-URLs', () => {
assertNotURL('a');
assertNotURL('/project/code.js');
assertNotURL('c:/project/code.js');
assertNotURL('abc123!@#');
assertNotURL('');
assertNotURL(null);
});
});
suite('lstrip', () => {
test('does what it says', () => {
assert.equal(getUtils().lstrip('test', 'te'), 'st');
assert.equal(getUtils().lstrip('asdf', ''), 'asdf');
assert.equal(getUtils().lstrip('asdf', null), 'asdf');
assert.equal(getUtils().lstrip('asdf', 'asdf'), '');
assert.equal(getUtils().lstrip('asdf', '123'), 'asdf');
assert.equal(getUtils().lstrip('asdf', 'sdf'), 'asdf');
});
});
suite('pathToFileURL', () => {
test('converts windows-style paths', () => {
assert.equal(getUtils().pathToFileURL('c:\\code\\app.js'), 'file:///c:/code/app.js');
});
test('converts unix-style paths', () => {
assert.equal(getUtils().pathToFileURL('/code/app.js'), 'file:///code/app.js');
});
test('encodes as URI and forces forwards slash', () => {
assert.equal(getUtils().pathToFileURL('c:\\path with spaces\\blah.js'), 'file:///c:/path%20with%20spaces/blah.js');
});
});
});

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

@ -1,435 +0,0 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as mockery from 'mockery';
import * as assert from 'assert';
import * as _path from 'path';
import * as testUtils from '../testUtils';
/** Utilities without mocks - use for type only */
import * as _Utilities from '../../webkit/utilities';
let path: typeof _path;
const MODULE_UNDER_TEST = '../../webkit/utilities';
suite('Utilities', () => {
function getUtilities(): typeof _Utilities {
return require(MODULE_UNDER_TEST);
}
setup(() => {
testUtils.setupUnhandledRejectionListener();
mockery.enable({ useCleanCache: true, warnOnReplace: false });
testUtils.win32Mocks();
mockery.registerMock('fs', { statSync: () => { } });
mockery.registerMock('http', {});
path = require('path');
mockery.registerAllowables([
'url', MODULE_UNDER_TEST]);
});
teardown(() => {
testUtils.removeUnhandledRejectionListener();
mockery.deregisterAll();
mockery.disable();
});
suite('getPlatform()/getBrowserPath()', () => {
test('osx', () => {
mockery.registerMock('os', { platform: () => 'darwin' });
const Utilities = getUtilities();
assert.equal(Utilities.getPlatform(), Utilities.Platform.OSX);
assert.equal(
Utilities.getBrowserPath(),
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome');
});
test('win', () => {
// Overwrite the statSync mock to say the x86 path doesn't exist
const statSync = (aPath: string) => {
if (aPath.indexOf('(x86)') >= 0) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
const Utilities = getUtilities();
assert.equal(Utilities.getPlatform(), Utilities.Platform.Windows);
assert.equal(
Utilities.getBrowserPath(),
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe');
});
test('winx86', () => {
const Utilities = getUtilities();
assert.equal(Utilities.getPlatform(), Utilities.Platform.Windows);
assert.equal(
Utilities.getBrowserPath(),
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe');
});
test('linux', () => {
mockery.registerMock('os', { platform: () => 'linux' });
const Utilities = getUtilities();
assert.equal(Utilities.getPlatform(), Utilities.Platform.Linux);
assert.equal(
Utilities.getBrowserPath(),
'/usr/bin/google-chrome');
});
test('freebsd (default to Linux for anything unknown)', () => {
mockery.registerMock('os', { platform: () => 'freebsd' });
const Utilities = getUtilities();
assert.equal(Utilities.getPlatform(), Utilities.Platform.Linux);
assert.equal(
Utilities.getBrowserPath(),
'/usr/bin/google-chrome');
});
});
suite('existsSync()', () => {
test('it returns false when statSync throws', () => {
const statSync = (aPath: string) => {
if (aPath.indexOf('notfound') >= 0) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
const Utilities = getUtilities();
assert.equal(Utilities.existsSync('exists'), true);
assert.equal(Utilities.existsSync('thisfilenotfound'), false);
});
});
suite('reversedArr()', () => {
test('it does not modify the input array', () => {
let arr = [2, 4, 6];
getUtilities().reversedArr(arr);
assert.deepEqual(arr, [2, 4, 6]);
arr = [1];
getUtilities().reversedArr(arr);
assert.deepEqual(arr, [1]);
});
test('it reverses the array', () => {
assert.deepEqual(getUtilities().reversedArr([1, 3, 5, 7]), [7, 5, 3, 1]);
assert.deepEqual(
getUtilities().reversedArr([-1, 'hello', null, undefined, [1, 2]]),
[[1, 2], undefined, null, 'hello', -1]);
});
});
suite('promiseTimeout()', () => {
test('when given a promise it fails if the promise never resolves', () => {
return getUtilities().promiseTimeout(new Promise(() => { }), 5).then(
() => assert.fail('This promise should fail'),
e => { }
);
});
test('when given a promise it succeeds if the promise resolves', () => {
return getUtilities().promiseTimeout(Promise.resolve('test'), 5).then(
result => {
assert.equal(result, 'test');
},
e => assert.fail('This promise should pass')
);
});
test('when not given a promise it resolves', () => {
return getUtilities().promiseTimeout(null, 5).then(
null,
() => assert.fail('This promise should pass')
);
});
});
suite('retryAsync()', () => {
test('when the function passes, it resolves with the value', () => {
return getUtilities().retryAsync(() => Promise.resolve('pass'), /*timeoutMs=*/5).then(
result => {
assert.equal(result, 'pass');
},
e => {
assert.fail('This should have passed');
});
});
test('when the function fails, it rejects', () => {
return getUtilities().retryAsync(() => getUtilities().errP('fail'), /*timeoutMs=*/5)
.then(
() => assert.fail('This promise should fail'),
e => assert.equal(e.message, 'fail'));
});
});
suite('webkitUrlToClientPath()', () => {
const TEST_CLIENT_PATH = 'c:\\site\\scripts\\a.js';
const TEST_WEBKIT_LOCAL_URL = 'file:///' + TEST_CLIENT_PATH;
const TEST_WEBKIT_HTTP_URL = 'http://site.com/page/scripts/a.js';
const TEST_WEB_ROOT = 'c:\\site';
test('an empty string is returned for a missing url', () => {
assert.equal(getUtilities().webkitUrlToClientPath('', ''), '');
});
test('an empty string is returned when the webRoot is missing', () => {
assert.equal(getUtilities().webkitUrlToClientPath(null, TEST_WEBKIT_HTTP_URL), '');
});
test('a url without a path returns an empty string', () => {
assert.equal(getUtilities().webkitUrlToClientPath(TEST_WEB_ROOT, 'http://site.com'), '');
});
test('it searches the disk for a path that exists, built from the url', () => {
const statSync = (aPath: string) => {
if (aPath !== TEST_CLIENT_PATH) throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
assert.equal(getUtilities().webkitUrlToClientPath(TEST_WEB_ROOT, TEST_WEBKIT_HTTP_URL), TEST_CLIENT_PATH);
});
test(`returns an empty string when it can't resolve a url`, () => {
const statSync = (aPath: string) => {
throw new Error('Not found');
};
mockery.registerMock('fs', { statSync });
assert.equal(getUtilities().webkitUrlToClientPath(TEST_WEB_ROOT, TEST_WEBKIT_HTTP_URL), '');
});
test('file:/// urls are returned canonicalized', () => {
assert.equal(getUtilities().webkitUrlToClientPath('', TEST_WEBKIT_LOCAL_URL), TEST_CLIENT_PATH);
});
test('uri encodings are fixed for file:/// paths', () => {
const clientPath = 'c:\\project\\path with spaces\\script.js';
assert.equal(getUtilities().webkitUrlToClientPath(TEST_WEB_ROOT, 'file:///' + encodeURI(clientPath)), clientPath);
});
test('uri encodings are fixed in URLs', () => {
const pathSegment = 'path with spaces\\script.js';
const url = 'http:\\' + encodeURIComponent(pathSegment);
assert.equal(getUtilities().webkitUrlToClientPath(TEST_WEB_ROOT, url), path.join(TEST_WEB_ROOT, pathSegment));
});
});
suite('canonicalizeUrl()', () => {
function testCanUrl(inUrl: string, expectedUrl: string): void {
const Utilities = getUtilities();
assert.equal(Utilities.canonicalizeUrl(inUrl), expectedUrl);
}
test('enforces path.sep slash', () => {
testCanUrl('c:\\thing\\file.js', 'c:\\thing\\file.js');
testCanUrl('c:/thing/file.js', 'c:\\thing\\file.js');
});
test('removes file:///', () => {
testCanUrl('file:///c:/file.js', 'c:\\file.js');
});
test('ensures local path starts with / on OSX', () => {
mockery.registerMock('os', { platform: () => 'darwin' });
testCanUrl('file:///Users/scripts/app.js', '/Users/scripts/app.js');
});
test('force lowercase drive letter on Win to match VS Code', () => {
// note default 'os' mock is win32
testCanUrl('file:///D:/FILE.js', 'd:\\FILE.js');
});
test('http:// url - no change', () => {
const url = 'http://site.com/My/Cool/Site/script.js?stuff';
testCanUrl(url, url);
});
test('strips trailing slash', () => {
testCanUrl('http://site.com/', 'http://site.com');
});
});
suite('fixDriveLetterAndSlashes', () => {
test('works for c:/... cases', () => {
assert.equal(getUtilities().fixDriveLetterAndSlashes('C:/path/stuff'), 'c:\\path\\stuff');
assert.equal(getUtilities().fixDriveLetterAndSlashes('c:/path\\stuff'), 'c:\\path\\stuff');
assert.equal(getUtilities().fixDriveLetterAndSlashes('C:\\path'), 'c:\\path');
assert.equal(getUtilities().fixDriveLetterAndSlashes('C:\\'), 'c:\\');
});
test('works for file:/// cases', () => {
assert.equal(getUtilities().fixDriveLetterAndSlashes('file:///C:/path/stuff'), 'file:///c:\\path\\stuff');
assert.equal(getUtilities().fixDriveLetterAndSlashes('file:///c:/path\\stuff'), 'file:///c:\\path\\stuff');
assert.equal(getUtilities().fixDriveLetterAndSlashes('file:///C:\\path'), 'file:///c:\\path');
assert.equal(getUtilities().fixDriveLetterAndSlashes('file:///C:\\'), 'file:///c:\\');
});
});
suite('remoteObjectToValue()', () => {
const TEST_OBJ_ID = 'objectId';
function testRemoteObjectToValue(obj: any, value: string, variableHandleRef?: string, stringify?: boolean): void {
const Utilities = getUtilities();
assert.deepEqual(Utilities.remoteObjectToValue(obj, stringify), { value, variableHandleRef });
}
test('bool', () => {
testRemoteObjectToValue({ type: 'boolean', value: true }, 'true');
});
test('string', () => {
let value = 'test string';
testRemoteObjectToValue({ type: 'string', value }, `"${value}"`);
testRemoteObjectToValue({ type: 'string', value }, `${value}`, undefined, /*stringify=*/false);
value = 'test string\r\nwith\nnewlines\n\n';
const expValue = 'test string\\r\\nwith\\nnewlines\\n\\n';
testRemoteObjectToValue({ type: 'string', value }, `"${expValue}"`);
});
test('number', () => {
testRemoteObjectToValue({ type: 'number', value: 1, description: '1' }, '1');
});
test('array', () => {
const description = 'Array[2]';
testRemoteObjectToValue({ type: 'object', description, objectId: TEST_OBJ_ID }, description, TEST_OBJ_ID);
});
test('regexp', () => {
const description = '/^asdf/g';
testRemoteObjectToValue({ type: 'object', description, objectId: TEST_OBJ_ID }, description, TEST_OBJ_ID);
});
test('symbol', () => {
const description = 'Symbol(s)';
testRemoteObjectToValue({ type: 'symbol', description, objectId: TEST_OBJ_ID }, description);
});
test('function', () => {
// ES6 arrow fn
testRemoteObjectToValue({ type: 'function', description: '() => {\n var x = 1;\n var y = 1;\n}', objectId: TEST_OBJ_ID }, '() => { … }');
// named fn
testRemoteObjectToValue({ type: 'function', description: 'function asdf() {\n var z = 5;\n}' }, 'function asdf() { … }');
// anonymous fn
testRemoteObjectToValue({ type: 'function', description: 'function () {\n var z = 5;\n}' }, 'function () { … }');
});
test('undefined', () => {
testRemoteObjectToValue({ type: 'undefined' }, 'undefined');
});
test('null', () => {
testRemoteObjectToValue({ type: 'object', subtype: 'null' }, 'null');
});
});
suite('getUrl', () => {
const URL = 'http://testsite.com/testfile';
const RESPONSE = 'response';
function registerMockHTTP(dataResponses: string[], error?: string): void {
mockery.registerMock('http', { get: (url, callback) => {
assert.equal(url, URL);
if (error) {
return { on:
(eventName, eventCallback) => {
if (eventName === 'error') {
eventCallback(error);
}
}};
} else {
callback({
statusCode: 200,
on: (eventName, eventCallback) => {
if (eventName === 'data') {
dataResponses.forEach(eventCallback);
} else if (eventName === 'end') {
setTimeout(eventCallback, 0);
}
}});
return { on: () => { }};
}
}});
}
test('combines chunks', () => {
// Create a mock http.get that provides data in two chunks
registerMockHTTP(['res', 'ponse']);
return getUtilities().getURL(URL).then(response => {
assert.equal(response, RESPONSE);
});
});
test('rejects the promise on an error', () => {
registerMockHTTP(undefined, 'fail');
return getUtilities().getURL(URL).then(
response => {
assert.fail('Should not be resolved');
},
e => {
assert.equal(e, 'fail');
});
});
});
suite('isURL', () => {
function assertIsURL(url: string): void {
assert(getUtilities().isURL(url));
}
function assertNotURL(url: string): void {
assert(!getUtilities().isURL(url));
}
test('returns true for URLs', () => {
assertIsURL('http://localhost');
assertIsURL('http://mysite.com');
assertIsURL('file:///c:/project/code.js');
assertIsURL('webpack:///webpack/webpackthing');
assertIsURL('https://a.b.c:123/asdf?fsda');
});
test('returns false for not-URLs', () => {
assertNotURL('a');
assertNotURL('/project/code.js');
assertNotURL('c:/project/code.js');
assertNotURL('abc123!@#');
assertNotURL('');
assertNotURL(null);
});
});
suite('lstrip', () => {
test('does what it says', () => {
assert.equal(getUtilities().lstrip('test', 'te'), 'st');
assert.equal(getUtilities().lstrip('asdf', ''), 'asdf');
assert.equal(getUtilities().lstrip('asdf', null), 'asdf');
assert.equal(getUtilities().lstrip('asdf', 'asdf'), '');
assert.equal(getUtilities().lstrip('asdf', '123'), 'asdf');
assert.equal(getUtilities().lstrip('asdf', 'sdf'), 'asdf');
});
});
suite('pathToFileURL', () => {
test('converts windows-style paths', () => {
assert.equal(getUtilities().pathToFileURL('c:\\code\\app.js'), 'file:///c:/code/app.js');
});
test('converts unix-style paths', () => {
assert.equal(getUtilities().pathToFileURL('/code/app.js'), 'file:///code/app.js');
});
test('encodes as URI and forces forwards slash', () => {
assert.equal(getUtilities().pathToFileURL('c:\\path with spaces\\blah.js'), 'file:///c:/path%20with%20spaces/blah.js');
});
});
});