| /** |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| const fs = require('fs-extra'); |
| const rewire = require('rewire'); |
| const path = require('path'); |
| const requireFresh = require('import-fresh'); |
| const create = require('..'); |
| const { CordovaError, ConfigParser } = require('cordova-common'); |
| const { tmpDir, createWith, createWithMockFetch, expectRejection } = require('./helpers'); |
| |
| const appName = 'TestBase'; |
| const appId = 'org.testing'; |
| const appVersion = '1.0.0'; |
| const project = path.join(tmpDir, appName); |
| |
| let opts; |
| |
| beforeEach(() => { |
| fs.emptyDirSync(tmpDir); |
| opts = { name: appName, id: appId }; |
| }); |
| afterAll(() => { |
| process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows. |
| fs.removeSync(tmpDir); |
| }); |
| |
| describe('cordova create checks for valid-identifier', () => { |
| const error = new CordovaError('is not a valid identifier'); |
| |
| it('should reject reserved words from start of id', () => { |
| opts.id = 'int.bob'; |
| return expectRejection(create(project, opts), error); |
| }); |
| |
| it('should reject reserved words from end of id', () => { |
| opts.id = 'bob.class'; |
| return expectRejection(create(project, opts), error); |
| }); |
| }); |
| |
| describe('create end-to-end', () => { |
| function checkCommonArtifacts () { |
| // Check that www dir exist |
| expect(path.join(project, 'www')).toExist(); |
| |
| // Check that index.html exists inside of www |
| expect(path.join(project, 'www', 'index.html')).toExist(); |
| |
| // Check that www files don't get copied to the top level |
| expect(path.join(project, 'index.html')).not.toExist(); |
| |
| // index.js and template subdir folder should not exist in top level |
| // (inner files should be copied to the project top level folder) |
| expect(path.join(project, 'index.js')).not.toExist(); |
| expect(path.join(project, 'template')).not.toExist(); |
| |
| // Check that config.xml does not exist inside of www |
| expect(path.join(project, 'www', 'config.xml')).not.toExist(); |
| |
| // Check that config.xml was updated correctly |
| const configXml = new ConfigParser(path.join(project, 'config.xml')); |
| expect(configXml.packageName()).toEqual(appId); |
| expect(configXml.name()).toEqual(appName); |
| } |
| |
| // Check that we got package.json and it was updated correctly |
| function checkPackageJson () { |
| const pkg = requireFresh(path.join(project, 'package.json')); |
| expect(pkg.name).toEqual(appId); |
| expect(pkg.displayName).toEqual(appName); |
| } |
| |
| // Check that we got no package.json |
| function checkNoPackageJson () { |
| expect(path.join(project, 'package.json')).not.toExist(); |
| } |
| |
| // Check that we did use the default template |
| function checkDefaultTemplate () { |
| const pkg = requireFresh(path.join(project, 'package.json')); |
| expect(pkg.author).toEqual('Apache Cordova Team'); |
| expect(pkg.version).toEqual(appVersion); |
| |
| const configXml = new ConfigParser(path.join(project, 'config.xml')); |
| expect(configXml.author()).toEqual('Apache Cordova Team'); |
| expect(configXml.version()).toEqual(appVersion); |
| } |
| |
| // Check that we did not use the default template |
| function checkNotDefaultTemplate () { |
| const configXml = new ConfigParser(path.join(project, 'config.xml')); |
| expect(configXml.author()).not.toEqual('Apache Cordova Team'); |
| expect(configXml.version()).toEqual('0.0.1'); |
| } |
| |
| function checkProjectCreatedWithDefaultTemplate () { |
| checkCommonArtifacts(); |
| checkPackageJson(); |
| checkDefaultTemplate(); |
| } |
| |
| it('should successfully run without template and use default hello-world app', () => { |
| // Create a real project with no template |
| // use default cordova-app-hello-world app |
| return create(project, opts) |
| .then(checkProjectCreatedWithDefaultTemplate); |
| }); |
| |
| it('should successfully run with Git URL', () => { |
| // Create a real project with git URL as template |
| opts.template = 'https://github.com/apache/cordova-app-hello-world'; |
| return createWithMockFetch(project, opts) |
| .then(fetchSpy => { |
| expect(fetchSpy).toHaveBeenCalledTimes(1); |
| expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template); |
| }) |
| .then(checkProjectCreatedWithDefaultTemplate); |
| }); |
| |
| it('should successfully run with NPM package (specific version)', () => { |
| // Create a real project with npm module as template |
| opts.template = 'phonegap-template-vue-f7-tabs@1'; |
| return createWithMockFetch(project, opts) |
| .then(fetchSpy => { |
| expect(fetchSpy).toHaveBeenCalledTimes(1); |
| expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template); |
| }) |
| .then(checkProjectCreatedWithDefaultTemplate); |
| }); |
| |
| it('should successfully run with NPM package (no specific version)', () => { |
| // Create a real project with npm module as template |
| opts.template = 'phonegap-template-vue-f7-tabs'; |
| return createWithMockFetch(project, opts) |
| .then(fetchSpy => { |
| expect(fetchSpy).toHaveBeenCalledTimes(1); |
| expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template); |
| }) |
| .then(checkProjectCreatedWithDefaultTemplate); |
| }); |
| |
| it('should successfully run with local template having no package.json in template dir', () => { |
| opts.template = path.join(__dirname, 'templates/withsubdirectory'); |
| return create(project, opts) |
| .then(checkCommonArtifacts) |
| .then(checkNoPackageJson) |
| .then(checkNotDefaultTemplate); |
| }); |
| |
| it('should successfully run with local template having package.json in template dir', () => { |
| opts.template = path.join(__dirname, 'templates/withsubdirectory_package_json'); |
| return create(project, opts) |
| .then(checkCommonArtifacts) |
| .then(checkPackageJson) |
| .then(checkNotDefaultTemplate); |
| }); |
| |
| it('should successfully run with existing, empty destination', () => { |
| fs.ensureDirSync(project); |
| return create(project, opts) |
| .then(checkProjectCreatedWithDefaultTemplate); |
| }); |
| |
| it('should rename all gitignore files in template to .gitignore', () => { |
| const baseTemplatePkg = path.join(__dirname, 'templates/withsubdirectory'); |
| const templatePkg = path.join(tmpDir, 'gitignore-template'); |
| fs.copySync(baseTemplatePkg, templatePkg); |
| |
| // Setup a few gitignore files that should be renamed (or not) |
| const templateDir = path.join(templatePkg, 'template'); |
| fs.ensureFileSync(path.join(templateDir, 'gitignore')); |
| fs.ensureFileSync(path.join(templateDir, 'www/gitignore')); |
| fs.ensureDirSync(path.join(templateDir, 'foo/gitignore')); |
| |
| opts.template = templatePkg; |
| return create(project, opts).then(() => { |
| // Renames gitignore at template root |
| expect(path.join(project, 'gitignore')).not.toExist(); |
| expect(path.join(project, '.gitignore')).toExist(); |
| |
| // Renames gitignores in sub-directories |
| expect(path.join(project, 'www/gitignore')).not.toExist(); |
| expect(path.join(project, 'www/.gitignore')).toExist(); |
| |
| // Does not rename directories with name gitignore |
| expect(path.join(project, 'foo/gitignore')).toExist(); |
| expect(path.join(project, 'foo/.gitignore')).not.toExist(); |
| }); |
| }); |
| }); |
| |
| describe('when shit happens', () => { |
| it('should fail when dir is missing', () => { |
| return expectRejection( |
| create(null, opts), |
| new CordovaError('Directory not specified') |
| ); |
| }); |
| |
| it('should fail when dir already exists', () => { |
| return expectRejection( |
| create(__dirname, opts), |
| new CordovaError('Path already exists and is not empty') |
| ); |
| }); |
| |
| it('should fail when destination is inside template', () => { |
| opts.template = path.join(tmpDir, 'template'); |
| return expectRejection( |
| create(path.join(opts.template, 'destination'), opts), |
| new CordovaError('inside the template') |
| ); |
| }); |
| |
| it('should fail when fetch fails', () => { |
| const fetchError = new Error('Fetch fail'); |
| const failingFetch = jasmine.createSpy('failingFetch') |
| .and.callFake(() => Promise.reject(fetchError)); |
| |
| opts.template = 'http://localhost:123456789/cordova-create'; |
| return expectRejection( |
| createWith({ fetch: failingFetch })(project, opts), |
| fetchError |
| ); |
| }); |
| |
| // FIXME: we need to improve isRemote to make this different from the test above |
| xit('should fail when template does not exist', () => { |
| opts.template = path.join(__dirname, 'doesnotexist'); |
| return expectRejection( |
| create(project, opts), |
| new CordovaError('not a valid template') |
| ); |
| }); |
| }); |
| |
| describe('cordova create needsToBeFetched', () => { |
| let needsToBeFetched; |
| |
| beforeEach(() => { |
| needsToBeFetched = rewire('..').__get__('needsToBeFetched'); |
| }); |
| |
| it('should recognize URLs as remote', () => { |
| expect(needsToBeFetched('https://example.com/pkg/foo')).toBe(true); |
| }); |
| |
| it('should recognize package@version as remote', () => { |
| expect(needsToBeFetched('foo@1')).toBe(true); |
| }); |
| |
| it('should not detect paths as remote only because they include an @', () => { |
| expect(needsToBeFetched('../foo@1')).toBe(false); |
| }); |
| }); |