diff --git a/test/webkit/utilities.test.ts b/test/webkit/utilities.test.ts index 48789dd..45d6a65 100644 --- a/test/webkit/utilities.test.ts +++ b/test/webkit/utilities.test.ts @@ -2,6 +2,7 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import * as sinon from 'sinon'; import * as mockery from 'mockery'; import * as assert from 'assert'; @@ -14,9 +15,9 @@ suite('Utilities', () => { mockery.enable({ useCleanCache: true, warnOnReplace: false }); mockery.registerMock('fs', { statSync: () => { } }); mockery.registerMock('os', { platform: () => 'win32' }); - mockery.registerMock('url', { }); - mockery.registerMock('path', { }); - mockery.registerAllowable(MODULE_UNDER_TEST); + + mockery.registerAllowables([ + 'url', 'path', MODULE_UNDER_TEST]); }); teardown(() => { @@ -178,4 +179,82 @@ suite('Utilities', () => { }); }); }); + + suite('webkitUrlToClientUrl()', () => { + 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_CWD = 'c:/site'; + + function Utilities(): typeof _Utilities { + return require(MODULE_UNDER_TEST); + } + + test('file:/// urls are returned canonicalized', () => { + assert.equal(Utilities().webkitUrlToClientUrl('', TEST_WEBKIT_LOCAL_URL), TEST_CLIENT_PATH); + }); + + test('an empty string is returned for a missing url', () => { + assert.equal(Utilities().webkitUrlToClientUrl('', ''), ''); + }); + + test('an empty string is returned when the cwd is missing', () => { + assert.equal(Utilities().webkitUrlToClientUrl(null, TEST_WEBKIT_HTTP_URL), ''); + }); + + test('a url without a path returns an empty string', () => { + assert.equal(Utilities().webkitUrlToClientUrl(TEST_CWD, 'http://site.com'), ''); + }); + + test('it searches the disk for a path that exists, built from the url', () => { + const statSync = (path: string) => { + if (path !== TEST_CLIENT_PATH) throw new Error('Not found'); + }; + mockery.registerMock('fs', { statSync }); + assert.equal(Utilities().webkitUrlToClientUrl(TEST_CWD, TEST_WEBKIT_HTTP_URL), TEST_CLIENT_PATH); + }); + + test(`returns an empty string when it can't resolve a url`, () => { + const statSync = (path: string) => { + throw new Error('Not found'); + }; + mockery.registerMock('fs', { statSync }); + const Utilities = require(MODULE_UNDER_TEST); + assert.equal(Utilities.webkitUrlToClientUrl(TEST_CWD, TEST_WEBKIT_HTTP_URL), ''); + }); + }); + + suite('canonicalizeUrl()', () => { + function testCanUrl(inUrl: string, expectedUrl: string): void { + const Utilities: typeof _Utilities = require(MODULE_UNDER_TEST); + assert.equal(Utilities.canonicalizeUrl(inUrl), expectedUrl); + } + + test('removes file:///', () => { + testCanUrl('file:///c:/file.js', 'c:/file.js'); + }); + + test('enforces forward slash', () => { + 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); + }) + }); }); diff --git a/webkit/utilities.ts b/webkit/utilities.ts index b2112c6..2a6834e 100644 --- a/webkit/utilities.ts +++ b/webkit/utilities.ts @@ -160,7 +160,9 @@ export class Logger { } /** - * http://localhost/app/scripts/code.js => d:/scripts/code.js + * 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 webkitUrlToClientUrl(cwd: string, url: string): string { @@ -181,15 +183,16 @@ export function webkitUrlToClientUrl(cwd: string, url: string): string { // Search the filesystem under our cwd for the file that best matches the given url const pathName = nodeUrl.parse(canonicalizeUrl(url)).pathname; - if (!pathName) { + if (!pathName || pathName === '/') { return ''; } const pathParts = pathName.split('/'); while (pathParts.length > 0) { const clientUrl = path.join(cwd, pathParts.join('/')); - if (existsSync(clientUrl)) { - return canonicalizeUrl(clientUrl); // path.join will change / to \ + const canClientUrl = canonicalizeUrl(clientUrl); // path.join will change / to \ + if (existsSync(canClientUrl)) { + return canonicalizeUrl(canClientUrl); } pathParts.shift(); @@ -199,18 +202,21 @@ export function webkitUrlToClientUrl(cwd: string, url: string): string { } /** - * Modify a url either from the ODP client or the webkit target to a canonical version for comparing. - * The ODP client can handle urls in this format too. - * file:///d:\\scripts\\code.js => d:/scripts/code.js + * Modify a url either from the client or the webkit target to a platform-independent 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 + * c:\scripts\code.js => c:/scripts/code.js + * http://site.com/scripts/code.js => (no change) */ export function canonicalizeUrl(url: string): string { url = url .replace('file:///', '') .replace(/\\/g, '/'); // \ to / - // Ensure osx path starts with /, it can be removed when file:/// was stripped - if (url[0] !== '/' && getPlatform() === Platform.OSX) { + // Ensure osx path starts with /, it can be removed when file:/// was stripped. + // Don't add if the url still has a protocol + if (url[0] !== '/' && url.indexOf(':') < 0 && getPlatform() === Platform.OSX) { url = '/' + url; }