From 77dd128b9586460ec91d7115bc17c8411b132587 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Mon, 12 Nov 2018 17:43:39 +0200 Subject: [PATCH 1/2] fix: fix project creation with github url passed as template --- lib/declarations.d.ts | 27 +++++++ lib/node-package-manager.ts | 56 ++++++++++++++- lib/npm-installation-manager.ts | 30 ++++---- lib/services/project-templates-service.ts | 35 ++++----- test/node-package-manager.ts | 88 +++++++++++++++++++++++ test/project-templates-service.ts | 38 +++++++--- test/services/android-debug-service.ts | 1 + test/stubs.ts | 55 +++++++++++++- 8 files changed, 275 insertions(+), 55 deletions(-) create mode 100644 test/node-package-manager.ts diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 67ecb3381f..c42b2dfab4 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -25,6 +25,27 @@ interface INodePackageManager { */ view(packageName: string, config: Object): Promise; + /** + * Checks if the specified string is name of a packaged published in the NPM registry. + * @param {string} packageName The string to be checked. + * @return {Promise} True if the specified string is a registered package name, false otherwise. + */ + isRegistered(packageName: string): Promise; + + /** + * Separates the package name and version from a specified fullPackageName. + * @param {string} fullPackageName The full name of the package like nativescript@10.0.0. + * @return {INpmPackageNameParts} An object containing the separated package name and version. + */ + getPackageNameParts(fullPackageName: string): INpmPackageNameParts + + /** + * Returns the full name of an npm package based on the provided name and version. + * @param {INpmPackageNameParts} packageNameParts An object containing the package name and version. + * @return {string} The full name of the package like nativescript@10.0.0. + */ + getPackageFullName(packageNameParts: INpmPackageNameParts): string + /** * Searches for a package. * @param {string[]} filter Keywords with which to perform the search. @@ -59,6 +80,7 @@ interface INpmInstallationManager { getLatestVersion(packageName: string): Promise; getNextVersion(packageName: string): Promise; getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise; + getLatestCompatibleVersionSafe(packageName: string, referenceVersion?: string): Promise; getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise; } @@ -353,6 +375,11 @@ interface INpmsPackageData { maintainers: INpmsUser[]; } +interface INpmPackageNameParts { + name: string; + version: string; +} + interface IUsername { username: string; } diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts index 1a82ac4cae..282fc974ac 100644 --- a/lib/node-package-manager.ts +++ b/lib/node-package-manager.ts @@ -112,6 +112,46 @@ export class NodePackageManager implements INodePackageManager { return JSON.parse(viewResult); } + public async isRegistered(packageName: string): Promise { + if (this.isURL(packageName) || this.$fs.exists(packageName) || this.isTgz(packageName)) { + return false; + } + + try { + const viewResult = await this.view(packageName, { name: true }); + + // `npm view nonExistingPackageName` will return `nativescript` + // if executed in the root dir of the CLI (npm 6.4.1) + const packageNameRegex = new RegExp(packageName, "i"); + const isProperResult = packageNameRegex.test(viewResult); + + return isProperResult; + } catch (e) { + return false; + } + } + + public getPackageNameParts(fullPackageName: string): INpmPackageNameParts { + // support @ syntax, for example typescript@1.0.0 + // support @ syntax, for example @nativescript/vue-template@1.0.0 + const lastIndexOfAtSign = fullPackageName.lastIndexOf("@"); + let version = ""; + let templateName = ""; + if (lastIndexOfAtSign > 0) { + templateName = fullPackageName.substr(0, lastIndexOfAtSign).toLowerCase(); + version = fullPackageName.substr(lastIndexOfAtSign + 1); + } + + return { + name: templateName || fullPackageName, + version: version + } + } + + public getPackageFullName(packageNameParts: INpmPackageNameParts): string { + return packageNameParts.version ? `${packageNameParts.name}@${packageNameParts.version}` : packageNameParts.name; + } + public async searchNpms(keyword: string): Promise { // TODO: Fix the generation of url - in case it contains @ or / , the call may fail. const httpRequestResult = await this.$httpClient.httpRequest(`https://api.npms.io/v2/search?q=keywords:${keyword}`); @@ -136,6 +176,16 @@ export class NodePackageManager implements INodePackageManager { return path.join(cachePath.trim(), CACACHE_DIRECTORY_NAME); } + private isTgz(packageName: string): boolean { + return packageName.indexOf(".tgz") >= 0; + } + + private isURL(str: string): boolean { + const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; + const url = new RegExp(urlRegex, 'i'); + return str.length < 2083 && url.test(str); + } + private getNpmExecutableName(): string { let npmExecutableName = "npm"; @@ -153,7 +203,7 @@ export class NodePackageManager implements INodePackageManager { array.push(`--${flag}`); array.push(`${config[flag]}`); } else if (config[flag]) { - if (flag === "dist-tags" || flag === "versions") { + if (flag === "dist-tags" || flag === "versions" || flag === "name") { array.push(` ${flag}`); continue; } @@ -171,8 +221,8 @@ export class NodePackageManager implements INodePackageManager { // TODO: Add tests for this functionality try { const originalOutput: INpmInstallCLIResult | INpm5InstallCliResult = JSON.parse(npmDryRunInstallOutput); - const npm5Output = originalOutput; - const npmOutput = originalOutput; + const npm5Output = originalOutput; + const npmOutput = originalOutput; let name: string; _.forOwn(npmOutput.dependencies, (peerDependency: INpmPeerDependencyInfo, key: string) => { if (!peerDependency.required && !peerDependency.peerMissing) { diff --git a/lib/npm-installation-manager.ts b/lib/npm-installation-manager.ts index b3501286ca..0f3499f328 100644 --- a/lib/npm-installation-manager.ts +++ b/lib/npm-installation-manager.ts @@ -38,6 +38,16 @@ export class NpmInstallationManager implements INpmInstallationManager { return maxSatisfying || latestVersion; } + public async getLatestCompatibleVersionSafe(packageName: string, referenceVersion?: string): Promise { + let version = ""; + const canGetVersionFromNpm = await this.$npm.isRegistered(packageName); + if (canGetVersionFromNpm) { + version = await this.getLatestCompatibleVersion(packageName, referenceVersion); + } + + return version; + } + public async install(packageToInstall: string, projectDir: string, opts?: INpmInstallOptions): Promise { try { const pathToSave = projectDir; @@ -100,6 +110,7 @@ export class NpmInstallationManager implements INpmInstallationManager { if (this.$fs.exists(pathToInspector)) { return true; } + return false; } @@ -109,28 +120,13 @@ export class NpmInstallationManager implements INpmInstallationManager { packageName = possiblePackageName; } - // check if the packageName is url or local file and if it is, let npm install deal with the version - if (this.isURL(packageName) || this.$fs.exists(packageName) || this.isTgz(packageName)) { - version = null; - } else { - version = version || await this.getLatestCompatibleVersion(packageName); - } - + version = version || await this.getLatestCompatibleVersionSafe(packageName); const installResultInfo = await this.npmInstall(packageName, pathToSave, version, dependencyType); const installedPackageName = installResultInfo.name; const pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName); - return pathToInstalledPackage; - } - private isTgz(packageName: string): boolean { - return packageName.indexOf(".tgz") >= 0; - } - - private isURL(str: string): boolean { - const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; - const url = new RegExp(urlRegex, 'i'); - return str.length < 2083 && url.test(str); + return pathToInstalledPackage; } private async npmInstall(packageName: string, pathToSave: string, version: string, dependencyType: string): Promise { diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 712202148f..89cc5f85b1 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -12,42 +12,33 @@ export class ProjectTemplatesService implements IProjectTemplatesService { private $logger: ILogger, private $npmInstallationManager: INpmInstallationManager, private $pacoteService: IPacoteService, - private $errors: IErrors) { } + private $errors: IErrors, + private $npm: INodePackageManager) { } - public async prepareTemplate(originalTemplateName: string, projectDir: string): Promise { - if (!originalTemplateName) { - originalTemplateName = constants.RESERVED_TEMPLATE_NAMES["default"]; + public async prepareTemplate(templateValue: string, projectDir: string): Promise { + if (!templateValue) { + templateValue = constants.RESERVED_TEMPLATE_NAMES["default"]; } - // support @ syntax, for example typescript@1.0.0 - // support @ syntax, for example @nativescript/vue-template@1.0.0 - const lastIndexOfAtSign = originalTemplateName.lastIndexOf("@"); - let name = originalTemplateName; - let version = ""; - if (lastIndexOfAtSign > 0) { - name = originalTemplateName.substr(0, lastIndexOfAtSign); - version = originalTemplateName.substr(lastIndexOfAtSign + 1); - } + const templateNameParts = this.$npm.getPackageNameParts(templateValue); + templateValue = constants.RESERVED_TEMPLATE_NAMES[templateNameParts.name] || templateNameParts.name; - const templateName = constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()] || name; - if (!this.$fs.exists(templateName)) { - version = version || await this.$npmInstallationManager.getLatestCompatibleVersion(templateName); - } + let version = templateNameParts.version || await this.$npmInstallationManager.getLatestCompatibleVersionSafe(templateValue); + const fullTemplateName = this.$npm.getPackageFullName({ name: templateValue, version: version }); - const fullTemplateName = version ? `${templateName}@${version}` : templateName; const templatePackageJsonContent = await this.getTemplatePackageJsonContent(fullTemplateName); const templateVersion = await this.getTemplateVersion(fullTemplateName); let templatePath = null; if (templateVersion === constants.TemplateVersions.v1) { - templatePath = await this.prepareNativeScriptTemplate(templateName, version, projectDir); + templatePath = await this.prepareNativeScriptTemplate(templateValue, version, projectDir); // this removes dependencies from templates so they are not copied to app folder this.$fs.deleteDirectory(path.join(templatePath, constants.NODE_MODULES_FOLDER_NAME)); } - await this.$analyticsService.track("Template used for project creation", templateName); + await this.$analyticsService.track("Template used for project creation", templateValue); - const templateNameToBeTracked = this.getTemplateNameToBeTracked(templateName, templatePackageJsonContent); + const templateNameToBeTracked = this.getTemplateNameToBeTracked(templateValue, templatePackageJsonContent); if (templateNameToBeTracked) { await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: constants.TrackActionNames.CreateProject, @@ -61,7 +52,7 @@ export class ProjectTemplatesService implements IProjectTemplatesService { }); } - return { templateName, templatePath, templateVersion, templatePackageJsonContent, version }; + return { templateName: templateValue, templatePath, templateVersion, templatePackageJsonContent, version }; } private async getTemplateVersion(templateName: string): Promise { diff --git a/test/node-package-manager.ts b/test/node-package-manager.ts new file mode 100644 index 0000000000..65e53bfec7 --- /dev/null +++ b/test/node-package-manager.ts @@ -0,0 +1,88 @@ +import { Yok } from "../lib/common/yok"; +import * as stubs from "./stubs"; +import { assert } from "chai"; +import { NodePackageManager } from "../lib/node-package-manager"; + +function createTestInjector(configuration: { +} = {}): IInjector { + const injector = new Yok(); + injector.register("hostInfo", {}); + injector.register("errors", stubs.ErrorsStub); + injector.register("logger", stubs.LoggerStub); + injector.register("childProcess", stubs.ChildProcessStub); + injector.register("httpClient", {}); + injector.register("fs", stubs.FileSystemStub); + injector.register("npm", NodePackageManager); + + return injector; +} + +describe.only("node-package-manager", () => { + + describe("getPackageNameParts", () => { + [ + { + name: "should return both name and version when valid fullName passed", + templateFullName: "some-template@1.0.0", + expectedVersion: "1.0.0", + expectedName: "some-template", + }, + { + name: "should return both name and version when valid fullName with scope passed", + templateFullName: "@nativescript/some-template@1.0.0", + expectedVersion: "1.0.0", + expectedName: "@nativescript/some-template", + }, + { + name: "should return only name when version is not specified and the template is scoped", + templateFullName: "@nativescript/some-template", + expectedVersion: "", + expectedName: "@nativescript/some-template", + }, + { + name: "should return only name when version is not specified", + templateFullName: "some-template", + expectedVersion: "", + expectedName: "some-template", + } + ].forEach(testCase => { + it(testCase.name, async () => { + const testInjector = createTestInjector(); + const npm = testInjector.resolve("npm"); + const templateNameParts = await npm.getPackageNameParts(testCase.templateFullName); + assert.strictEqual(templateNameParts.name, testCase.expectedName); + assert.strictEqual(templateNameParts.version, testCase.expectedVersion); + }); + }); + }); + + describe("getPackageFullName", () => { + [ + { + name: "should return name and version when specified", + templateName: "some-template", + templateVersion: "1.0.0", + expectedFullName: "some-template@1.0.0", + }, + { + name: "should return only the github url when no version specified", + templateName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", + templateVersion: "", + expectedFullName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", + }, + { + name: "should return only the name when no version specified", + templateName: "some-template", + templateVersion: "", + expectedFullName: "some-template", + } + ].forEach(testCase => { + it(testCase.name, async () => { + const testInjector = createTestInjector(); + const npm = testInjector.resolve("npm"); + const templateFullName = await npm.getPackageFullName({ name: testCase.templateName, version: testCase.templateVersion }); + assert.strictEqual(templateFullName, testCase.expectedFullName); + }); + }); + }); +}); diff --git a/test/project-templates-service.ts b/test/project-templates-service.ts index 48743641c4..f96eeb4e60 100644 --- a/test/project-templates-service.ts +++ b/test/project-templates-service.ts @@ -10,7 +10,13 @@ let isDeleteDirectoryCalledForNodeModulesDir = false; const nativeScriptValidatedTemplatePath = "nsValidatedTemplatePath"; const compatibleTemplateVersion = "1.2.3"; -function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, packageJsonContent?: any } = {}): IInjector { + +function createTestInjector(configuration: { + shouldNpmInstallThrow?: boolean, + packageJsonContent?: any, + packageVersion?: string, + packageName?: string +} = {}): IInjector { const injector = new Yok(); injector.register("errors", stubs.ErrorsStub); injector.register("logger", stubs.LoggerStub); @@ -26,28 +32,38 @@ function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, pa } }); - injector.register("npm", { - install: (packageName: string, pathToSave: string, config?: any) => { + class NpmStub extends stubs.NodePackageManagerStub { + public async install(packageName: string, pathToSave: string, config: INodePackageManagerInstallOptions): Promise { if (configuration.shouldNpmInstallThrow) { throw new Error("NPM install throws error."); } - return "sample result"; + return { name: "Some Result", version: "1" }; } - }); + getPackageNameParts(fullPackageName: string): INpmPackageNameParts { + return { + name: configuration.packageName || fullPackageName, + version: configuration.packageVersion || "" + } + } + } - injector.register("npmInstallationManager", { - install: (packageName: string, options?: INpmInstallOptions) => { + injector.register("npm", NpmStub); + + class NpmInstallationManagerStub extends stubs.NpmInstallationManagerStub { + async install(packageName: string, pathToSave?: string, options?: INpmInstallOptions): Promise { if (configuration.shouldNpmInstallThrow) { throw new Error("NPM install throws error."); } return Promise.resolve(nativeScriptValidatedTemplatePath); - }, - getLatestCompatibleVersion: (packageName: string) => { + } + async getLatestCompatibleVersionSafe(packageName: string): Promise { return compatibleTemplateVersion; } - }); + } + + injector.register("npmInstallationManager", NpmInstallationManagerStub); injector.register("projectTemplatesService", ProjectTemplatesService); @@ -253,7 +269,7 @@ describe("project-templates-service", () => { } ].forEach(testCase => { it(testCase.name, async () => { - const testInjector = createTestInjector(); + const testInjector = createTestInjector({ packageVersion: testCase.expectedVersion, packageName: testCase.expectedTemplateName }); const projectTemplatesService = testInjector.resolve("projectTemplatesService"); const { version, templateName } = await projectTemplatesService.prepareTemplate(testCase.templateName, "tempFolder"); assert.strictEqual(version, testCase.expectedVersion); diff --git a/test/services/android-debug-service.ts b/test/services/android-debug-service.ts index f8aa1e90ed..234d8aa26c 100644 --- a/test/services/android-debug-service.ts +++ b/test/services/android-debug-service.ts @@ -27,6 +27,7 @@ const createTestInjector = (): IInjector => { testInjector.register("devicesService", {}); testInjector.register("errors", stubs.ErrorsStub); testInjector.register("logger", stubs.LoggerStub); + testInjector.register("npm", stubs.NodePackageManagerStub); testInjector.register("androidDeviceDiscovery", {}); testInjector.register("androidProcessService", {}); testInjector.register("net", {}); diff --git a/test/stubs.ts b/test/stubs.ts index 3880b3eeaf..f642c1426b 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -243,11 +243,62 @@ export class NpmInstallationManagerStub implements INpmInstallationManager { return Promise.resolve(""); } + async getLatestCompatibleVersionSafe(packageName: string): Promise { + return Promise.resolve(""); + } + async getInspectorFromCache(name: string, projectDir: string): Promise { return Promise.resolve(""); } } +export class NodePackageManagerStub implements INodePackageManager { + constructor() { } + + public async install(packageName: string, pathToSave: string, config: INodePackageManagerInstallOptions): Promise { + return null; + } + + public async uninstall(packageName: string, config?: any, path?: string): Promise { + return ""; + } + + public async search(filter: string[], config: any): Promise { + return ""; + } + + public async view(packageName: string, config: Object): Promise { + return {}; + } + + public async isRegistered(packageName: string): Promise { + return true; + } + + public getPackageNameParts(fullPackageName: string): INpmPackageNameParts { + return { + name: fullPackageName, + version: "" + } + } + + public getPackageFullName(packageNameParts: INpmPackageNameParts): string { + return packageNameParts.version ? `${packageNameParts.name}@${packageNameParts.version}` : packageNameParts.name; + } + + public async searchNpms(keyword: string): Promise { + return null; + } + + public async getRegistryPackageData(packageName: string): Promise { + return null + } + + public async getCachePath(): Promise { + return ""; + } +} + export class ProjectDataStub implements IProjectData { projectDir: string; projectName: string; @@ -275,7 +326,7 @@ export class ProjectDataStub implements IProjectData { public initializeProjectData(projectDir?: string): void { this.projectDir = this.projectDir || projectDir; - this.projectIdentifiers = { android: "", ios: ""}; + this.projectIdentifiers = { android: "", ios: "" }; this.projectId = ""; } public initializeProjectDataFromContent(): void { @@ -429,7 +480,7 @@ export class PlatformsDataStub extends EventEmitter implements IPlatformsData { normalizedPlatformName: "", appDestinationDirectoryPath: "", deviceBuildOutputPath: "", - getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: []}), + getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }), frameworkFilesExtensions: [], relativeToFrameworkConfigurationFilePath: "", fastLivesyncFileExtensions: [] From b2afcf77ab08000ec28faf0ecfb1a9c7d8075b79 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Tue, 13 Nov 2018 11:43:55 +0200 Subject: [PATCH 2/2] fix: fix linting errors --- lib/node-package-manager.ts | 2 +- lib/services/project-templates-service.ts | 2 +- test/node-package-manager.ts | 150 +++++++++++----------- test/project-templates-service.ts | 3 +- test/stubs.ts | 4 +- 5 files changed, 80 insertions(+), 81 deletions(-) diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts index 282fc974ac..77ad88c01b 100644 --- a/lib/node-package-manager.ts +++ b/lib/node-package-manager.ts @@ -145,7 +145,7 @@ export class NodePackageManager implements INodePackageManager { return { name: templateName || fullPackageName, version: version - } + }; } public getPackageFullName(packageNameParts: INpmPackageNameParts): string { diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 89cc5f85b1..be5d4555e2 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -23,7 +23,7 @@ export class ProjectTemplatesService implements IProjectTemplatesService { const templateNameParts = this.$npm.getPackageNameParts(templateValue); templateValue = constants.RESERVED_TEMPLATE_NAMES[templateNameParts.name] || templateNameParts.name; - let version = templateNameParts.version || await this.$npmInstallationManager.getLatestCompatibleVersionSafe(templateValue); + const version = templateNameParts.version || await this.$npmInstallationManager.getLatestCompatibleVersionSafe(templateValue); const fullTemplateName = this.$npm.getPackageFullName({ name: templateValue, version: version }); const templatePackageJsonContent = await this.getTemplatePackageJsonContent(fullTemplateName); diff --git a/test/node-package-manager.ts b/test/node-package-manager.ts index 65e53bfec7..48b3c6eb81 100644 --- a/test/node-package-manager.ts +++ b/test/node-package-manager.ts @@ -5,84 +5,84 @@ import { NodePackageManager } from "../lib/node-package-manager"; function createTestInjector(configuration: { } = {}): IInjector { - const injector = new Yok(); - injector.register("hostInfo", {}); - injector.register("errors", stubs.ErrorsStub); - injector.register("logger", stubs.LoggerStub); - injector.register("childProcess", stubs.ChildProcessStub); - injector.register("httpClient", {}); - injector.register("fs", stubs.FileSystemStub); - injector.register("npm", NodePackageManager); + const injector = new Yok(); + injector.register("hostInfo", {}); + injector.register("errors", stubs.ErrorsStub); + injector.register("logger", stubs.LoggerStub); + injector.register("childProcess", stubs.ChildProcessStub); + injector.register("httpClient", {}); + injector.register("fs", stubs.FileSystemStub); + injector.register("npm", NodePackageManager); - return injector; + return injector; } -describe.only("node-package-manager", () => { +describe("node-package-manager", () => { - describe("getPackageNameParts", () => { - [ - { - name: "should return both name and version when valid fullName passed", - templateFullName: "some-template@1.0.0", - expectedVersion: "1.0.0", - expectedName: "some-template", - }, - { - name: "should return both name and version when valid fullName with scope passed", - templateFullName: "@nativescript/some-template@1.0.0", - expectedVersion: "1.0.0", - expectedName: "@nativescript/some-template", - }, - { - name: "should return only name when version is not specified and the template is scoped", - templateFullName: "@nativescript/some-template", - expectedVersion: "", - expectedName: "@nativescript/some-template", - }, - { - name: "should return only name when version is not specified", - templateFullName: "some-template", - expectedVersion: "", - expectedName: "some-template", - } - ].forEach(testCase => { - it(testCase.name, async () => { - const testInjector = createTestInjector(); - const npm = testInjector.resolve("npm"); - const templateNameParts = await npm.getPackageNameParts(testCase.templateFullName); - assert.strictEqual(templateNameParts.name, testCase.expectedName); - assert.strictEqual(templateNameParts.version, testCase.expectedVersion); - }); - }); - }); + describe("getPackageNameParts", () => { + [ + { + name: "should return both name and version when valid fullName passed", + templateFullName: "some-template@1.0.0", + expectedVersion: "1.0.0", + expectedName: "some-template", + }, + { + name: "should return both name and version when valid fullName with scope passed", + templateFullName: "@nativescript/some-template@1.0.0", + expectedVersion: "1.0.0", + expectedName: "@nativescript/some-template", + }, + { + name: "should return only name when version is not specified and the template is scoped", + templateFullName: "@nativescript/some-template", + expectedVersion: "", + expectedName: "@nativescript/some-template", + }, + { + name: "should return only name when version is not specified", + templateFullName: "some-template", + expectedVersion: "", + expectedName: "some-template", + } + ].forEach(testCase => { + it(testCase.name, async () => { + const testInjector = createTestInjector(); + const npm = testInjector.resolve("npm"); + const templateNameParts = await npm.getPackageNameParts(testCase.templateFullName); + assert.strictEqual(templateNameParts.name, testCase.expectedName); + assert.strictEqual(templateNameParts.version, testCase.expectedVersion); + }); + }); + }); - describe("getPackageFullName", () => { - [ - { - name: "should return name and version when specified", - templateName: "some-template", - templateVersion: "1.0.0", - expectedFullName: "some-template@1.0.0", - }, - { - name: "should return only the github url when no version specified", - templateName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", - templateVersion: "", - expectedFullName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", - }, - { - name: "should return only the name when no version specified", - templateName: "some-template", - templateVersion: "", - expectedFullName: "some-template", - } - ].forEach(testCase => { - it(testCase.name, async () => { - const testInjector = createTestInjector(); - const npm = testInjector.resolve("npm"); - const templateFullName = await npm.getPackageFullName({ name: testCase.templateName, version: testCase.templateVersion }); - assert.strictEqual(templateFullName, testCase.expectedFullName); - }); - }); - }); + describe("getPackageFullName", () => { + [ + { + name: "should return name and version when specified", + templateName: "some-template", + templateVersion: "1.0.0", + expectedFullName: "some-template@1.0.0", + }, + { + name: "should return only the github url when no version specified", + templateName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", + templateVersion: "", + expectedFullName: "https://github.com/NativeScript/template-drawer-navigation-ng#master", + }, + { + name: "should return only the name when no version specified", + templateName: "some-template", + templateVersion: "", + expectedFullName: "some-template", + } + ].forEach(testCase => { + it(testCase.name, async () => { + const testInjector = createTestInjector(); + const npm = testInjector.resolve("npm"); + const templateFullName = await npm.getPackageFullName({ name: testCase.templateName, version: testCase.templateVersion }); + assert.strictEqual(templateFullName, testCase.expectedFullName); + }); + }); + }); }); diff --git a/test/project-templates-service.ts b/test/project-templates-service.ts index f96eeb4e60..6ecdd1bac2 100644 --- a/test/project-templates-service.ts +++ b/test/project-templates-service.ts @@ -10,7 +10,6 @@ let isDeleteDirectoryCalledForNodeModulesDir = false; const nativeScriptValidatedTemplatePath = "nsValidatedTemplatePath"; const compatibleTemplateVersion = "1.2.3"; - function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, packageJsonContent?: any, @@ -44,7 +43,7 @@ function createTestInjector(configuration: { return { name: configuration.packageName || fullPackageName, version: configuration.packageVersion || "" - } + }; } } diff --git a/test/stubs.ts b/test/stubs.ts index f642c1426b..0708ee4dc2 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -279,7 +279,7 @@ export class NodePackageManagerStub implements INodePackageManager { return { name: fullPackageName, version: "" - } + }; } public getPackageFullName(packageNameParts: INpmPackageNameParts): string { @@ -291,7 +291,7 @@ export class NodePackageManagerStub implements INodePackageManager { } public async getRegistryPackageData(packageName: string): Promise { - return null + return null; } public async getCachePath(): Promise {