Skip to content

Commit 8b730f3

Browse files
Add tests for debugService
Add tests for debugService. Add verification that in case device's OS is not supported (for example WP8), we'll throw correct error.
1 parent b3e50ad commit 8b730f3

File tree

4 files changed

+309
-9
lines changed

4 files changed

+309
-9
lines changed

lib/definitions/debug.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface IDebugData {
3333
*/
3434
interface IDebugOptions {
3535
/**
36-
* Defines if Chrome-Dev Tools should be used for debugging.
36+
* Defines if Chrome DevTools should be used for debugging.
3737
*/
3838
chrome?: boolean;
3939

lib/services/debug-service.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { EventEmitter } from "events";
33
import { CONNECTION_ERROR_EVENT_NAME } from "../constants";
44
import { CONNECTED_STATUS } from "../common/constants";
55

6-
class DebugService extends EventEmitter implements IDebugService {
6+
export class DebugService extends EventEmitter implements IDebugService {
77
constructor(private $devicesService: Mobile.IDevicesService,
88
private $androidDebugService: IPlatformDebugService,
99
private $iOSDebugService: IPlatformDebugService,
@@ -16,10 +16,9 @@ class DebugService extends EventEmitter implements IDebugService {
1616

1717
public async debug(debugData: IDebugData, options: IDebugOptions): Promise<string> {
1818
const device = this.$devicesService.getDeviceByIdentifier(debugData.deviceIdentifier);
19-
const debugService = this.getDebugService(device);
2019

2120
if (!device) {
22-
this.$errors.failWithoutHelp(`Can't find device with identifier ${debugData.deviceIdentifier}`);
21+
this.$errors.failWithoutHelp(`Cannot find device with identifier ${debugData.deviceIdentifier}.`);
2322
}
2423

2524
if (device.deviceInfo.status !== CONNECTED_STATUS) {
@@ -38,9 +37,15 @@ class DebugService extends EventEmitter implements IDebugService {
3837
// After we find a way to check on iOS we should use it here.
3938
const isAppRunning = true;
4039
let result: string[];
41-
debugOptions.chrome = !debugOptions.client;
40+
debugOptions.chrome = true;
41+
42+
const debugService = this.getDebugService(device);
43+
if (!debugService) {
44+
this.$errors.failWithoutHelp(`Unsupported device OS: ${device.deviceInfo.platform}. You can debug your applications only on iOS or Android.`);
45+
}
46+
4247
if (this.$mobileHelper.isiOSPlatform(device.deviceInfo.platform)) {
43-
if (device.isEmulator && !debugData.pathToAppPackage) {
48+
if (device.isEmulator && !debugData.pathToAppPackage && debugOptions.debugBrk) {
4449
this.$errors.failWithoutHelp("To debug on iOS simulator you need to provide path to the app package.");
4550
}
4651

lib/services/ios-debug-service.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,14 @@ class IOSDebugService extends DebugServiceBase implements IPlatformDebugService
196196
if (debugOptions.chrome) {
197197
this._socketProxy = await this.$socketProxyFactory.createWebSocketProxy(this.getSocketFactory(device));
198198

199-
const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; // corresponds to 55.0.2883 Chrome version
200-
let chromeDevToolsPrefix = `chrome-devtools://devtools/remote/serve_file/@${commitSHA}`;
199+
let chromeDevToolsPrefix = `chrome-devtools://devtools/`;
201200

202201
if (debugOptions.useBundledDevTools) {
203-
chromeDevToolsPrefix = "chrome-devtools://devtools/bundled";
202+
chromeDevToolsPrefix += "bundled";
203+
} else {
204+
// corresponds to 55.0.2883 Chrome version
205+
const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024";
206+
chromeDevToolsPrefix += `remote/serve_file/@${commitSHA}`;
204207
}
205208

206209
return `${chromeDevToolsPrefix}/inspector.html?experiments=true&ws=localhost:${this._socketProxy.options.port}`;

test/services/debug-service.ts

+292
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
import { DebugService } from "../../lib/services/debug-service";
2+
import { Yok } from "../../lib/common/yok";
3+
import * as stubs from "../stubs";
4+
import { assert } from "chai";
5+
import { EventEmitter } from "events";
6+
import * as constants from "../../lib/common/constants";
7+
import { CONNECTION_ERROR_EVENT_NAME } from "../../lib/constants";
8+
9+
const fakeChromeDebugUrl = "fakeChromeDebugUrl";
10+
class PlatformDebugService extends EventEmitter /* implements IPlatformDebugService */ {
11+
public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string[]> {
12+
return [fakeChromeDebugUrl];
13+
}
14+
};
15+
16+
interface IDebugTestDeviceInfo {
17+
deviceInfo: {
18+
status: string;
19+
platform: string;
20+
};
21+
22+
isEmulator: boolean;
23+
};
24+
25+
interface IDebugTestData {
26+
isDeviceFound: boolean;
27+
deviceInformation: IDebugTestDeviceInfo;
28+
isApplicationInstalledOnDevice: boolean;
29+
hostInfo: {
30+
isWindows: boolean;
31+
isDarwin: boolean;
32+
};
33+
};
34+
35+
const getDefaultDeviceInformation = (platform?: string): IDebugTestDeviceInfo => ({
36+
deviceInfo: {
37+
status: constants.CONNECTED_STATUS,
38+
platform: platform || "Android"
39+
},
40+
41+
isEmulator: false
42+
});
43+
44+
const getDefaultTestData = (platform?: string): IDebugTestData => ({
45+
isDeviceFound: true,
46+
deviceInformation: getDefaultDeviceInformation(platform),
47+
isApplicationInstalledOnDevice: true,
48+
hostInfo: {
49+
isWindows: false,
50+
isDarwin: true
51+
}
52+
});
53+
54+
describe("debugService", () => {
55+
const getTestInjectorForTestConfiguration = (testData: IDebugTestData): IInjector => {
56+
const testInjector = new Yok();
57+
testInjector.register("devicesService", {
58+
getDeviceByIdentifier: (identifier: string): Mobile.IDevice => {
59+
return testData.isDeviceFound ?
60+
<Mobile.IDevice> {
61+
deviceInfo: testData.deviceInformation.deviceInfo,
62+
63+
applicationManager: {
64+
isApplicationInstalled: async (appIdentifier: string): Promise<boolean> => testData.isApplicationInstalledOnDevice
65+
},
66+
67+
isEmulator: testData.deviceInformation.isEmulator
68+
} : null;
69+
}
70+
});
71+
72+
testInjector.register("androidDebugService", PlatformDebugService);
73+
74+
testInjector.register("iOSDebugService", PlatformDebugService);
75+
76+
testInjector.register("mobileHelper", {
77+
isAndroidPlatform: (platform: string) => {
78+
return platform.toLowerCase() === "android";
79+
},
80+
isiOSPlatform: (platform: string) => {
81+
return platform.toLowerCase() === "ios";
82+
}
83+
});
84+
85+
testInjector.register("errors", stubs.ErrorsStub);
86+
87+
testInjector.register("hostInfo", testData.hostInfo);
88+
89+
return testInjector;
90+
};
91+
92+
describe("debug", () => {
93+
const getDebugData = (deviceIdentifier?: string): IDebugData => ({
94+
applicationIdentifier: "org.nativescript.app1",
95+
deviceIdentifier: deviceIdentifier || "Nexus5",
96+
projectDir: "/Users/user/app1",
97+
projectName: "app1"
98+
});
99+
100+
describe("rejects the result promise when", () => {
101+
const assertIsRejected = async (testData: IDebugTestData, expectedError: string, userSpecifiedOptions?: IDebugOptions): Promise<void> => {
102+
const testInjector = getTestInjectorForTestConfiguration(testData);
103+
const debugService = testInjector.resolve<IDebugService>(DebugService);
104+
105+
const debugData = getDebugData();
106+
await assert.isRejected(debugService.debug(debugData, userSpecifiedOptions), expectedError);
107+
};
108+
109+
it("there's no attached device as the specified identifier", async () => {
110+
const testData = getDefaultTestData();
111+
testData.isDeviceFound = false;
112+
113+
await assertIsRejected(testData, "Cannot find device with identifier");
114+
});
115+
116+
it("the device is not trusted", async () => {
117+
const testData = getDefaultTestData();
118+
testData.deviceInformation.deviceInfo.status = constants.UNREACHABLE_STATUS;
119+
120+
await assertIsRejected(testData, "is unreachable. Make sure it is Trusted ");
121+
});
122+
123+
it("the application is not installed on device", async () => {
124+
const testData = getDefaultTestData();
125+
testData.isApplicationInstalledOnDevice = false;
126+
127+
await assertIsRejected(testData, "is not installed on device with identifier");
128+
});
129+
130+
it("the OS is neither Windows or macOS and device is iOS", async () => {
131+
const testData = getDefaultTestData();
132+
testData.deviceInformation.deviceInfo.platform = "iOS";
133+
testData.hostInfo.isDarwin = testData.hostInfo.isWindows = false;
134+
135+
await assertIsRejected(testData, "Debugging on iOS devices is not supported for");
136+
});
137+
138+
it("device is neither iOS or Android", async () => {
139+
const testData = getDefaultTestData();
140+
testData.deviceInformation.deviceInfo.platform = "WP8";
141+
142+
await assertIsRejected(testData, "Unsupported device OS:");
143+
});
144+
145+
it("when trying to debug on iOS Simulator on macOS, debug-brk is passed, but pathToAppPackage is not", async () => {
146+
const testData = getDefaultTestData();
147+
testData.deviceInformation.deviceInfo.platform = "iOS";
148+
testData.deviceInformation.isEmulator = true;
149+
150+
await assertIsRejected(testData, "To debug on iOS simulator you need to provide path to the app package.", { debugBrk: true });
151+
});
152+
153+
const assertIsRejectedWhenPlatformDebugServiceFails = async (platform: string): Promise<void> => {
154+
const testData = getDefaultTestData();
155+
testData.deviceInformation.deviceInfo.platform = platform;
156+
157+
const testInjector = getTestInjectorForTestConfiguration(testData);
158+
const expectedErrorMessage = "Platform specific error";
159+
const platformDebugService = testInjector.resolve<IPlatformDebugService>(`${platform}DebugService`);
160+
platformDebugService.debug = async (debugData: IDebugData, debugOptions: IDebugOptions): Promise<any> => {
161+
throw new Error(expectedErrorMessage);
162+
};
163+
164+
const debugService = testInjector.resolve<IDebugService>(DebugService);
165+
166+
const debugData = getDebugData();
167+
await assert.isRejected(debugService.debug(debugData, null), expectedErrorMessage);
168+
};
169+
170+
it("androidDebugService's debug method fails", async () => {
171+
await assertIsRejectedWhenPlatformDebugServiceFails("android");
172+
});
173+
174+
it("iOSDebugService's debug method fails", async () => {
175+
await assertIsRejectedWhenPlatformDebugServiceFails("iOS");
176+
});
177+
});
178+
179+
describe("passes correct args to", () => {
180+
const assertPassedDebugOptions = async (platform: string, userSpecifiedOptions?: IDebugOptions, hostInfo?: { isWindows: boolean, isDarwin: boolean }): Promise<IDebugOptions> => {
181+
const testData = getDefaultTestData();
182+
testData.deviceInformation.deviceInfo.platform = platform;
183+
if (hostInfo) {
184+
testData.hostInfo = hostInfo;
185+
}
186+
187+
const testInjector = getTestInjectorForTestConfiguration(testData);
188+
const platformDebugService = testInjector.resolve<IPlatformDebugService>(`${platform}DebugService`);
189+
let passedDebugOptions: IDebugOptions = null;
190+
platformDebugService.debug = async (debugData: IDebugData, debugOptions: IDebugOptions): Promise<any> => {
191+
passedDebugOptions = debugOptions;
192+
return [];
193+
};
194+
195+
const debugService = testInjector.resolve<IDebugService>(DebugService);
196+
197+
const debugData = getDebugData();
198+
await assert.isFulfilled(debugService.debug(debugData, userSpecifiedOptions));
199+
assert.isTrue(passedDebugOptions.chrome);
200+
assert.isTrue(passedDebugOptions.start);
201+
202+
return passedDebugOptions;
203+
};
204+
205+
_.each(["android", "iOS"], platform => {
206+
describe(`${platform}DebugService's debug method`, () => {
207+
describe("on macOS", () => {
208+
it("passes chrome and start as true, when no debugOptions are passed to debugService", async () => {
209+
await assertPassedDebugOptions(platform);
210+
});
211+
212+
it("when calling debug service with chrome and start set to false, should disregard them and set both to true", async () => {
213+
await assertPassedDebugOptions(platform, { chrome: false, start: false });
214+
});
215+
216+
it("passes other custom options without modification", async () => {
217+
const passedDebugOptions = await assertPassedDebugOptions(platform, { emulator: true, useBundledDevTools: true });
218+
assert.isTrue(passedDebugOptions.useBundledDevTools);
219+
assert.isTrue(passedDebugOptions.emulator);
220+
});
221+
});
222+
223+
describe("on Windows", () => {
224+
const assertEmulatorOption = (passedDebugOptions: IDebugOptions) => {
225+
if (platform === "iOS") {
226+
assert.isFalse(passedDebugOptions.emulator);
227+
}
228+
};
229+
230+
it("passes chrome and start as true, when no debugOptions are passed to debugService", async () => {
231+
const passedDebugOptions = await assertPassedDebugOptions(platform, null, { isWindows: true, isDarwin: false });
232+
assertEmulatorOption(passedDebugOptions);
233+
});
234+
235+
it("when calling debug service with chrome and start set to false, should disregard them and set both to true", async () => {
236+
const passedDebugOptions = await assertPassedDebugOptions(platform, { chrome: false, start: false }, { isWindows: true, isDarwin: false });
237+
assertEmulatorOption(passedDebugOptions);
238+
});
239+
240+
it("passes other custom options without modification", async () => {
241+
const passedDebugOptions = await assertPassedDebugOptions(platform, { debugBrk: true, useBundledDevTools: true });
242+
assert.isTrue(passedDebugOptions.useBundledDevTools);
243+
assert.isTrue(passedDebugOptions.debugBrk);
244+
});
245+
});
246+
247+
});
248+
});
249+
});
250+
251+
describe(`raises ${CONNECTION_ERROR_EVENT_NAME} event`, () => {
252+
_.each(["android", "iOS"], platform => {
253+
it(`when ${platform}DebugService raises ${CONNECTION_ERROR_EVENT_NAME} event`, async () => {
254+
const testData = getDefaultTestData();
255+
testData.deviceInformation.deviceInfo.platform = platform;
256+
257+
const testInjector = getTestInjectorForTestConfiguration(testData);
258+
const debugService = testInjector.resolve<IDebugService>(DebugService);
259+
let dataRaisedForConnectionError: any = null;
260+
debugService.on(CONNECTION_ERROR_EVENT_NAME, (data: any) => {
261+
dataRaisedForConnectionError = data;
262+
});
263+
264+
const debugData = getDebugData();
265+
await assert.isFulfilled(debugService.debug(debugData, null));
266+
267+
const expectedErrorData = { deviceId: "deviceId", message: "my message", code: 2048 };
268+
const platformDebugService = testInjector.resolve<IPlatformDebugService>(`${platform}DebugService`);
269+
platformDebugService.emit(CONNECTION_ERROR_EVENT_NAME, expectedErrorData);
270+
assert.deepEqual(dataRaisedForConnectionError, expectedErrorData);
271+
});
272+
});
273+
});
274+
275+
describe("returns chrome url returned by platform specific debug service", () => {
276+
_.each(["android", "iOS"], platform => {
277+
it(`for ${platform} device`, async () => {
278+
const testData = getDefaultTestData();
279+
testData.deviceInformation.deviceInfo.platform = platform;
280+
281+
const testInjector = getTestInjectorForTestConfiguration(testData);
282+
const debugService = testInjector.resolve<IDebugService>(DebugService);
283+
284+
const debugData = getDebugData();
285+
const url = await debugService.debug(debugData, null);
286+
287+
assert.deepEqual(url, fakeChromeDebugUrl);
288+
});
289+
});
290+
});
291+
});
292+
});

0 commit comments

Comments
 (0)