diff --git a/Gulpfile.js b/Gulpfile.js index 9a082bb4479..cf6f0a7410d 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -54,7 +54,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), { debug: process.env.debug || process.env["debug-brk"] || process.env.d, inspect: process.env.inspect || process.env["inspect-brk"] || process.env.i, host: process.env.TYPESCRIPT_HOST || process.env.host || "node", - browser: process.env.browser || process.env.b || "IE", + browser: process.env.browser || process.env.b || (os.platform() === "win32" ? "edge" : "chrome"), timeout: process.env.timeout || 40000, tests: process.env.test || process.env.tests || process.env.t, runners: process.env.runners || process.env.runner || process.env.ru, diff --git a/Jakefile.js b/Jakefile.js index 2eeccdd7a4f..0cf794edd28 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -922,7 +922,7 @@ desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is task("runtests-browser", ["browserify", nodeServerOutFile], function () { cleanTestDirs(); host = "node"; - var browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE"); + var browser = process.env.browser || process.env.b || (os.platform() === "win32" ? "edge" : "chrome"); var runners = process.env.runners || process.env.runner || process.env.ru; var tests = process.env.test || process.env.tests || process.env.t; var light = process.env.light || false; diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index 22f066bbf85..c33196a99ca 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -1,6 +1,7 @@ /// // tslint:disable:no-null-keyword +import minimist = require("minimist"); import http = require("http"); import fs = require("fs"); import path = require("path"); @@ -20,7 +21,8 @@ const baseUrl = new URL(`http://localhost:${port}/`); const rootDir = path.dirname(__dirname); const useCaseSensitiveFileNames = isFileSystemCaseSensitive(); -let browser = "IE"; +const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome"; +let browser: "edge" | "chrome" | "none" = defaultBrowser; let grep: string | undefined; let verbose = false; @@ -862,50 +864,101 @@ function startServer() { return http.createServer(handleRequest).listen(port); } +const REG_COLUMN_PADDING = 4; + +function queryRegistryValue(keyPath: string, callback: (error: Error | null, value: string) => void) { + const args = ["query", keyPath]; + child_process.execFile("reg", ["query", keyPath, "/ve"], { encoding: "utf8" }, (error, stdout) => { + if (error) return callback(error, null); + + const valueLine = stdout.replace(/^\r\n.+?\r\n|\r\n\r\n$/g, ""); + if (!valueLine) { + return callback(new Error("Unable to retrieve value."), null); + } + + const valueNameColumnOffset = REG_COLUMN_PADDING; + if (valueLine.lastIndexOf("(Default)", valueNameColumnOffset) !== valueNameColumnOffset) { + return callback(new Error("Unable to retrieve value."), null); + } + + const dataTypeColumnOffset = valueNameColumnOffset + "(Default)".length + REG_COLUMN_PADDING; + if (valueLine.lastIndexOf("REG_SZ", dataTypeColumnOffset) !== dataTypeColumnOffset) { + return callback(new Error("Unable to retrieve value."), null); + } + + const valueColumnOffset = dataTypeColumnOffset + "REG_SZ".length + REG_COLUMN_PADDING; + const value = valueLine.slice(valueColumnOffset); + return callback(null, value); + }); +} + +interface Browser { + description: string; + command: string; +} + +function createBrowserFromPath(path: string): Browser { + return { description: path, command: path }; +} + +function getChromePath(callback: (error: Error | null, browser: Browser | string | null) => void) { + switch (os.platform()) { + case "win32": + return queryRegistryValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", (error, value) => { + if (error) return callback(null, "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"); + return callback(null, createBrowserFromPath(value)); + }); + case "darwin": return callback(null, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"); + case "linux": return callback(null, "/opt/google/chrome/chrome"); + default: return callback(new Error(`Chrome location is unknown for platform '${os.platform()}'`), null); + } +} + +function getEdgePath(callback: (error: Error | null, browser: Browser | null) => void) { + switch (os.platform()) { + case "win32": return callback(null, { description: "Microsoft Edge", command: "cmd /c start microsoft-edge:%1" }); + default: return callback(new Error(`Edge location is unknown for platform '${os.platform()}'`), null); + } +} + +function getBrowserPath(callback: (error: Error | null, browser: Browser | null) => void) { + switch (browser) { + case "chrome": return getChromePath(afterGetBrowserPath); + case "edge": return getEdgePath(afterGetBrowserPath); + default: return callback(new Error(`Browser location is unknown for '${browser}'`), null); + } + + function afterGetBrowserPath(error: Error | null, browser: Browser | string | null) { + if (error) return callback(error, null); + if (typeof browser === "object") return callback(null, browser); + return fs.stat(browser, (error, stats) => { + if (!error && stats.isFile()) { + return callback(null, createBrowserFromPath(browser)); + } + if (browser === "chrome") return callback(null, createBrowserFromPath("chrome")); + return callback(new Error(`Browser location is unknown for '${browser}'`), null); + }); + } +} + function startClient(server: http.Server) { let browserPath: string; if (browser === "none") { return; } - if (browser === "chrome") { - let defaultChromePath = ""; - switch (os.platform()) { - case "win32": - defaultChromePath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"; - break; - case "darwin": - defaultChromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; - break; - case "linux": - defaultChromePath = "/opt/google/chrome/chrome"; - break; - default: - console.log(`default Chrome location is unknown for platform '${os.platform()}'`); - break; - } - if (fs.existsSync(defaultChromePath)) { - browserPath = defaultChromePath; + getBrowserPath((error, browser) => { + if (error) return console.error(error); + console.log(`Using browser: ${browser.description}`); + const queryString = grep ? `?grep=${grep}` : ""; + const args = [`http://localhost:${port}/tests/webTestResults.html${queryString}`]; + if (browser.command.indexOf("%") === -1) { + child_process.spawn(browser.command, args); } else { - browserPath = browser; + const command = browser.command.replace(/%(\d+)/g, (_, offset) => args[+offset - 1]); + child_process.exec(command); } - } - else { - const defaultIEPath = "C:/Program Files/Internet Explorer/iexplore.exe"; - if (fs.existsSync(defaultIEPath)) { - browserPath = defaultIEPath; - } - else { - browserPath = browser; - } - } - - console.log(`Using browser: ${browserPath}`); - - const queryString = grep ? `?grep=${grep}` : ""; - const child = child_process.spawn(browserPath, [`http://localhost:${port}/tests/webTestResults.html${queryString}`], { - stdio: "inherit" }); } @@ -913,42 +966,35 @@ function printHelp() { console.log("Runs an http server on port 8888, looking for tests folder in the current directory\n"); console.log("Syntax: node webTestServer.js [browser] [tests] [--verbose]\n"); console.log("Options:"); - console.log(" The browser to launch. One of 'IE', 'chrome', or 'none' (default 'IE')."); + console.log(" The browser to launch. One of 'edge', 'chrome', or 'none' (default 'edge' on Windows, otherwise `chrome`)."); console.log(" A regular expression to pass to Mocha."); console.log(" --verbose Enables verbose logging."); } function parseCommandLine(args: string[]) { - let offset = 0; - for (const arg of args) { - const argLower = arg.toLowerCase(); - if (argLower === "--help") { - printHelp(); - return false; - } - else if (argLower === "--verbose") { - verbose = true; - } - else { - if (offset === 0) { - browser = arg; - } - else if (offset === 1) { - grep = arg; - } - else { - console.log(`Unrecognized argument: ${arg}\n`); - return false; - } - offset++; - } - } - - if (browser !== "IE" && browser !== "chrome" && browser !== "none") { - console.log(`Unrecognized browser '${browser}', expected 'IE' or 'chrome'.`); + const parsed = minimist(args, { boolean: ["help", "verbose"] }); + if (parsed.help) { + printHelp(); return false; } + if (parsed.verbose) { + verbose = true; + } + + const [parsedBrowser = defaultBrowser, parsedGrep, ...unrecognized] = parsed._; + if (parsedBrowser !== "edge" && parsedBrowser !== "chrome" && parsedBrowser !== "none") { + console.log(`Unrecognized browser '${parsedBrowser}', expected 'edge', 'chrome', or 'none'.`); + return false; + } + + if (unrecognized.length > 0) { + console.log(`Unrecognized argument: ${unrecognized[0]}`); + return false; + } + + browser = parsedBrowser; + grep = parsedGrep; return true; }