diff --git a/lib/controllers/preview-app-controller.ts b/lib/controllers/preview-app-controller.ts index e568475a5b..d769a7b175 100644 --- a/lib/controllers/preview-app-controller.ts +++ b/lib/controllers/preview-app-controller.ts @@ -138,7 +138,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon @performanceLog() private async handlePrepareReadyEvent(data: IPreviewAppLiveSyncData, currentPrepareData: IFilesChangeEventData) { const { hmrData, files, platform } = currentPrepareData; - const platformHmrData = _.cloneDeep(hmrData); + const platformHmrData = _.cloneDeep(hmrData) || {}; const connectedDevices = this.$previewDevicesService.getDevicesForPlatform(platform); if (!connectedDevices || !connectedDevices.length) { this.$logger.warn(`Unable to find any connected devices for platform '${platform}'. In order to execute live sync, open your Preview app and optionally re-scan the QR code using the Playground app.`); diff --git a/lib/definitions/plugins.d.ts b/lib/definitions/plugins.d.ts index f5325536c8..3ab41f3a89 100644 --- a/lib/definitions/plugins.d.ts +++ b/lib/definitions/plugins.d.ts @@ -35,7 +35,7 @@ interface IBasePluginData { } interface IPluginData extends INodeModuleData { - platformsDataService: IPluginPlatformsData; + platformsData: IPluginPlatformsData; /* Gets all plugin variables from plugin */ pluginVariables: IDictionary; pluginPlatformsFolderPath(platform: string): string; diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index 6875f7c05f..3b95776bd9 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -115,9 +115,8 @@ export class PluginsService implements IPluginsService { } } - public async preparePluginNativeCode({pluginData, platform, projectData}: IPreparePluginNativeCodeData): Promise { + public async preparePluginNativeCode({ pluginData, platform, projectData }: IPreparePluginNativeCodeData): Promise { const platformData = this.$platformsDataService.getPlatformData(platform, projectData); - pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform.toLowerCase()); const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(platform); if (this.$fs.exists(pluginPlatformsFolderPath)) { @@ -213,18 +212,18 @@ export class PluginsService implements IPluginsService { pluginData.version = cacheData.version; pluginData.fullPath = cacheData.directory || path.dirname(this.getPackageJsonFilePathForModule(cacheData.name, projectDir)); pluginData.isPlugin = !!cacheData.nativescript || !!cacheData.moduleInfo; - pluginData.pluginPlatformsFolderPath = (platform: string) => path.join(pluginData.fullPath, "platforms", platform); + pluginData.pluginPlatformsFolderPath = (platform: string) => path.join(pluginData.fullPath, "platforms", platform.toLowerCase()); const data = cacheData.nativescript || cacheData.moduleInfo; if (pluginData.isPlugin) { - pluginData.platformsDataService = data.platforms; + pluginData.platformsData = data.platforms; pluginData.pluginVariables = data.variables; } return pluginData; } - private removeDependencyFromPackageJsonContent(dependency: string, packageJsonContent: Object): {hasModifiedPackageJson: boolean, packageJsonContent: Object} { + private removeDependencyFromPackageJsonContent(dependency: string, packageJsonContent: Object): { hasModifiedPackageJson: boolean, packageJsonContent: Object } { let hasModifiedPackageJson = false; if (packageJsonContent.devDependencies && packageJsonContent.devDependencies[dependency]) { @@ -260,7 +259,7 @@ export class PluginsService implements IPluginsService { private getPackageJsonFilePathForModule(moduleName: string, projectDir: string): string { const pathToJsonFile = require.resolve(`${moduleName}/package.json`, { - paths: [projectDir] + paths: [projectDir] }); return pathToJsonFile; } @@ -333,7 +332,7 @@ export class PluginsService implements IPluginsService { let isValid = true; const installedFrameworkVersion = this.getInstalledFrameworkVersion(platform, projectData); - const pluginPlatformsData = pluginData.platformsDataService; + const pluginPlatformsData = pluginData.platformsData; if (pluginPlatformsData) { const versionRequiredByPlugin = (pluginPlatformsData)[platform]; if (!versionRequiredByPlugin) { diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 19fba7eb75..3df7b97f03 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -879,8 +879,9 @@ describe("handleNativeDependenciesChange", () => { it("ensure the correct order of pod install and merging pod's xcconfig file", async () => { const executedCocoapodsMethods: string[] = []; const projectPodfilePath = "my/test/project/platforms/ios/Podfile"; + const dir = temp.mkdirSync("myTestProjectPath"); - const testInjector = createTestInjector("myTestProjectPath", "myTestProjectName"); + const testInjector = createTestInjector(dir, "myTestProjectName"); const iOSProjectService = testInjector.resolve("iOSProjectService"); const projectData = testInjector.resolve("projectData"); diff --git a/test/plugins-service.ts b/test/plugins-service.ts index 1c86369fbc..fa15c86f45 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -207,31 +207,6 @@ async function addPluginWhenExpectingToFail(testInjector: IInjector, plugin: str assert.isTrue(isErrorThrown); } -function createAndroidManifestFile(projectFolder: string, fs: IFileSystem): void { - const manifest = ` - - - - - - - - - - - - - - - - - `; - - fs.createDirectory(path.join(projectFolder, "platforms")); - fs.createDirectory(path.join(projectFolder, "platforms", "android")); - fs.writeFile(path.join(projectFolder, "platforms", "android", "AndroidManifest.xml"), manifest); -} - describe("Plugins service", () => { let testInjector: IInjector; const commands = ["add", "install"]; @@ -505,76 +480,6 @@ describe("Plugins service", () => { }); }); - describe("merge xmls tests", () => { - beforeEach(() => { - testInjector = createTestInjector(); - testInjector.registerCommand("plugin|add", AddPluginCommand); - }); - it("fails if the plugin contains incorrect xml", async () => { - const pluginName = "mySamplePlugin"; - const projectFolder = createProjectFile(testInjector); - const pluginFolderPath = path.join(projectFolder, pluginName); - const pluginJsonData: IDependencyData = { - name: pluginName, - nativescript: { - platforms: { - android: "0.10.0" - } - }, - depth: 0, - directory: "some dir" - }; - const fs = testInjector.resolve("fs"); - fs.writeJson(path.join(pluginFolderPath, "package.json"), pluginJsonData); - - // Adds AndroidManifest.xml file in platforms/android folder - createAndroidManifestFile(projectFolder, fs); - - // Mock plugins service - const pluginsService: IPluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = async (pData: IProjectData) => { - return [{ name: "" }]; - }; - - const appDestinationDirectoryPath = path.join(projectFolder, "platforms", "android"); - - // Mock platformsDataService - const platformsDataService = testInjector.resolve("platformsDataService"); - platformsDataService.getPlatformData = (platform: string) => { - return { - appDestinationDirectoryPath: appDestinationDirectoryPath, - frameworkPackageName: "tns-android", - configurationFileName: "AndroidManifest.xml", - normalizedPlatformName: "Android", - platformProjectService: { - preparePluginNativeCode: (pluginData: IPluginData) => Promise.resolve() - } - }; - }; - - // Ensure the pluginDestinationPath folder exists - const pluginPlatformsDirPath = path.join(projectFolder, "node_modules", pluginName, "platforms", "android"); - const projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); - fs.ensureDirectoryExists(pluginPlatformsDirPath); - - // Creates invalid plugin's AndroidManifest.xml file - const xml = '' + - '' + - ''; - const pluginConfigurationFilePath = path.join(pluginPlatformsDirPath, "AndroidManifest.xml"); - fs.writeFile(pluginConfigurationFilePath, xml); - - // Expected error message. The assertion happens in mockBeginCommand - const expectedErrorMessage = `Exception: Invalid xml file ${pluginConfigurationFilePath}. Additional technical information: element parse error: Exception: Invalid xml file ` + - `${pluginConfigurationFilePath}. Additional technical information: unclosed xml attribute` + - `\n@#[line:1,col:39].` + - `\n@#[line:1,col:39].`; - mockBeginCommand(testInjector, expectedErrorMessage); - await pluginsService.preparePluginNativeCode({pluginData: pluginsService.convertToPluginData(pluginJsonData, projectData.projectDir), platform: "android", projectData}); - }); - }); - describe("preparePluginNativeCode", () => { const setupTest = (opts: { hasChangesInShasums?: boolean, newPluginHashes?: IStringDictionary, buildDataFileExists?: boolean, hasPluginPlatformsDir?: boolean }): any => { const testData: any = { @@ -599,7 +504,8 @@ describe("Plugins service", () => { const pluginHashes = opts.newPluginHashes || { "file1": "hash1" }; const samplePluginData: IPluginData = { fullPath: "plugin_full_path", - name: "plugin_name" + name: "plugin_name", + pluginPlatformsFolderPath: (_platform: string) => path.join("plugin_dir", "platforms", _platform.toLowerCase()) }; unitTestsInjector.register("filesHashService", { @@ -646,22 +552,85 @@ describe("Plugins service", () => { it("does not prepare the files when plugin does not have platforms dir", async () => { const testData = setupTest({ hasPluginPlatformsDir: false }); - await testData.pluginsService.preparePluginNativeCode({pluginData: testData.pluginData, platform, projectData}); + await testData.pluginsService.preparePluginNativeCode({ pluginData: testData.pluginData, platform, projectData }); assert.isFalse(testData.isPreparePluginNativeCodeCalled); }); it("prepares the files when plugin has platforms dir and has not been built before", async () => { const newPluginHashes = { "file": "hash" }; const testData = setupTest({ newPluginHashes, hasPluginPlatformsDir: true }); - await testData.pluginsService.preparePluginNativeCode({pluginData: testData.pluginData, platform, projectData}); + await testData.pluginsService.preparePluginNativeCode({ pluginData: testData.pluginData, platform, projectData }); assert.isTrue(testData.isPreparePluginNativeCodeCalled); assert.deepEqual(testData.dataPassedToWriteJson, { [testData.pluginData.name]: newPluginHashes }); }); it("does not prepare the files when plugin has platforms dir and files have not changed since then", async () => { const testData = setupTest({ hasChangesInShasums: false, buildDataFileExists: true, hasPluginPlatformsDir: true }); - await testData.pluginsService.preparePluginNativeCode({pluginData: testData.pluginData, platform, projectData}); + await testData.pluginsService.preparePluginNativeCode({ pluginData: testData.pluginData, platform, projectData }); assert.isFalse(testData.isPreparePluginNativeCodeCalled); }); }); + + describe("convertToPluginData", () => { + const createUnitTestsInjector = () => { + const unitTestsInjector = new Yok(); + unitTestsInjector.register("platformsDataService", {}); + unitTestsInjector.register("filesHashService", {}); + unitTestsInjector.register("fs", {}); + unitTestsInjector.register("packageManager", {}); + unitTestsInjector.register("options", {}); + unitTestsInjector.register("logger", {}); + unitTestsInjector.register("errors", {}); + unitTestsInjector.register("injector", unitTestsInjector); + unitTestsInjector.register("mobileHelper", MobileHelper); + unitTestsInjector.register("devicePlatformsConstants", DevicePlatformsConstants); + unitTestsInjector.register("nodeModulesDependenciesBuilder", {}); + return unitTestsInjector; + }; + + const pluginDir = "pluginDir"; + const dataFromPluginPackageJson = { + name: "name", + version: "1.0.0", + directory: pluginDir, + nativescript: { + platforms: { + ios: "6.0.0", + android: "6.0.0" + } + } + }; + + it("returns correct pluginData", () => { + const unitTestsInjector = createUnitTestsInjector(); + const pluginsService: PluginsService = unitTestsInjector.resolve(PluginsService); + const pluginData = pluginsService.convertToPluginData(dataFromPluginPackageJson, "my project dir"); + // Remove the comparison of a function + delete pluginData["pluginPlatformsFolderPath"]; + assert.deepEqual(pluginData, { + name: "name", + version: "1.0.0", + fullPath: pluginDir, + isPlugin: true, + platformsData: { android: "6.0.0", ios: "6.0.0" }, + pluginVariables: undefined + }); + }); + + it("always returns lowercased platform in the path to plugins dir", () => { + const unitTestsInjector = createUnitTestsInjector(); + const pluginsService: PluginsService = unitTestsInjector.resolve(PluginsService); + const pluginData = pluginsService.convertToPluginData(dataFromPluginPackageJson, "my project dir"); + + const expectediOSPath = path.join(pluginDir, "platforms", "ios"); + const expectedAndroidPath = path.join(pluginDir, "platforms", "android"); + assert.equal(pluginData.pluginPlatformsFolderPath("iOS"), expectediOSPath); + assert.equal(pluginData.pluginPlatformsFolderPath("ios"), expectediOSPath); + assert.equal(pluginData.pluginPlatformsFolderPath("IOS"), expectediOSPath); + + assert.equal(pluginData.pluginPlatformsFolderPath("Android"), expectedAndroidPath); + assert.equal(pluginData.pluginPlatformsFolderPath("android"), expectedAndroidPath); + assert.equal(pluginData.pluginPlatformsFolderPath("ANDROID"), expectedAndroidPath); + }); + }); }); diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index 343cfd514e..122a2d5499 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -171,7 +171,8 @@ function createTestInjector(options?: { mapFilePath: (filePath: string) => path.join(path.join(platformsDirPath, "app"), path.relative(path.join(projectDirPath, "app"), filePath)) }); injector.register("previewDevicesService", { - getConnectedDevices: () => [deviceMockData] + getConnectedDevices: () => [deviceMockData], + getDevicesForPlatform: (platform: string): Device[] => [deviceMockData] }); injector.register("previewAppFilesService", PreviewAppFilesService); injector.register("previewQrCodeService", {