Merge pull request #40 from raphinesse/breaking-changes
Major usability & maintainability improvements
diff --git a/README.md b/README.md
index 6768763..160eceb 100644
--- a/README.md
+++ b/README.md
@@ -29,27 +29,31 @@
## Usage:
```
-var create = require('cordova-create');
+const create = require('cordova-create');
-create(dir, id, name, cfg, extEvents);
+await create(dest, opts);
```
-- `dir` - directory where the project will be created. Required.
-- `id` - app id. Required (but can be "undefined").
-- `name` - app name. Required (but can be "undefined").
-- `cfg` - extra config to be saved in .cordova/config.json Required (but can be "{}").
-- `extEvents` - An EventEmitter instance that will be used for logging purposes. Required (but can be "undefined").
+### Parameters
-An example of `cfg` which would fetch a cordova template from npm (or git):
+#### `dest`
+_Required_. Path to the destination where the project will be created. Must be an empty dir if it exists.
+
+#### `opts`
+_Optional_. Supports following properties.
```
-var cfg = {
- lib: {
- www: {
- template: true,
- url: 'cordova-app-hello-world',
- version: ''
- }
- }
-};
+{
+ // Attributes to be set in package.json & config.xml
+ id: String,
+ name: String,
+ version: String,
+
+ // The path/url/package-name to the template that should be used
+ template: String,
+
+ // An EventEmitter instance that will be used for logging purposes
+ // (actually it only needs to implement a compatible `emit` method)
+ events: EventEmitter
+}
```
diff --git a/index.js b/index.js
index 2ddec33..0ce0636 100644
--- a/index.js
+++ b/index.js
@@ -29,47 +29,10 @@
var validateIdentifier = require('valid-identifier');
var fetch = require('cordova-fetch');
-var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser;
-var CordovaLogger = require('cordova-common').CordovaLogger.get();
-const DEFAULT_VERSION = '1.0.0';
-
-/**
- * Sets up to forward events to another instance, or log console.
- * This will make the create internal events visible outside
- * @param {EventEmitter} externalEventEmitter An EventEmitter instance that will be used for
- * logging purposes. If no EventEmitter provided, all events will be logged to console
- * @return {EventEmitter}
- */
-function setupEvents (externalEventEmitter) {
- if (externalEventEmitter) {
- // This will make the platform internal events visible outside
- events.forwardEventsTo(externalEventEmitter);
- // There is no logger if external emitter is not present,
- // so attach a console logger
- } else {
- CordovaLogger.subscribe(events);
- }
- return events;
-}
-
-module.exports = cordovaCreateLegacyAdapter;
-
-/**
-* Legacy interface. See README for documentation
-*/
-function cordovaCreateLegacyAdapter (dir, id, name, cfg, extEvents) {
- // Unwrap and shallow-clone that nasty nested config object
- const opts = Object.assign({}, ((cfg || {}).lib || {}).www);
-
- if (id) opts.id = id;
- if (name) opts.name = name;
- if (extEvents) opts.extEvents = extEvents;
-
- return cordovaCreate(dir, opts);
-}
+module.exports = cordovaCreate;
/**
* Creates a new cordova project in the given directory.
@@ -79,6 +42,7 @@
* @returns {Promise} Resolves when project creation has finished.
*/
function cordovaCreate (dest, opts = {}) {
+ let emit;
// TODO this is to avoid having a huge diff. Remove later.
let dir = dest;
@@ -94,8 +58,8 @@
// Shallow copy opts
opts = Object.assign({}, opts);
- events = setupEvents(opts.extEvents);
- events.emit('verbose', 'Using detached cordova-create');
+ emit = getEventEmitter(opts);
+ emit('verbose', 'Using detached cordova-create');
// Make absolute.
dir = path.resolve(dir);
@@ -108,54 +72,38 @@
throw new CordovaError('App id contains a reserved word, or is not a valid identifier.');
}
- // This was changed from "uri" to "url", but checking uri for backwards compatibility.
- opts.url = opts.url || opts.uri;
-
- if (!opts.url) {
- opts.url = require.resolve('cordova-app-hello-world');
- opts.template = true;
+ if (!opts.template) {
+ opts.template = require.resolve('cordova-app-hello-world');
}
// Ensure that the destination is outside the template location
- if (pathIsInside(dir, opts.url)) {
+ if (pathIsInside(dir, opts.template)) {
throw new CordovaError(
- `Cannot create project "${dir}" inside the template used to create it "${opts.url}".`
+ `Cannot create project "${dir}" inside the template used to create it "${opts.template}".`
);
}
})
.then(function () {
// Finally, Ready to start!
- events.emit('log', 'Creating a new cordova project.');
-
- // If symlink, don't fetch
- if (opts.link) {
- return opts.url;
- }
+ emit('log', 'Creating a new cordova project.');
// Use cordova-fetch to obtain npm or git templates
- if (opts.template && isRemoteUri(opts.url)) {
- var target = opts.url;
- events.emit('verbose', 'Using cordova-fetch for ' + target);
+ if (isRemoteUri(opts.template)) {
+ var target = opts.template;
+ emit('verbose', 'Using cordova-fetch for ' + target);
return fetch(target, getSelfDestructingTempDir(), {});
} else {
// If assets are not online, resolve as a relative path on local computer
- return path.resolve(opts.url);
+ return path.resolve(opts.template);
}
})
- .then(function (input_directory) {
- var import_from_path = input_directory;
+ .then(function (templatePath) {
+ var import_from_path;
- // handle when input wants to specify sub-directory (specified in index.js as "dirname" export);
- var isSubDir = false;
try {
- var templatePkg = requireFresh(input_directory);
- if (templatePkg && templatePkg.dirname) {
- import_from_path = templatePkg.dirname;
- isSubDir = true;
- }
+ import_from_path = requireFresh(templatePath).dirname;
} catch (e) {
- events.emit('verbose', 'index.js does not specify valid sub-directory: ' + input_directory);
- isSubDir = false;
+ throw new CordovaError(templatePath + ' is not a valid template');
}
if (!fs.existsSync(import_from_path)) {
@@ -170,180 +118,45 @@
try {
// Copy files from template to project
- if (opts.template) {
- events.emit('verbose', 'Copying assets.');
- copyTemplateFiles(import_from_path, dir, isSubDir);
- }
-
- // If --link, link merges, hooks, www, and config.xml (and/or copy to root)
- if (opts.link) {
- events.emit('verbose', 'Symlinking assets.');
- linkFromTemplate(import_from_path, dir);
- }
-
- // If following were not copied/linked from template, copy from stock app hello world
- // TODO: get stock package.json if template does not contain package.json;
- copyIfNotExists(stockAssetPath('www'), path.join(dir, 'www'));
- copyIfNotExists(stockAssetPath('hooks'), path.join(dir, 'hooks'));
- var configXmlExists = projectConfig(dir); // moves config to root if in www
- if (!configXmlExists) {
- fs.copySync(stockAssetPath('config.xml'), path.join(dir, 'config.xml'));
- }
+ emit('verbose', 'Copying assets.');
+ fs.copySync(import_from_path, dir);
} catch (e) {
if (!dirAlreadyExisted) {
fs.removeSync(dir);
}
- if (process.platform.slice(0, 3) === 'win' && e.code === 'EPERM') {
- throw new CordovaError('Symlinks on Windows require Administrator privileges');
- }
throw e;
}
- var pkgjsonPath = path.join(dir, 'package.json');
- // Update package.json name and version fields
- if (fs.existsSync(pkgjsonPath)) {
- var pkgjson = requireFresh(pkgjsonPath);
+ // Write out id, name and version to config.xml
+ const configPath = path.join(dir, 'config.xml');
+ const conf = new ConfigParser(configPath);
- // Pkjson.displayName should equal config's name.
- if (opts.name) {
- pkgjson.displayName = opts.name;
- }
- // Pkjson.name should equal config's id.
- if (opts.id) {
- pkgjson.name = opts.id.toLowerCase();
- } else {
- // Use default name.
- pkgjson.name = 'helloworld';
- }
+ conf.setPackageName(opts.id || conf.packageName() || 'com.example.cordova.app');
+ conf.setName(opts.name || conf.name() || 'Cordova Example App');
+ conf.setVersion(opts.version || conf.version() || '1.0.0');
- pkgjson.version = DEFAULT_VERSION;
- fs.writeFileSync(pkgjsonPath, JSON.stringify(pkgjson, null, 4), 'utf8');
- }
+ conf.write();
- // Create basic project structure.
- fs.ensureDirSync(path.join(dir, 'platforms'));
- fs.ensureDirSync(path.join(dir, 'plugins'));
+ // Copy values from config.xml to package.json
+ const pkgJsonPath = path.join(dir, 'package.json');
+ if (fs.existsSync(pkgJsonPath)) {
+ const pkgJson = requireFresh(pkgJsonPath);
- var configPath = path.join(dir, 'config.xml');
- // only update config.xml if not a symlink
- if (!fs.lstatSync(configPath).isSymbolicLink()) {
- // Write out id, name and default version to config.xml
- var conf = new ConfigParser(configPath);
- if (opts.id) conf.setPackageName(opts.id);
- if (opts.name) conf.setName(opts.name);
- conf.setVersion(DEFAULT_VERSION);
- conf.write();
+ Object.assign(pkgJson, {
+ name: conf.packageName().toLowerCase(),
+ displayName: conf.name(),
+ version: conf.version()
+ });
+
+ fs.writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2 });
}
});
}
-/**
- * Recursively copies folder to destination if folder is not found in destination (including symlinks).
- * @param {string} src for copying
- * @param {string} dst for copying
- * @return No return value
- */
-function copyIfNotExists (src, dst) {
- if (!fs.existsSync(dst) && src) {
- fs.copySync(src, dst);
- }
-}
-
-/**
- * Copies template files, and directories into a Cordova project directory.
- * If the template is a www folder, the www folder is simply copied
- * Otherwise if the template exists in a subdirectory everything is copied
- * Otherwise package.json, RELEASENOTES.md, .git, NOTICE, LICENSE, COPYRIGHT, and .npmignore are not copied over.
- * A template directory, and project directory must be passed.
- * templateDir - Template directory
- * projectDir - Project directory
- * isSubDir - boolean is true if template has subdirectory structure (see code around line 229)
- */
-function copyTemplateFiles (templateDir, projectDir, isSubDir) {
- var copyPath;
- // if template is a www dir
- if (path.basename(templateDir) === 'www') {
- copyPath = path.resolve(templateDir);
- fs.copySync(copyPath, path.resolve(projectDir, 'www'));
- } else {
- var templateFiles = fs.readdirSync(templateDir);
- // Remove directories, and files that are unwanted
- if (!isSubDir) {
- var excludes = ['package.json', 'RELEASENOTES.md', '.git', 'NOTICE', 'LICENSE', 'COPYRIGHT', '.npmignore'];
- templateFiles = templateFiles.filter(function (value) {
- return excludes.indexOf(value) < 0;
- });
- }
- // Copy each template file after filter
- templateFiles.forEach(f => {
- copyPath = path.resolve(templateDir, f);
- fs.copySync(copyPath, path.resolve(projectDir, f));
- });
- }
-}
-
-/**
- * Find config file in project directory or www directory
- * If file is in www directory, move it outside
- * @param {String} project directory to be searched
- * @return {String or False} location of config file; if none exists, returns false
- */
-function projectConfig (projectDir) {
- var rootPath = path.join(projectDir, 'config.xml');
- var wwwPath = path.join(projectDir, 'www', 'config.xml');
- if (fs.existsSync(rootPath)) {
- return rootPath;
- } else if (fs.existsSync(wwwPath)) {
- fs.renameSync(wwwPath, rootPath);
- return wwwPath;
- }
- return false;
-}
-
-/**
- * Removes existing files and symlinks them if they exist.
- * Symlinks folders: www, merges, hooks
- * Symlinks file: config.xml (but only if it exists outside of the www folder)
- * If config.xml exists inside of template/www, COPY (not link) it to project/
- * */
-function linkFromTemplate (templateDir, projectDir) {
- var linkSrc, linkDst, linkFolders, copySrc, copyDst;
- function rmlinkSync (src, dst, type) {
- if (src && dst) {
- fs.removeSync(dst);
- if (fs.existsSync(src)) {
- fs.symlinkSync(src, dst, type);
- }
- }
- }
- // if template is a www dir
- if (path.basename(templateDir) === 'www') {
- linkSrc = path.resolve(templateDir);
- linkDst = path.join(projectDir, 'www');
- rmlinkSync(linkSrc, linkDst, 'dir');
- copySrc = path.join(templateDir, 'config.xml');
- } else {
- linkFolders = ['www', 'merges', 'hooks'];
- // Link each folder
- for (var i = 0; i < linkFolders.length; i++) {
- linkSrc = path.join(templateDir, linkFolders[i]);
- linkDst = path.join(projectDir, linkFolders[i]);
- rmlinkSync(linkSrc, linkDst, 'dir');
- }
- linkSrc = path.join(templateDir, 'config.xml');
- linkDst = path.join(projectDir, 'config.xml');
- rmlinkSync(linkSrc, linkDst, 'file');
- copySrc = path.join(templateDir, 'www', 'config.xml');
- }
- // if template/www/config.xml then copy to project/config.xml
- copyDst = path.join(projectDir, 'config.xml');
- if (!fs.existsSync(copyDst) && fs.existsSync(copySrc)) {
- fs.copySync(copySrc, copyDst);
- }
-}
-
-function stockAssetPath (p) {
- return path.join(require('cordova-app-hello-world').dirname, p);
+function getEventEmitter ({ events }) {
+ return events
+ ? (...args) => events.emit(...args)
+ : () => {};
}
// Creates temp dir that is deleted on process exit
diff --git a/spec/create.spec.js b/spec/create.spec.js
index ef84270..821ed37 100644
--- a/spec/create.spec.js
+++ b/spec/create.spec.js
@@ -24,7 +24,6 @@
var requireFresh = require('import-fresh');
var create = require('..');
-var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser;
const { tmpDir, createWith, createWithMockFetch, expectRejection } = require('./helpers');
@@ -34,9 +33,11 @@
const appVersion = '1.0.0';
const project = path.join(tmpDir, appName);
-// Setup and teardown test dirs
+let opts;
+
beforeEach(function () {
fs.emptyDirSync(tmpDir);
+ opts = { name: appName, id: appId };
});
afterAll(function () {
process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows.
@@ -47,25 +48,21 @@
const error = new CordovaError('is not a valid identifier');
it('should reject reserved words from start of id', function () {
- return expectRejection(create(project, 'int.bob', appName, {}, events), error);
+ opts.id = 'int.bob';
+ return expectRejection(create(project, opts), error);
});
it('should reject reserved words from end of id', function () {
- return expectRejection(create(project, 'bob.class', appName, {}, events), error);
+ opts.id = 'bob.class';
+ return expectRejection(create(project, opts), error);
});
});
describe('create end-to-end', function () {
function checkCommonArtifacts () {
- // Check that top level dirs exist
- var dirs = ['hooks', 'platforms', 'plugins', 'www'];
- dirs.forEach(function (d) {
- expect(path.join(project, d)).toExist();
- });
-
- // Check that README.md exists inside of hooks
- expect(path.join(project, 'hooks', 'README.md')).toExist();
+ // 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();
@@ -85,7 +82,6 @@
var configXml = new ConfigParser(path.join(project, 'config.xml'));
expect(configXml.packageName()).toEqual(appId);
expect(configXml.name()).toEqual(appName);
- expect(configXml.version()).toEqual(appVersion);
}
// Check that we got package.json and it was updated correctly
@@ -93,7 +89,6 @@
const pkg = requireFresh(path.join(project, 'package.json'));
expect(pkg.name).toEqual(appId);
expect(pkg.displayName).toEqual(appName);
- expect(pkg.version).toEqual(appVersion);
}
// Check that we got no package.json
@@ -105,21 +100,18 @@
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');
- }
-
- function checkProjectCreatedWithFixtureTemplate () {
- checkCommonArtifacts();
- checkNoPackageJson();
- checkNotDefaultTemplate();
+ expect(configXml.version()).toEqual('0.0.1');
}
function checkProjectCreatedWithDefaultTemplate () {
@@ -131,361 +123,107 @@
it('should successfully run without template and use default hello-world app', function () {
// Create a real project with no template
// use default cordova-app-hello-world app
- return create(project, appId, appName, {}, events)
+ return create(project, opts)
.then(checkProjectCreatedWithDefaultTemplate);
});
it('should successfully run with Git URL', function () {
// Create a real project with git URL as template
- var config = {
- lib: {
- www: {
- url: 'https://github.com/apache/cordova-app-hello-world',
- template: true
- }
- }
- };
- return createWithMockFetch(project, appId, appName, config, events)
+ 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(config.lib.www.url);
+ expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template);
})
.then(checkProjectCreatedWithDefaultTemplate);
});
it('should successfully run with NPM package (specific version)', function () {
// Create a real project with npm module as template
- var config = {
- lib: {
- www: {
- template: true,
- url: 'phonegap-template-vue-f7-tabs@1'
- }
- }
- };
- return createWithMockFetch(project, appId, appName, config, events)
+ 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(config.lib.www.url);
+ expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template);
})
.then(checkProjectCreatedWithDefaultTemplate);
});
it('should successfully run with NPM package (no specific version)', function () {
// Create a real project with npm module as template
- var config = {
- lib: {
- www: {
- template: true,
- url: 'phonegap-template-vue-f7-tabs'
- }
- }
- };
- return createWithMockFetch(project, appId, appName, config, events)
+ opts.template = 'phonegap-template-vue-f7-tabs';
+ return createWithMockFetch(project, opts)
.then(fetchSpy => {
expect(fetchSpy).toHaveBeenCalledTimes(1);
- expect(fetchSpy.calls.argsFor(0)[0]).toBe(config.lib.www.url);
+ expect(fetchSpy.calls.argsFor(0)[0]).toBe(opts.template);
})
.then(checkProjectCreatedWithDefaultTemplate);
});
- it('should successfully run with template not having a package.json at toplevel', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'nopackage_json')
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkProjectCreatedWithFixtureTemplate);
+ it('should successfully run with local template having no package.json in template dir', function () {
+ opts.template = path.join(__dirname, 'templates/withsubdirectory');
+ return create(project, opts)
+ .then(checkCommonArtifacts)
+ .then(checkNoPackageJson)
+ .then(checkNotDefaultTemplate);
});
- it('should successfully run with template having package.json and no sub directory', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'withpackage_json')
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkProjectCreatedWithFixtureTemplate);
- });
-
- it('should successfully run with template having package.json, and subdirectory, and no package.json in subdirectory', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'withsubdirectory')
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkProjectCreatedWithFixtureTemplate);
- });
-
- it('should successfully run with template having package.json, and subdirectory, and package.json in subdirectory', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'withsubdirectory_package_json')
- }
- }
- };
- return create(project, appId, appName, config, events)
+ it('should successfully run with local template having package.json in template dir', function () {
+ opts.template = path.join(__dirname, 'templates/withsubdirectory_package_json');
+ return create(project, opts)
.then(checkCommonArtifacts)
.then(checkPackageJson)
.then(checkNotDefaultTemplate);
});
- it('should successfully run config.xml in the www folder and move it outside', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'config_in_www')
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkProjectCreatedWithFixtureTemplate);
- });
-
- it('should successfully run with www folder as the template', function () {
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'config_in_www', 'www')
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkProjectCreatedWithFixtureTemplate);
- });
-
it('should successfully run with existing, empty destination', function () {
fs.ensureDirSync(project);
- return create(project, appId, appName, {}, events)
+ return create(project, opts)
.then(checkProjectCreatedWithDefaultTemplate);
});
-
- describe('when --link-to is provided', function () {
- function allowSymlinkErrorOnWindows (err) {
- const onWindows = process.platform.slice(0, 3) === 'win';
- const isSymlinkError = err && String(err.message).startsWith('Symlinks on Windows');
- if (onWindows && isSymlinkError) {
- pending(err.message);
- } else {
- throw err;
- }
- }
-
- it('when passed www folder should not move www/config.xml, only copy and update', function () {
- function checkSymWWW () {
- // Check if top level dirs exist.
- var dirs = ['hooks', 'platforms', 'plugins', 'www'];
- dirs.forEach(function (d) {
- expect(path.join(project, d)).toExist();
- });
- expect(path.join(project, 'hooks', 'README.md')).toExist();
-
- // Check if www files exist.
- expect(path.join(project, 'www', 'index.html')).toExist();
-
- // Check www/config exists
- expect(path.join(project, 'www', 'config.xml')).toExist();
- // Check www/config.xml was not updated.
- var configXml = new ConfigParser(path.join(project, 'www', 'config.xml'));
- expect(configXml.packageName()).toEqual('io.cordova.hellocordova');
- expect(configXml.version()).toEqual('0.0.1');
- expect(configXml.description()).toEqual('this is the correct config.xml');
-
- // Check that config.xml was copied to project/config.xml
- expect(path.join(project, 'config.xml')).toExist();
- configXml = new ConfigParser(path.join(project, 'config.xml'));
- expect(configXml.description()).toEqual('this is the correct config.xml');
- // Check project/config.xml was updated.
- expect(configXml.packageName()).toEqual(appId);
- expect(configXml.version()).toEqual('1.0.0');
-
- // Check that we got no package.json
- expect(path.join(project, 'package.json')).not.toExist();
-
- // Check that www is really a symlink,
- // and project/config.xml , hooks and merges are not
- expect(fs.lstatSync(path.join(project, 'www')).isSymbolicLink()).toBe(true);
- expect(fs.lstatSync(path.join(project, 'hooks')).isSymbolicLink()).not.toBe(true);
- expect(fs.lstatSync(path.join(project, 'config.xml')).isSymbolicLink()).not.toBe(true);
- }
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'config_in_www', 'www'),
- link: true
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkSymWWW)
- .catch(allowSymlinkErrorOnWindows);
- });
-
- it('with subdirectory should not update symlinked project/config.xml', function () {
- function checkSymSubDir () {
- // Check if top level dirs exist.
- var dirs = ['hooks', 'platforms', 'plugins', 'www'];
- dirs.forEach(function (d) {
- expect(path.join(project, d)).toExist();
- });
- expect(path.join(project, 'hooks', 'README.md')).toExist();
-
- // index.js and template subdir folder should not exist (inner files should be copied to the project folder)
- expect(path.join(project, 'index.js')).not.toExist();
- expect(path.join(project, 'template')).not.toExist();
-
- // Check if www files exist.
- expect(path.join(project, 'www', 'index.html')).toExist();
-
- // Check that www, and config.xml is really a symlink
- expect(fs.lstatSync(path.join(project, 'www')).isSymbolicLink()).toBe(true);
- expect(fs.lstatSync(path.join(project, 'config.xml')).isSymbolicLink()).toBe(true);
-
- // Check that config.xml was not updated. (symlinked config does not get updated!)
- var configXml = new ConfigParser(path.join(project, 'config.xml'));
- expect(configXml.packageName()).toEqual('io.cordova.hellocordova');
- expect(configXml.version()).toEqual('0.0.1');
-
- // Check that we got the right config.xml
- expect(configXml.description()).toEqual('this is the correct config.xml');
-
- // Check that we got package.json (the correct one) and it was changed
- var pkjson = requireFresh(path.join(project, 'package.json'));
- // Pkjson.name should equal config's id.
- expect(pkjson.name).toEqual(appId.toLowerCase());
- expect(pkjson.valid).toEqual('true');
- }
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'withsubdirectory_package_json'),
- link: true
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkSymSubDir)
- .catch(allowSymlinkErrorOnWindows);
- });
-
- it('with no config should create one and update it', function () {
- function checkSymNoConfig () {
- // Check if top level dirs exist.
- var dirs = ['hooks', 'platforms', 'plugins', 'www'];
- dirs.forEach(function (d) {
- expect(path.join(project, d)).toExist();
- });
- expect(path.join(project, 'hooks', 'hooks.file')).toExist();
- expect(path.join(project, 'merges', 'merges.file')).toExist();
-
- // Check if www files exist.
- expect(path.join(project, 'www', 'index.html')).toExist();
-
- // Check that config.xml was updated.
- var configXml = new ConfigParser(path.join(project, 'config.xml'));
- expect(configXml.packageName()).toEqual(appId);
-
- // Check that www, hooks, merges are really a symlink; config is not
- expect(fs.lstatSync(path.join(project, 'www')).isSymbolicLink()).toBe(true);
- expect(fs.lstatSync(path.join(project, 'hooks')).isSymbolicLink()).toBe(true);
- expect(fs.lstatSync(path.join(project, 'merges')).isSymbolicLink()).toBe(true);
- expect(fs.lstatSync(path.join(project, 'config.xml')).isSymbolicLink()).not.toBe(true);
- }
-
- var config = {
- lib: {
- www: {
- template: true,
- url: path.join(__dirname, 'templates', 'noconfig'),
- link: true
- }
- }
- };
- return create(project, appId, appName, config, events)
- .then(checkSymNoConfig)
- .catch(allowSymlinkErrorOnWindows);
- });
-
- });
});
describe('when shit happens', function () {
it('should fail when dir is missing', function () {
return expectRejection(
- create(null, appId, appName, {}, events),
+ create(null, opts),
new CordovaError('Directory not specified')
);
});
it('should fail when dir already exists', function () {
return expectRejection(
- create(__dirname, appId, appName, {}, events),
+ create(__dirname, opts),
new CordovaError('Path already exists and is not empty')
);
});
it('should fail when destination is inside template', function () {
- const config = {
- lib: {
- www: {
- url: path.join(tmpDir, 'template')
- }
- }
- };
- const destination = path.join(config.lib.www.url, 'destination');
+ opts.template = path.join(tmpDir, 'template');
return expectRejection(
- create(destination, appId, appName, config, events),
+ create(path.join(opts.template, 'destination'), opts),
new CordovaError('inside the template')
);
});
it('should fail when fetch fails', function () {
- const config = {
- lib: {
- www: {
- template: true,
- url: 'http://localhost:123456789/cordova-create'
- }
- }
- };
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, appId, appName, config),
+ createWith({ fetch: failingFetch })(project, opts),
fetchError
);
-
});
- it('should fail when template does not exist', function () {
- const config = {
- lib: {
- www: {
- url: path.join(__dirname, 'doesnotexist')
- }
- }
- };
+ // FIXME: we need to improve isRemote to make this different from the test above
+ xit('should fail when template does not exist', function () {
+ opts.template = path.join(__dirname, 'doesnotexist');
return expectRejection(
- create(project, appId, appName, config, events),
- new CordovaError('Could not find directory')
+ create(project, opts),
+ new CordovaError('not a valid template')
);
});
});
diff --git a/spec/helpers.js b/spec/helpers.js
index bc48248..399f292 100644
--- a/spec/helpers.js
+++ b/spec/helpers.js
@@ -23,10 +23,6 @@
const rewire = require('rewire');
-// Disable regular console output during tests
-const CordovaLogger = require('cordova-common').CordovaLogger;
-CordovaLogger.get().setLevel(CordovaLogger.ERROR);
-
// Temporary directory to use for all tests
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cordova-create-tests-'));
diff --git a/spec/templates/config_in_www/www/config.xml b/spec/templates/config_in_www/www/config.xml
deleted file mode 100644
index ccf0e68..0000000
--- a/spec/templates/config_in_www/www/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<!--
- 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.
--->
-<widget id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
- <name>HelloCordova</name>
- <description>this is the correct config.xml</description>
- <author email="dev@cordova.apache.org" href="http://cordova.io">
- Apache Cordova Test Team
- </author>
- <content src="index.html" />
- <!-- Whitelist configuration. Refer to https://cordova.apache.org/docs/en/edge/guide_appdev_whitelist_index.md.html -->
- <plugin name="cordova-plugin-whitelist" spec="1" />
- <access origin="*" />
- <allow-intent href="http://*/*" />
- <allow-intent href="https://*/*" />
- <allow-intent href="tel:*" />
- <allow-intent href="sms:*" />
- <allow-intent href="mailto:*" />
- <allow-intent href="geo:*" />
- <platform name="android">
- <allow-intent href="market:*" />
- </platform>
- <platform name="ios">
- <allow-intent href="itms:*" />
- <allow-intent href="itms-apps:*" />
- </platform>
-</widget>
diff --git a/spec/templates/config_in_www/www/index.html b/spec/templates/config_in_www/www/index.html
deleted file mode 100644
index 646f9cb..0000000
--- a/spec/templates/config_in_www/www/index.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
--->
-<html>
- <head>
- <!--
- Customize this policy to fit your own app's needs. For more guidance, see:
- https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
- Some notes:
- * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
- * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
- * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
- * Enable inline JS: add 'unsafe-inline' to default-src
- -->
- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
- <meta name="format-detection" content="telephone=no">
- <meta name="msapplication-tap-highlight" content="no">
- <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
- <link rel="stylesheet" type="text/css" href="css/index.css">
- <title>Hello World</title>
- </head>
- <body>
- <div class="app">
- <h1>Apache Cordova</h1>
- <div id="deviceready" class="blink">
- <p class="event listening">Connecting to Device</p>
- <p class="event received">Device is Ready</p>
- </div>
- </div>
- <script type="text/javascript" src="cordova.js"></script>
- <script type="text/javascript" src="js/index.js"></script>
- </body>
-</html>
diff --git a/spec/templates/noconfig/hooks/hooks.file b/spec/templates/noconfig/hooks/hooks.file
deleted file mode 100644
index e69de29..0000000
--- a/spec/templates/noconfig/hooks/hooks.file
+++ /dev/null
diff --git a/spec/templates/noconfig/merges/merges.file b/spec/templates/noconfig/merges/merges.file
deleted file mode 100644
index e69de29..0000000
--- a/spec/templates/noconfig/merges/merges.file
+++ /dev/null
diff --git a/spec/templates/noconfig/www/index.html b/spec/templates/noconfig/www/index.html
deleted file mode 100644
index 646f9cb..0000000
--- a/spec/templates/noconfig/www/index.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
--->
-<html>
- <head>
- <!--
- Customize this policy to fit your own app's needs. For more guidance, see:
- https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
- Some notes:
- * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
- * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
- * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
- * Enable inline JS: add 'unsafe-inline' to default-src
- -->
- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
- <meta name="format-detection" content="telephone=no">
- <meta name="msapplication-tap-highlight" content="no">
- <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
- <link rel="stylesheet" type="text/css" href="css/index.css">
- <title>Hello World</title>
- </head>
- <body>
- <div class="app">
- <h1>Apache Cordova</h1>
- <div id="deviceready" class="blink">
- <p class="event listening">Connecting to Device</p>
- <p class="event received">Device is Ready</p>
- </div>
- </div>
- <script type="text/javascript" src="cordova.js"></script>
- <script type="text/javascript" src="js/index.js"></script>
- </body>
-</html>
diff --git a/spec/templates/nopackage_json/config.xml b/spec/templates/nopackage_json/config.xml
deleted file mode 100644
index 1e821e0..0000000
--- a/spec/templates/nopackage_json/config.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<!--
- 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.
--->
-<widget id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
- <name>HelloCordova</name>
- <description>this is the very correct config.xml</description>
- <author email="dev@cordova.apache.org" href="http://cordova.io">
- Apache Cordova Test Team
- </author>
- <content src="index.html" />
- <!-- Whitelist configuration. Refer to https://cordova.apache.org/docs/en/edge/guide_appdev_whitelist_index.md.html -->
- <plugin name="cordova-plugin-whitelist" spec="1" />
- <access origin="*" />
- <allow-intent href="http://*/*" />
- <allow-intent href="https://*/*" />
- <allow-intent href="tel:*" />
- <allow-intent href="sms:*" />
- <allow-intent href="mailto:*" />
- <allow-intent href="geo:*" />
- <platform name="android">
- <allow-intent href="market:*" />
- </platform>
- <platform name="ios">
- <allow-intent href="itms:*" />
- <allow-intent href="itms-apps:*" />
- </platform>
-</widget>
diff --git a/spec/templates/nopackage_json/www/index.html b/spec/templates/nopackage_json/www/index.html
deleted file mode 100644
index 646f9cb..0000000
--- a/spec/templates/nopackage_json/www/index.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
--->
-<html>
- <head>
- <!--
- Customize this policy to fit your own app's needs. For more guidance, see:
- https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
- Some notes:
- * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
- * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
- * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
- * Enable inline JS: add 'unsafe-inline' to default-src
- -->
- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
- <meta name="format-detection" content="telephone=no">
- <meta name="msapplication-tap-highlight" content="no">
- <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
- <link rel="stylesheet" type="text/css" href="css/index.css">
- <title>Hello World</title>
- </head>
- <body>
- <div class="app">
- <h1>Apache Cordova</h1>
- <div id="deviceready" class="blink">
- <p class="event listening">Connecting to Device</p>
- <p class="event received">Device is Ready</p>
- </div>
- </div>
- <script type="text/javascript" src="cordova.js"></script>
- <script type="text/javascript" src="js/index.js"></script>
- </body>
-</html>
diff --git a/spec/templates/withpackage_json/config.xml b/spec/templates/withpackage_json/config.xml
deleted file mode 100644
index 4b7e53e..0000000
--- a/spec/templates/withpackage_json/config.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<!--
- 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.
--->
-<widget id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
- <name>HelloCordova</name>
- <description>
- A sample Apache Cordova application that responds to the deviceready event.
- </description>
- <author email="dev@cordova.apache.org" href="http://cordova.io">
- Apache Cordova Test Team
- </author>
- <content src="index.html" />
- <!-- Whitelist configuration. Refer to https://cordova.apache.org/docs/en/edge/guide_appdev_whitelist_index.md.html -->
- <plugin name="cordova-plugin-whitelist" spec="1" />
- <access origin="*" />
- <allow-intent href="http://*/*" />
- <allow-intent href="https://*/*" />
- <allow-intent href="tel:*" />
- <allow-intent href="sms:*" />
- <allow-intent href="mailto:*" />
- <allow-intent href="geo:*" />
- <platform name="android">
- <allow-intent href="market:*" />
- </platform>
- <platform name="ios">
- <allow-intent href="itms:*" />
- <allow-intent href="itms-apps:*" />
- </platform>
-</widget>
diff --git a/spec/templates/withpackage_json/pacakge.json b/spec/templates/withpackage_json/pacakge.json
deleted file mode 100644
index ef6f89e..0000000
--- a/spec/templates/withpackage_json/pacakge.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "template",
- "version": "1.0.0",
- "description": "Apache Cordova Application",
- "repository": {
- "type": "git",
- "url": "https://github.com/apache/cordova-app-hello-world.git"
- },
- "license": "Apache-2.0",
- "scripts": {
- "preinstall": "echo \"Yay npm preinstall script\" ",
- "postinstall": "echo \"Yay npm postinstall script\" "
- }
-}
\ No newline at end of file
diff --git a/spec/templates/withpackage_json/www/index.html b/spec/templates/withpackage_json/www/index.html
deleted file mode 100644
index 646f9cb..0000000
--- a/spec/templates/withpackage_json/www/index.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
--->
-<html>
- <head>
- <!--
- Customize this policy to fit your own app's needs. For more guidance, see:
- https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
- Some notes:
- * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
- * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
- * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
- * Enable inline JS: add 'unsafe-inline' to default-src
- -->
- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
- <meta name="format-detection" content="telephone=no">
- <meta name="msapplication-tap-highlight" content="no">
- <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
- <link rel="stylesheet" type="text/css" href="css/index.css">
- <title>Hello World</title>
- </head>
- <body>
- <div class="app">
- <h1>Apache Cordova</h1>
- <div id="deviceready" class="blink">
- <p class="event listening">Connecting to Device</p>
- <p class="event received">Device is Ready</p>
- </div>
- </div>
- <script type="text/javascript" src="cordova.js"></script>
- <script type="text/javascript" src="js/index.js"></script>
- </body>
-</html>