23e4138c07
SauceLabs is struggling to keep connecting and disconnecting for each of the modules unit test runs. This commit puts most of the module tests into IIFEs so that they can be run in one go. * ngMock is still tested separately since unlike the other tests it doesn't want to have a pre-built version of ngMock available. * ngAnimate is still tested separately because it does some funny things with globals that were difficult to isolate in the main modules test run.
319 lines
11 KiB
JavaScript
319 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
var fs = require('fs');
|
|
var shell = require('shelljs');
|
|
var grunt = require('grunt');
|
|
var spawn = require('npm-run').spawn;
|
|
|
|
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
|
|
|
|
|
module.exports = {
|
|
|
|
startKarma: function(config, singleRun, done) {
|
|
var browsers = grunt.option('browsers');
|
|
var reporters = grunt.option('reporters');
|
|
var noColor = grunt.option('no-colors');
|
|
var port = grunt.option('port');
|
|
var p = spawn('karma', ['start', config,
|
|
singleRun ? '--single-run=true' : '',
|
|
reporters ? '--reporters=' + reporters : '',
|
|
browsers ? '--browsers=' + browsers : '',
|
|
noColor ? '--no-colors' : '',
|
|
port ? '--port=' + port : ''
|
|
]);
|
|
p.stdout.pipe(process.stdout);
|
|
p.stderr.pipe(process.stderr);
|
|
p.on('exit', function(code) {
|
|
if (code !== 0) grunt.fail.warn('Karma test(s) failed. Exit code: ' + code);
|
|
done();
|
|
});
|
|
},
|
|
|
|
|
|
updateWebdriver: function(done) {
|
|
if (process.env.TRAVIS) {
|
|
// Skip the webdriver-manager update on Travis, since the browsers will
|
|
// be provided remotely.
|
|
done();
|
|
return;
|
|
}
|
|
var p = spawn('webdriver-manager', ['update']);
|
|
p.stdout.pipe(process.stdout);
|
|
p.stderr.pipe(process.stderr);
|
|
p.on('exit', function(code) {
|
|
if (code !== 0) grunt.fail.warn('Webdriver failed to update');
|
|
done();
|
|
});
|
|
},
|
|
|
|
startProtractor: function(config, done) {
|
|
var sauceUser = grunt.option('sauceUser');
|
|
var sauceKey = grunt.option('sauceKey');
|
|
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
|
|
var sauceBuild = grunt.option('capabilities.build');
|
|
var browser = grunt.option('browser');
|
|
var specs = grunt.option('specs');
|
|
var args = [config];
|
|
if (sauceUser) args.push('--sauceUser=' + sauceUser);
|
|
if (sauceKey) args.push('--sauceKey=' + sauceKey);
|
|
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
|
|
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
|
|
if (specs) args.push('--specs=' + specs);
|
|
if (browser) {
|
|
args.push('--browser=' + browser);
|
|
}
|
|
|
|
|
|
var p = spawn('protractor', args);
|
|
p.stdout.pipe(process.stdout);
|
|
p.stderr.pipe(process.stderr);
|
|
p.on('exit', function(code) {
|
|
if (code !== 0) grunt.fail.warn('Protractor test(s) failed. Exit code: ' + code);
|
|
done();
|
|
});
|
|
},
|
|
|
|
|
|
wrap(src, name) {
|
|
return [`src/${name}.prefix`, ...src, `src/${name}.suffix`];
|
|
},
|
|
|
|
|
|
addStyle: function(src, styles, minify) {
|
|
styles = styles.reduce(processCSS.bind(this), {
|
|
js: [src],
|
|
css: []
|
|
});
|
|
return {
|
|
js: styles.js.join('\n'),
|
|
css: styles.css.join('\n')
|
|
};
|
|
|
|
function processCSS(state, file) {
|
|
var css = fs.readFileSync(file).toString(),
|
|
js;
|
|
state.css.push(css);
|
|
|
|
if (minify) {
|
|
css = css
|
|
.replace(/\r?\n/g, '')
|
|
.replace(/\/\*.*?\*\//g, '')
|
|
.replace(/:\s+/g, ':')
|
|
.replace(/\s*\{\s*/g, '{')
|
|
.replace(/\s*\}\s*/g, '}')
|
|
.replace(/\s*,\s*/g, ',')
|
|
.replace(/\s*;\s*/g, ';');
|
|
}
|
|
//escape for js
|
|
css = css
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/'/g, '\\\'')
|
|
.replace(/\r?\n/g, '\\n');
|
|
js = '!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(\'<style type="text/css">' + css + '</style>\');';
|
|
state.js.push(js);
|
|
|
|
return state;
|
|
}
|
|
},
|
|
|
|
|
|
process: function(src, NG_VERSION, strict) {
|
|
var processed = src
|
|
.replace(/(['"])NG_VERSION_FULL\1/g, NG_VERSION.full)
|
|
.replace(/(['"])NG_VERSION_MAJOR\1/, NG_VERSION.major)
|
|
.replace(/(['"])NG_VERSION_MINOR\1/, NG_VERSION.minor)
|
|
.replace(/(['"])NG_VERSION_DOT\1/, NG_VERSION.patch)
|
|
.replace(/(['"])NG_VERSION_CDN\1/, NG_VERSION.cdn)
|
|
.replace(/(['"])NG_VERSION_CODENAME\1/, NG_VERSION.codeName);
|
|
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
|
|
return processed;
|
|
},
|
|
|
|
|
|
build: function(config, fn) {
|
|
var files = grunt.file.expand(config.src);
|
|
// grunt.file.expand might reorder the list of files
|
|
// when it is expanding globs, so we use prefix and suffix
|
|
// fields to ensure that files are at the start of end of
|
|
// the list (primarily for wrapping in an IIFE).
|
|
if (config.prefix) {
|
|
files = grunt.file.expand(config.prefix).concat(files);
|
|
}
|
|
if (config.suffix) {
|
|
files = files.concat(grunt.file.expand(config.suffix));
|
|
}
|
|
var styles = config.styles;
|
|
var processedStyles;
|
|
//concat
|
|
var src = files.map(function(filepath) {
|
|
return grunt.file.read(filepath);
|
|
}).join(grunt.util.normalizelf('\n'));
|
|
//process
|
|
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
|
|
if (styles) {
|
|
processedStyles = this.addStyle(processed, styles.css, styles.minify);
|
|
processed = processedStyles.js;
|
|
if (config.styles.generateCspCssFile) {
|
|
grunt.file.write(removeSuffix(config.dest) + '-csp.css', CSP_CSS_HEADER + processedStyles.css);
|
|
}
|
|
}
|
|
//write
|
|
grunt.file.write(config.dest, processed);
|
|
grunt.log.ok('File ' + config.dest + ' created.');
|
|
fn();
|
|
|
|
function removeSuffix(fileName) {
|
|
return fileName.replace(/\.js$/, '');
|
|
}
|
|
},
|
|
|
|
|
|
singleStrict: function(src, insert) {
|
|
return src
|
|
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
|
|
.replace(/(\(function\([^)]*\)\s*\{)/, '$1\'use strict\';'); // add single strict mode flag
|
|
},
|
|
|
|
|
|
sourceMap: function(mapFile, fileContents) {
|
|
var sourceMapLine = '//# sourceMappingURL=' + mapFile + '\n';
|
|
return fileContents + sourceMapLine;
|
|
},
|
|
|
|
|
|
min: function(file, done) {
|
|
var classPathSep = (process.platform === 'win32') ? ';' : ':';
|
|
var minFile = file.replace(/\.js$/, '.min.js');
|
|
var mapFile = minFile + '.map';
|
|
var mapFileName = mapFile.match(/[^/]+$/)[0];
|
|
var errorFileName = file.replace(/\.js$/, '-errors.json');
|
|
var versionNumber = grunt.config('NG_VERSION').full;
|
|
var compilationLevel = (file === 'build/angular-message-format.js') ?
|
|
'ADVANCED_OPTIMIZATIONS' : 'SIMPLE_OPTIMIZATIONS';
|
|
shell.exec(
|
|
'java ' +
|
|
this.java32flags() + ' ' +
|
|
this.memoryRequirement() + ' ' +
|
|
'-cp vendor/closure-compiler/compiler.jar' + classPathSep +
|
|
'vendor/ng-closure-runner/ngcompiler.jar ' +
|
|
'org.angularjs.closurerunner.NgClosureRunner ' +
|
|
'--compilation_level ' + compilationLevel + ' ' +
|
|
'--language_in ECMASCRIPT5_STRICT ' +
|
|
'--minerr_pass ' +
|
|
'--minerr_errors ' + errorFileName + ' ' +
|
|
'--minerr_url http://errors.angularjs.org/' + versionNumber + '/ ' +
|
|
'--source_map_format=V3 ' +
|
|
'--create_source_map ' + mapFile + ' ' +
|
|
'--js ' + file + ' ' +
|
|
'--js_output_file ' + minFile,
|
|
function(code) {
|
|
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
|
|
|
|
// closure creates the source map relative to build/ folder, we need to strip those references
|
|
grunt.file.write(mapFile, grunt.file.read(mapFile).replace('"file":"build/', '"file":"').
|
|
replace('"sources":["build/','"sources":["'));
|
|
|
|
// move add use strict into the closure + add source map pragma
|
|
grunt.file.write(minFile, this.sourceMap(mapFileName, this.singleStrict(grunt.file.read(minFile), '\n')));
|
|
grunt.log.ok(file + ' minified into ' + minFile);
|
|
done();
|
|
}.bind(this));
|
|
},
|
|
|
|
memoryRequirement: function() {
|
|
return (process.platform === 'win32') ? '' : '-Xmx2g';
|
|
},
|
|
|
|
|
|
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
|
|
java32flags: function() {
|
|
if (process.platform === 'win32') return '';
|
|
if (shell.exec('java -d32 -version 2>&1', {silent: true}).code !== 0) return '';
|
|
return ' -d32 -client';
|
|
},
|
|
|
|
|
|
//collects and combines error messages stripped out in minify step
|
|
collectErrors: function() {
|
|
var combined = {
|
|
id: 'ng',
|
|
generated: new Date().toString(),
|
|
errors: {}
|
|
};
|
|
grunt.file.expand('build/*-errors.json').forEach(function(file) {
|
|
var errors = grunt.file.readJSON(file),
|
|
namespace;
|
|
Object.keys(errors).forEach(function(prop) {
|
|
if (typeof errors[prop] === 'object') {
|
|
namespace = errors[prop];
|
|
if (combined.errors[prop]) {
|
|
Object.keys(namespace).forEach(function(code) {
|
|
if (combined.errors[prop][code] && combined.errors[prop][code] !== namespace[code]) {
|
|
grunt.warn('[collect-errors] Duplicate minErr codes don\'t match!');
|
|
} else {
|
|
combined.errors[prop][code] = namespace[code];
|
|
}
|
|
});
|
|
} else {
|
|
combined.errors[prop] = namespace;
|
|
}
|
|
} else {
|
|
if (combined.errors[prop] && combined.errors[prop] !== errors[prop]) {
|
|
grunt.warn('[collect-errors] Duplicate minErr codes don\'t match!');
|
|
} else {
|
|
combined.errors[prop] = errors[prop];
|
|
}
|
|
}
|
|
});
|
|
});
|
|
grunt.file.write('build/errors.json', JSON.stringify(combined));
|
|
grunt.file.expand('build/*-errors.json').forEach(grunt.file.delete);
|
|
},
|
|
|
|
|
|
//csp connect middleware
|
|
conditionalCsp: function() {
|
|
return function(req, res, next) {
|
|
var CSP = /\.csp\W/;
|
|
|
|
if (CSP.test(req.url)) {
|
|
res.setHeader('X-WebKit-CSP', 'default-src \'self\';');
|
|
res.setHeader('X-Content-Security-Policy', 'default-src \'self\'');
|
|
res.setHeader('Content-Security-Policy', 'default-src \'self\'');
|
|
}
|
|
next();
|
|
};
|
|
},
|
|
|
|
|
|
//rewrite connect middleware
|
|
rewrite: function() {
|
|
return function(req, res, next) {
|
|
var REWRITE = /\/(guide|api|cookbook|misc|tutorial|error).*$/,
|
|
IGNORED = /(\.(css|js|png|jpg|gif|svg)$|partials\/.*\.html$)/,
|
|
match;
|
|
|
|
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
|
|
console.log('rewriting', req.url);
|
|
req.url = req.url.replace(match[0], '/index.html');
|
|
}
|
|
next();
|
|
};
|
|
},
|
|
|
|
// Our Firebase projects are in subfolders, but Travis expects them in the root,
|
|
// so we need to modify the upload folder path and copy the file into the root
|
|
firebaseDocsJsonForTravis: function() {
|
|
var docsScriptFolder = 'scripts/docs.angularjs.org-firebase';
|
|
|
|
var fileName = docsScriptFolder + '/firebase.json';
|
|
var json = grunt.file.readJSON(fileName);
|
|
|
|
(json.hosting || (json.hosting = {})).public = 'deploy/docs';
|
|
(json.functions || (json.functions = {})).source = docsScriptFolder + '/functions';
|
|
|
|
grunt.file.write('firebase.json', JSON.stringify(json));
|
|
}
|
|
};
|