diff --git a/PublicAPI.md b/PublicAPI.md index 0164703980..6104b7a7c8 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -496,9 +496,25 @@ interface IDebugData { */ interface IDebugOptions { /** - * Defines if bundled Chrome DevTools should be used or specific commit. Valid for iOS only. + * Defines if bundled Chrome DevTools should be used or specific commit. + * Default value is true for Android and false for iOS. */ useBundledDevTools?: boolean; + + /** + * Defines if https://chrome-devtools-frontend.appspot.com should be used instead of chrome-devtools://devtools + * In case it is passed, the value of `useBundledDevTools` is disregarded. + * Default value is false. + */ + useHttpUrl?: boolean; + + /** + * Defines the commit that will be used in cases where remote protocol is required. + * For Android this is the case when useHttpUrl is set to true or useBundledDevTools is set to false. + * For iOS the value is used by default and when useHttpUrl is set to true. + * Default value is 02e6bde1bbe34e43b309d4ef774b1168d25fd024 which corresponds to 55.0.2883 Chrome version + */ + devToolsCommit?: string; } ``` diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 5e9cfde535..50bd31b886 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -68,9 +68,25 @@ interface IDebugOptions { justlaunch?: boolean; /** - * Defines if bundled Chrome DevTools should be used or specific commit. Valid for iOS only. + * Defines if bundled Chrome DevTools should be used or specific commit. + * Default value is true for Android and false for iOS. */ useBundledDevTools?: boolean; + + /** + * Defines if https://chrome-devtools-frontend.appspot.com should be used instead of chrome-devtools://devtools + * In case it is passed, the value of `useBundledDevTools` is disregarded. + * Default value is false. + */ + useHttpUrl?: boolean; + + /** + * Defines the commit that will be used in cases where remote protocol is required. + * For Android this is the case when useHttpUrl is set to true or useBundledDevTools is set to false. + * For iOS the value is used by default and when useHttpUrl is set to true. + * Default value is 02e6bde1bbe34e43b309d4ef774b1168d25fd024 which corresponds to 55.0.2883 Chrome version + */ + devToolsCommit?: string; } /** diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index 04713f1752..ce08d73d4c 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -2,7 +2,7 @@ import { sleep } from "../common/helpers"; import { ChildProcess } from "child_process"; import { DebugServiceBase } from "./debug-service-base"; -class AndroidDebugService extends DebugServiceBase implements IPlatformDebugService { +export class AndroidDebugService extends DebugServiceBase implements IPlatformDebugService { private _device: Mobile.IAndroidDevice = null; private _debuggerClientProcess: ChildProcess; @@ -49,6 +49,14 @@ class AndroidDebugService extends DebugServiceBase implements IPlatformDebugServ return; } + protected getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string { + const debugOpts = _.cloneDeep(debugOptions); + debugOpts.useBundledDevTools = debugOpts.useBundledDevTools === undefined ? true : debugOpts.useBundledDevTools; + + const chromeDebugUrl = super.getChromeDebugUrl(debugOpts, port); + return chromeDebugUrl; + } + private async debugOnEmulator(debugData: IDebugData, debugOptions: IDebugOptions): Promise { // Assure we've detected the emulator as device // For example in case deployOnEmulator had stated new emulator instance @@ -124,11 +132,12 @@ class AndroidDebugService extends DebugServiceBase implements IPlatformDebugServ this.$errors.failWithoutHelp(`The application ${packageName} does not appear to be running on ${deviceId} or is not built with debugging enabled.`); } - let startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"]; + const startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"]; await this.device.adb.executeShellCommand(startDebuggerCommand); - let port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); - return `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${port}`; + const port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); + + return this.getChromeDebugUrl(debugOptions, port); } private detachDebugger(packageName: string): Promise { diff --git a/lib/services/debug-service-base.ts b/lib/services/debug-service-base.ts index d50327c442..3429e18e35 100644 --- a/lib/services/debug-service-base.ts +++ b/lib/services/debug-service-base.ts @@ -26,4 +26,23 @@ export abstract class DebugServiceBase extends EventEmitter implements IPlatform } }; } + + protected getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string { + // corresponds to 55.0.2883 Chrome version + const commitSHA = debugOptions.devToolsCommit || "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; + debugOptions.useHttpUrl = debugOptions.useHttpUrl === undefined ? false : debugOptions.useHttpUrl; + + let chromeDevToolsPrefix = `chrome-devtools://devtools/remote/serve_file/@${commitSHA}`; + + if (debugOptions.useBundledDevTools) { + chromeDevToolsPrefix = "chrome-devtools://devtools/bundled"; + } + + if (debugOptions.useHttpUrl) { + chromeDevToolsPrefix = `https://chrome-devtools-frontend.appspot.com/serve_file/@${commitSHA}`; + } + + const chromeUrl = `${chromeDevToolsPrefix}/inspector.html?experiments=true&ws=localhost:${port}`; + return chromeUrl; + } } diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 15c2f6bf1e..bb14ef2db1 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -14,7 +14,7 @@ const inspectorAppName = "NativeScript Inspector.app"; const inspectorNpmPackageName = "tns-ios-inspector"; const inspectorUiDir = "WebInspectorUI/"; -class IOSDebugService extends DebugServiceBase implements IPlatformDebugService { +export class IOSDebugService extends DebugServiceBase implements IPlatformDebugService { private _lldbProcess: ChildProcess; private _sockets: net.Socket[] = []; private _childProcess: ChildProcess; @@ -93,6 +93,14 @@ class IOSDebugService extends DebugServiceBase implements IPlatformDebugService } } + protected getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string { + const debugOpts = _.cloneDeep(debugOptions); + debugOpts.useBundledDevTools = debugOpts.useBundledDevTools === undefined ? false : debugOpts.useBundledDevTools; + + const chromeDebugUrl = super.getChromeDebugUrl(debugOpts, port); + return chromeDebugUrl; + } + private async killProcess(childProcess: ChildProcess): Promise { if (childProcess) { return new Promise((resolve, reject) => { @@ -195,17 +203,7 @@ class IOSDebugService extends DebugServiceBase implements IPlatformDebugService if (debugOptions.chrome) { this._socketProxy = await this.$socketProxyFactory.createWebSocketProxy(this.getSocketFactory(device)); - let chromeDevToolsPrefix = `chrome-devtools://devtools/`; - - if (debugOptions.useBundledDevTools) { - chromeDevToolsPrefix += "bundled"; - } else { - // corresponds to 55.0.2883 Chrome version - const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; - chromeDevToolsPrefix += `remote/serve_file/@${commitSHA}`; - } - - return `${chromeDevToolsPrefix}/inspector.html?experiments=true&ws=localhost:${this._socketProxy.options.port}`; + return this.getChromeDebugUrl(debugOptions, this._socketProxy.options.port); } else { this._socketProxy = await this.$socketProxyFactory.createTCPSocketProxy(this.getSocketFactory(device)); await this.openAppInspector(this._socketProxy.address(), debugData, debugOptions); diff --git a/test/services/android-debug-service.ts b/test/services/android-debug-service.ts new file mode 100644 index 0000000000..d7f239136f --- /dev/null +++ b/test/services/android-debug-service.ts @@ -0,0 +1,162 @@ +import { AndroidDebugService } from "../../lib/services/android-debug-service"; +import { Yok } from "../../lib/common/yok"; +import * as stubs from "../stubs"; +import { assert } from "chai"; + +const expectedDevToolsCommitSha = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; + +class AndroidDebugServiceInheritor extends AndroidDebugService { + constructor(protected $devicesService: Mobile.IDevicesService, + $errors: IErrors, + $logger: ILogger, + $config: IConfiguration, + $androidDeviceDiscovery: Mobile.IDeviceDiscovery, + $androidProcessService: Mobile.IAndroidProcessService, + $net: INet) { + super($devicesService, $errors, $logger, $config, $androidDeviceDiscovery, $androidProcessService, $net); + } + + public getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string { + return super.getChromeDebugUrl(debugOptions, port); + } +} + +const createTestInjector = (): IInjector => { + const testInjector = new Yok(); + testInjector.register("devicesService", {}); + testInjector.register("errors", stubs.ErrorsStub); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register("config", {}); + testInjector.register("androidDeviceDiscovery", {}); + testInjector.register("androidProcessService", {}); + testInjector.register("net", {}); + + return testInjector; +}; + +interface IChromeUrlTestCase { + debugOptions: IDebugOptions; + expectedChromeUrl: string; + scenarioName: string; +} + +describe("androidDebugService", () => { + describe("getChromeDebugUrl", () => { + const expectedPort = 12345; + const customDevToolsCommit = "customDevToolsCommit"; + + const chromUrlTestCases: IChromeUrlTestCase[] = [ + // Default CLI behavior: + { + scenarioName: "useBundledDevTools and useHttpUrl are not passed", + debugOptions: {}, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is true + { + scenarioName: "useBundledDevTools is true and useHttpUrl is not passed", + debugOptions: { + useBundledDevTools: true + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is true and useHttpUrl is false", + debugOptions: { + useBundledDevTools: true, + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is true and useHttpUrl is true", + debugOptions: { + useBundledDevTools: true, + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is false + { + scenarioName: "useBundledDevTools is false and useHttpUrl is not passed", + debugOptions: { + useBundledDevTools: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is false and useHttpUrl is false", + debugOptions: { + useBundledDevTools: false, + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is false and useHttpUrl is true", + debugOptions: { + useBundledDevTools: false, + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is not passed + { + scenarioName: "useBundledDevTools is not passed and useHttpUrl is false", + debugOptions: { + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is not passed and useHttpUrl is true", + debugOptions: { + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // devToolsCommit tests + { + scenarioName: "devToolsCommit defaults to ${expectedDevToolsCommitSha} when useBundledDevTools is set to false", + debugOptions: { + useBundledDevTools: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is disregarded when useBundledDevTools is not passed", + debugOptions: {}, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is set to passed value when useBundledDevTools is set to false", + debugOptions: { + useBundledDevTools: false, + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${customDevToolsCommit}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is set to passed value when useHttpUrl is set to true", + debugOptions: { + useHttpUrl: true, + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${customDevToolsCommit}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + } + ]; + + for (const testCase of chromUrlTestCases) { + it(`returns correct url when ${testCase.scenarioName}`, () => { + const testInjector = createTestInjector(); + const androidDebugService = testInjector.resolve(AndroidDebugServiceInheritor); + const actualChromeUrl = androidDebugService.getChromeDebugUrl(testCase.debugOptions, expectedPort); + assert.equal(actualChromeUrl, testCase.expectedChromeUrl); + }); + } + }); +}); diff --git a/test/services/ios-debug-service.ts b/test/services/ios-debug-service.ts new file mode 100644 index 0000000000..2f4898cfcb --- /dev/null +++ b/test/services/ios-debug-service.ts @@ -0,0 +1,187 @@ +import { IOSDebugService } from "../../lib/services/ios-debug-service"; +import { Yok } from "../../lib/common/yok"; +import * as stubs from "../stubs"; +import { assert } from "chai"; + +const expectedDevToolsCommitSha = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; + +class IOSDebugServiceInheritor extends IOSDebugService { + constructor(protected $devicesService: Mobile.IDevicesService, + $platformService: IPlatformService, + $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, + $childProcess: IChildProcess, + $logger: ILogger, + $errors: IErrors, + $npmInstallationManager: INpmInstallationManager, + $iOSNotification: IiOSNotification, + $iOSSocketRequestExecutor: IiOSSocketRequestExecutor, + $processService: IProcessService, + $socketProxyFactory: ISocketProxyFactory) { + super($devicesService, $platformService, $iOSEmulatorServices, $childProcess, $logger, $errors, + $npmInstallationManager, $iOSNotification, $iOSSocketRequestExecutor, $processService, $socketProxyFactory); + } + + public getChromeDebugUrl(debugOptions: IDebugOptions, port: number): string { + return super.getChromeDebugUrl(debugOptions, port); + } +} + +const createTestInjector = (): IInjector => { + const testInjector = new Yok(); + testInjector.register("devicesService", {}); + testInjector.register("platformService", {}); + testInjector.register("iOSEmulatorServices", {}); + testInjector.register("childProcess", {}); + + testInjector.register("errors", stubs.ErrorsStub); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register("npmInstallationManager", {}); + testInjector.register("iOSNotification", {}); + testInjector.register("iOSSocketRequestExecutor", {}); + testInjector.register("processService", { + attachToProcessExitSignals: (context: any, callback: () => void): void => undefined + }); + + testInjector.register("socketProxyFactory", { + on: (event: string | symbol, listener: Function): any => undefined + }); + + return testInjector; +}; + +interface IChromeUrlTestCase { + debugOptions: IDebugOptions; + expectedChromeUrl: string; + scenarioName: string; +} + +describe("iOSDebugService", () => { + describe("getChromeDebugUrl", () => { + const expectedPort = 12345; + const customDevToolsCommit = "customDevToolsCommit"; + + const chromUrlTestCases: IChromeUrlTestCase[] = [ + // Default CLI behavior: + { + scenarioName: "useBundledDevTools and useHttpUrl are not passed", + debugOptions: {}, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is true + { + scenarioName: "useBundledDevTools is true and useHttpUrl is not passed", + debugOptions: { + useBundledDevTools: true + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is true and useHttpUrl is false", + debugOptions: { + useBundledDevTools: true, + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is true and useHttpUrl is true", + debugOptions: { + useBundledDevTools: true, + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is false + { + scenarioName: "useBundledDevTools is false and useHttpUrl is not passed", + debugOptions: { + useBundledDevTools: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is false and useHttpUrl is false", + debugOptions: { + useBundledDevTools: false, + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is false and useHttpUrl is true", + debugOptions: { + useBundledDevTools: false, + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // When useBundledDevTools is not passed + { + scenarioName: "useBundledDevTools is not passed and useHttpUrl is false", + debugOptions: { + useHttpUrl: false + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "useBundledDevTools is not passed and useHttpUrl is true", + debugOptions: { + useHttpUrl: true + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + + // devToolsCommit tests + { + scenarioName: "devToolsCommit defaults to ${expectedDevToolsCommitSha} and is used in result when useBundledDevTools is not passed", + debugOptions: {}, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${expectedDevToolsCommitSha}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is set to passed value when useBundledDevTools is not passed", + debugOptions: { + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${customDevToolsCommit}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is set to passed value when useBundledDevTools is set to false", + debugOptions: { + useBundledDevTools: false, + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `chrome-devtools://devtools/remote/serve_file/@${customDevToolsCommit}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is set to passed value when useHttpUrl is set to true", + debugOptions: { + useHttpUrl: true, + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `https://chrome-devtools-frontend.appspot.com/serve_file/@${customDevToolsCommit}/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + }, + { + scenarioName: "devToolsCommit is disregarded when useBundledDevTools is set to true", + debugOptions: { + useBundledDevTools: true, + devToolsCommit: customDevToolsCommit + }, + expectedChromeUrl: `chrome-devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:${expectedPort}`, + } + + ]; + + for (const testCase of chromUrlTestCases) { + it(`returns correct url when ${testCase.scenarioName}`, () => { + const testInjector = createTestInjector(); + const iOSDebugService = testInjector.resolve(IOSDebugServiceInheritor); + const actualChromeUrl = iOSDebugService.getChromeDebugUrl(testCase.debugOptions, expectedPort); + assert.equal(actualChromeUrl, testCase.expectedChromeUrl); + }); + } + + }); +});