From 5ce601215a1b44e7691b7dd68d94171f733c5076 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 08:36:48 +0200 Subject: [PATCH 1/4] feat(preview): add api for deviceFound and deviceLost for preview devices --- lib/bootstrap.ts | 1 + lib/definitions/preview-app-livesync.d.ts | 8 ++++- .../devices/preview-devices-service.ts | 36 +++++++++++++++++++ .../preview-app-livesync-service.ts | 7 ++-- .../playground/preview-sdk-service.ts | 22 +++++------- 5 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 lib/services/livesync/playground/devices/preview-devices-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 770a938183..adbc81da63 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -134,6 +134,7 @@ $injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); $injector.require("previewAppLiveSyncService", "./services/livesync/playground/preview-app-livesync-service"); $injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service"); $injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service"); +$injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service"); $injector.require("playgroundQrCodeGenerator", "./services/livesync/playground/qr-code-generator"); $injector.requirePublic("sysInfo", "./sys-info"); diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 81907e41ef..1ffdf34205 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -12,7 +12,6 @@ declare global { interface IPreviewSdkService extends EventEmitter { getQrCodeUrl(options: IHasUseHotModuleReloadOption): string; - connectedDevices: Device[]; initialize(getInitialFiles: (device: Device) => Promise): void; applyChanges(filesPayload: FilesPayload): Promise; stop(): void; @@ -34,4 +33,11 @@ declare global { */ link: boolean; } + + interface IPreviewDevicesService extends EventEmitter { + connectedDevices: Device[]; + onDevicesPresence(devices: Device[]): void; + getDeviceById(id: string): Device; + getDevicesForPlatform(platform: string): Device[]; + } } \ No newline at end of file diff --git a/lib/services/livesync/playground/devices/preview-devices-service.ts b/lib/services/livesync/playground/devices/preview-devices-service.ts new file mode 100644 index 0000000000..b6b560fced --- /dev/null +++ b/lib/services/livesync/playground/devices/preview-devices-service.ts @@ -0,0 +1,36 @@ +import { Device } from "nativescript-preview-sdk"; +import { EventEmitter } from "events"; +import { DeviceDiscoveryEventNames } from "../../../../common/constants"; + +export class PreviewDevicesService extends EventEmitter implements IPreviewDevicesService { + public connectedDevices: Device[] = []; + + public onDevicesPresence(devices: Device[]): void { + _(devices) + .reject(d => _.find(this.connectedDevices, device => d.id === device.id)) + .each(device => this.raiseDeviceFound(device)); + + _(this.connectedDevices) + .reject(d => _.find(devices, device => d.id === device.id)) + .each(device => this.raiseDeviceLost(device)); + } + + public getDeviceById(id: string): Device { + return _.find(this.connectedDevices, { id }); + } + + public getDevicesForPlatform(platform: string): Device[] { + return _.filter(this.connectedDevices, { platform: platform.toLowerCase() }); + } + + private raiseDeviceFound(device: Device) { + this.emit(DeviceDiscoveryEventNames.DEVICE_FOUND, device); + this.connectedDevices.push(device); + } + + private raiseDeviceLost(device: Device) { + this.emit(DeviceDiscoveryEventNames.DEVICE_LOST, device); + _.remove(this.connectedDevices, d => d.id === device.id); + } +} +$injector.register("previewDevicesService", PreviewDevicesService); diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index a968797994..77809dcf87 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -28,6 +28,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { private $projectDataService: IProjectDataService, private $previewSdkService: IPreviewSdkService, private $previewAppPluginsService: IPreviewAppPluginsService, + private $previewDevicesService: IPreviewDevicesService, private $projectFilesManager: IProjectFilesManager, private $hmrStatusService: IHmrStatusService, private $projectFilesProvider: IProjectFilesProvider) { } @@ -55,11 +56,11 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { this.showWarningsForNativeFiles(filesToSync); - for (const device of this.$previewSdkService.connectedDevices) { + for (const device of this.$previewDevicesService.connectedDevices) { await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); } - const platforms = _(this.$previewSdkService.connectedDevices) + const platforms = _(this.$previewDevicesService.connectedDevices) .map(device => device.platform) .uniq() .value(); @@ -112,7 +113,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { await promise; if (data.appFilesUpdaterOptions.useHotModuleReload && platformHmrData.hash) { - const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() }); + const devices = this.$previewDevicesService.getDevicesForPlatform(platform); await Promise.all(_.map(devices, async (previewDevice: Device) => { const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash); diff --git a/lib/services/livesync/playground/preview-sdk-service.ts b/lib/services/livesync/playground/preview-sdk-service.ts index df72e02298..fe1dccefdc 100644 --- a/lib/services/livesync/playground/preview-sdk-service.ts +++ b/lib/services/livesync/playground/preview-sdk-service.ts @@ -1,18 +1,18 @@ import { MessagingService, Config, Device, DeviceConnectedMessage, SdkCallbacks, ConnectedDevices, FilesPayload } from "nativescript-preview-sdk"; import { PubnubKeys } from "./preview-app-constants"; -import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants"; import { EventEmitter } from "events"; +import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants"; const pako = require("pako"); export class PreviewSdkService extends EventEmitter implements IPreviewSdkService { private static MAX_FILES_UPLOAD_BYTE_LENGTH = 15 * 1024 * 1024; // In MBs private messagingService: MessagingService = null; private instanceId: string = null; - public connectedDevices: Device[] = []; - constructor(private $logger: ILogger, + constructor(private $config: IConfiguration, private $httpClient: Server.IHttpClient, - private $config: IConfiguration) { + private $logger: ILogger, + private $previewDevicesService: IPreviewDevicesService) { super(); } @@ -60,9 +60,8 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onLogSdkMessage: (log: string) => { this.$logger.trace("Received onLogSdkMessage message: ", log); }, - onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({ }), onLogMessage: (log: string, deviceName: string, deviceId: string) => { - const device = _.find(this.connectedDevices, { id: deviceId}); + const device = this.$previewDevicesService.getDeviceById(deviceId); this.emit(DEVICE_LOG_EVENT_NAME, log, deviceId, device ? device.platform : ""); this.$logger.info(`LOG from device ${deviceName}: ${log}`); }, @@ -72,13 +71,10 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onUncaughtErrorMessage: () => { this.$logger.warn("The Preview app has terminated unexpectedly. Please run it again to get a detailed crash report."); }, - onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({ }), - onDeviceConnected: (device: Device) => { - if (!_.find(this.connectedDevices, {id: device.id})) { - this.connectedDevices.push(device); - } - }, - onDevicesPresence: (devices: Device[]) => ({ }), + onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({}), + onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({}), + onDeviceConnected: (device: Device) => ({}), + onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.onDevicesPresence(devices), onSendingChange: (sending: boolean) => ({ }), onBiggerFilesUpload: async (filesContent, callback) => { const gzippedContent = Buffer.from(pako.gzip(filesContent)); From 569b4986b430ad0e1e2180c061196341fd7efda9 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 08:37:17 +0200 Subject: [PATCH 2/4] test(preview): add unit tests for previewDevicesService --- test/services/preview-devices-service.ts | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 test/services/preview-devices-service.ts diff --git a/test/services/preview-devices-service.ts b/test/services/preview-devices-service.ts new file mode 100644 index 0000000000..3ab1988825 --- /dev/null +++ b/test/services/preview-devices-service.ts @@ -0,0 +1,102 @@ +import { Yok } from "../../lib/common/yok"; +import { PreviewDevicesService } from "../../lib/services/livesync/playground/devices/preview-devices-service"; +import { Device } from "nativescript-preview-sdk"; +import { assert } from "chai"; +import { DeviceDiscoveryEventNames } from "../../lib/common/constants"; +import { LoggerStub } from "../stubs"; + +let foundDevices: Device[] = []; +let lostDevices: Device[] = []; + +function createTestInjector(): IInjector { + const injector = new Yok(); + injector.register("previewDevicesService", PreviewDevicesService); + injector.register("logger", LoggerStub); + return injector; +} + +function createDevice(id: string): Device { + return { + id, + platform: "ios", + model: "my test model", + name: "my test name", + osVersion: "10.0.0", + previewAppVersion: "19.0.0", + runtimeVersion: "5.0.0" + }; +} + +describe("PreviewDevicesService", () => { + describe("onDevicesPresence", () => { + let previewDevicesService: IPreviewDevicesService = null; + beforeEach(() => { + const injector = createTestInjector(); + previewDevicesService = injector.resolve("previewDevicesService"); + previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_FOUND, device => { + foundDevices.push(device); + }); + previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_LOST, device => { + lostDevices.push(device); + }); + }); + + afterEach(() => { + previewDevicesService.removeAllListeners(); + foundDevices = []; + lostDevices = []; + }); + + it("should add new device", () => { + const device = createDevice("device1"); + + previewDevicesService.onDevicesPresence([device]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device]); + assert.deepEqual(foundDevices, [device]); + assert.deepEqual(lostDevices, []); + }); + it("should add new device when there are already connected devices", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([device1, device2]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2]); + assert.deepEqual(foundDevices, [device2]); + assert.deepEqual(lostDevices, []); + }); + it("should add more than one new device", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + const device3 = createDevice("device3"); + + previewDevicesService.onDevicesPresence([device1, device2, device3]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2, device3]); + assert.deepEqual(foundDevices, [device1, device2, device3]); + assert.deepEqual(lostDevices, []); + }); + it("should remove device", () => { + const device1 = createDevice("device1"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([]); + + assert.deepEqual(foundDevices, []); + assert.deepEqual(lostDevices, [device1]); + }); + it("should add and remove devices in the same time", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([device2]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device2]); + assert.deepEqual(foundDevices, [device2]); + assert.deepEqual(lostDevices, [device1]); + }); + }); +}); From aeec0ac6d392035da9d64a2ce398cac8045e9aac Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 09:33:17 +0200 Subject: [PATCH 3/4] fix(preview-unit-tests): inject previewDevicesService in order to fix unit tests --- test/services/playground/preview-app-livesync-service.ts | 4 +++- test/services/preview-sdk-service.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index f93061b347..af6dfeb9c6 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -75,7 +75,6 @@ class PreviewSdkServiceMock extends EventEmitter implements IPreviewSdkService { return "my_cool_qr_code_url"; } - public connectedDevices: Device[] = [deviceMockData]; public initialize(getInitialFiles: (device: Device) => Promise) { this.getInitialFiles = async (device) => { const filesPayload = await getInitialFiles(device); @@ -158,6 +157,9 @@ function createTestInjector(options?: { isHookCalledWithHMR = args.hookArgs.config.appFilesUpdaterOptions.useHotModuleReload; } }); + injector.register("previewDevicesService", { + connectedDevices: [deviceMockData] + }); return injector; } diff --git a/test/services/preview-sdk-service.ts b/test/services/preview-sdk-service.ts index 0790c0de42..e4698c03e4 100644 --- a/test/services/preview-sdk-service.ts +++ b/test/services/preview-sdk-service.ts @@ -8,7 +8,7 @@ const getPreviewSdkService = (): IPreviewSdkService => { testInjector.register("logger", LoggerStub); testInjector.register("config", {}); testInjector.register("previewSdkService", PreviewSdkService); - + testInjector.register("previewDevicesService", {}); testInjector.register("httpClient", { httpRequest: async (options: any, proxySettings?: IProxySettings): Promise => undefined }); From 8a5f64f129283de39a54349d83a06caa2c241658 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 13:53:22 +0200 Subject: [PATCH 4/4] chore(preview): handle PR comments --- lib/definitions/preview-app-livesync.d.ts | 4 +- .../devices/preview-devices-service.ts | 8 +++- .../preview-app-livesync-service.ts | 6 +-- .../playground/preview-sdk-service.ts | 2 +- .../preview-app-livesync-service.ts | 2 +- test/services/preview-devices-service.ts | 46 +++++++++++++------ 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 1ffdf34205..23bcbdb491 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -35,8 +35,8 @@ declare global { } interface IPreviewDevicesService extends EventEmitter { - connectedDevices: Device[]; - onDevicesPresence(devices: Device[]): void; + getConnectedDevices(): Device[]; + updateConnectedDevices(devices: Device[]): void; getDeviceById(id: string): Device; getDevicesForPlatform(platform: string): Device[]; } diff --git a/lib/services/livesync/playground/devices/preview-devices-service.ts b/lib/services/livesync/playground/devices/preview-devices-service.ts index b6b560fced..09e18f275a 100644 --- a/lib/services/livesync/playground/devices/preview-devices-service.ts +++ b/lib/services/livesync/playground/devices/preview-devices-service.ts @@ -3,9 +3,13 @@ import { EventEmitter } from "events"; import { DeviceDiscoveryEventNames } from "../../../../common/constants"; export class PreviewDevicesService extends EventEmitter implements IPreviewDevicesService { - public connectedDevices: Device[] = []; + private connectedDevices: Device[] = []; - public onDevicesPresence(devices: Device[]): void { + public getConnectedDevices(): Device[] { + return this.connectedDevices; + } + + public updateConnectedDevices(devices: Device[]): void { _(devices) .reject(d => _.find(this.connectedDevices, device => d.id === device.id)) .each(device => this.raiseDeviceFound(device)); diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 77809dcf87..a1727430a0 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -56,15 +56,15 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { this.showWarningsForNativeFiles(filesToSync); - for (const device of this.$previewDevicesService.connectedDevices) { + const connectedDevices = this.$previewDevicesService.getConnectedDevices(); + for (const device of connectedDevices) { await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); } - const platforms = _(this.$previewDevicesService.connectedDevices) + const platforms = _(connectedDevices) .map(device => device.platform) .uniq() .value(); - for (const platform of platforms) { await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); } diff --git a/lib/services/livesync/playground/preview-sdk-service.ts b/lib/services/livesync/playground/preview-sdk-service.ts index fe1dccefdc..603f1bbae5 100644 --- a/lib/services/livesync/playground/preview-sdk-service.ts +++ b/lib/services/livesync/playground/preview-sdk-service.ts @@ -74,7 +74,7 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({}), onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({}), onDeviceConnected: (device: Device) => ({}), - onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.onDevicesPresence(devices), + onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.updateConnectedDevices(devices), onSendingChange: (sending: boolean) => ({ }), onBiggerFilesUpload: async (filesContent, callback) => { const gzippedContent = Buffer.from(pako.gzip(filesContent)); diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index af6dfeb9c6..d4ab2e2551 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -158,7 +158,7 @@ function createTestInjector(options?: { } }); injector.register("previewDevicesService", { - connectedDevices: [deviceMockData] + getConnectedDevices: () => [deviceMockData] }); return injector; diff --git a/test/services/preview-devices-service.ts b/test/services/preview-devices-service.ts index 3ab1988825..7fc2305115 100644 --- a/test/services/preview-devices-service.ts +++ b/test/services/preview-devices-service.ts @@ -27,6 +27,11 @@ function createDevice(id: string): Device { }; } +function resetDevices() { + foundDevices = []; + lostDevices = []; +} + describe("PreviewDevicesService", () => { describe("onDevicesPresence", () => { let previewDevicesService: IPreviewDevicesService = null; @@ -43,27 +48,31 @@ describe("PreviewDevicesService", () => { afterEach(() => { previewDevicesService.removeAllListeners(); - foundDevices = []; - lostDevices = []; + resetDevices(); }); it("should add new device", () => { const device = createDevice("device1"); - previewDevicesService.onDevicesPresence([device]); + previewDevicesService.updateConnectedDevices([device]); - assert.deepEqual(previewDevicesService.connectedDevices, [device]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device]); assert.deepEqual(foundDevices, [device]); assert.deepEqual(lostDevices, []); }); it("should add new device when there are already connected devices", () => { const device1 = createDevice("device1"); const device2 = createDevice("device2"); - previewDevicesService.connectedDevices = [device1]; - previewDevicesService.onDevicesPresence([device1, device2]); + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); + + previewDevicesService.updateConnectedDevices([device1, device2]); - assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2]); assert.deepEqual(foundDevices, [device2]); assert.deepEqual(lostDevices, []); }); @@ -72,17 +81,21 @@ describe("PreviewDevicesService", () => { const device2 = createDevice("device2"); const device3 = createDevice("device3"); - previewDevicesService.onDevicesPresence([device1, device2, device3]); + previewDevicesService.updateConnectedDevices([device1, device2, device3]); - assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2, device3]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2, device3]); assert.deepEqual(foundDevices, [device1, device2, device3]); assert.deepEqual(lostDevices, []); }); it("should remove device", () => { const device1 = createDevice("device1"); - previewDevicesService.connectedDevices = [device1]; + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); - previewDevicesService.onDevicesPresence([]); + previewDevicesService.updateConnectedDevices([]); assert.deepEqual(foundDevices, []); assert.deepEqual(lostDevices, [device1]); @@ -90,11 +103,16 @@ describe("PreviewDevicesService", () => { it("should add and remove devices in the same time", () => { const device1 = createDevice("device1"); const device2 = createDevice("device2"); - previewDevicesService.connectedDevices = [device1]; - previewDevicesService.onDevicesPresence([device2]); + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); + + previewDevicesService.updateConnectedDevices([device2]); - assert.deepEqual(previewDevicesService.connectedDevices, [device2]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device2]); assert.deepEqual(foundDevices, [device2]); assert.deepEqual(lostDevices, [device1]); });