diff --git a/Gruntfile.js b/Gruntfile.js
index fab8e27e743e..b4d9425bf0cb 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -4,6 +4,7 @@ var files = require('./angularFiles').files;
var util = require('./lib/grunt/utils.js');
var versionInfo = require('./lib/versions/version-info');
var path = require('path');
+var e2e = require('./test/e2e/tools');
module.exports = function(grunt) {
//grunt plugins
@@ -50,6 +51,7 @@ module.exports = function(grunt) {
return [
util.conditionalCsp(),
util.rewrite(),
+ e2e.middleware(),
connect.favicon('images/favicon.ico'),
connect.static(base),
connect.directory(base)
@@ -76,6 +78,7 @@ module.exports = function(grunt) {
next();
},
util.conditionalCsp(),
+ e2e.middleware(),
connect.favicon('images/favicon.ico'),
connect.static(base)
];
diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js
index cbf32cf360c7..208b109eaca5 100644
--- a/lib/grunt/plugins.js
+++ b/lib/grunt/plugins.js
@@ -57,7 +57,7 @@ module.exports = function(grunt) {
util.updateWebdriver.call(util, this.async());
});
- grunt.registerMultiTask('protractor', 'Run Protractor integration tests', function() {
+ grunt.registerMultiTask('protractor', 'Run Protractor Docs integration tests', function() {
util.startProtractor.call(util, this.data, this.async());
});
diff --git a/package.json b/package.json
index ecc7d1a57fd9..5298f8c5327e 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,8 @@
"semver": "~4.0.3",
"shelljs": "~0.3.0",
"sorted-object": "^1.0.0",
- "stringmap": "^0.2.2"
+ "stringmap": "^0.2.2",
+ "cheerio": "^0.17.0"
},
"licenses": [
{
diff --git a/protractor-conf.js b/protractor-conf.js
index 577d8b2b10ec..8fad56be8828 100644
--- a/protractor-conf.js
+++ b/protractor-conf.js
@@ -3,6 +3,7 @@
var config = require('./protractor-shared-conf').config;
config.specs = [
+ 'test/e2e/tests/**/*.js',
'build/docs/ptore2e/**/*.js',
'docs/app/e2e/**/*.scenario.js'
];
diff --git a/protractor-jenkins-conf.js b/protractor-jenkins-conf.js
index 64b67a2644ae..d1668e210a74 100644
--- a/protractor-jenkins-conf.js
+++ b/protractor-jenkins-conf.js
@@ -4,6 +4,7 @@ exports.config = {
allScriptsTimeout: 11000,
specs: [
+ 'test/e2e/tests/**/*.js',
'build/docs/ptore2e/**/*.js',
'docs/app/e2e/*.scenario.js'
],
@@ -30,7 +31,7 @@ exports.config = {
require('jasmine-reporters');
jasmine.getEnv().addReporter(
- new jasmine.JUnitXmlReporter('test_out/e2e-' + exports.config.capabilities.browserName + '-', true, true));
+ new jasmine.JUnitXmlReporter('test_out/docs-e2e-' + exports.config.capabilities.browserName + '-', true, true));
},
jasmineNodeOpts: {
diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh
index 658c146eb072..b4522cf43a38 100755
--- a/scripts/travis/build.sh
+++ b/scripts/travis/build.sh
@@ -9,12 +9,18 @@ if [ $JOB = "unit" ]; then
grunt test:promises-aplus
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
grunt tests:docs --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
- grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
+ grunt test:travis-protractor-docs --specs "docs/app/e2e/**/*.scenario.js"
elif [ $JOB = "e2e" ]; then
+ if [ $TEST_TARGET = "jquery" ]; then
+ export USE_JQUERY=1
+ fi
+
export TARGET_SPECS="build/docs/ptore2e/**/default_test.js"
if [ $TEST_TARGET = "jquery" ]; then
TARGET_SPECS="build/docs/ptore2e/**/jquery_test.js"
fi
+
+ export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
grunt test:travis-protractor --specs "$TARGET_SPECS"
else
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
diff --git a/test/e2e/fixtures/.jshintrc b/test/e2e/fixtures/.jshintrc
new file mode 100644
index 000000000000..f3f63911e866
--- /dev/null
+++ b/test/e2e/fixtures/.jshintrc
@@ -0,0 +1,8 @@
+{
+ "browser": true,
+ "globals": {
+ "angular": false,
+ "jQuery": false,
+ "$": false
+ }
+}
diff --git a/test/e2e/fixtures/sample/index.html b/test/e2e/fixtures/sample/index.html
new file mode 100644
index 000000000000..45e8747fa931
--- /dev/null
+++ b/test/e2e/fixtures/sample/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/test/e2e/fixtures/sample/script.js b/test/e2e/fixtures/sample/script.js
new file mode 100644
index 000000000000..2d625bb4f19c
--- /dev/null
+++ b/test/e2e/fixtures/sample/script.js
@@ -0,0 +1,4 @@
+angular.module("test", []).
+ controller("TestCtrl", function($scope) {
+ $scope.text = "Hello, world!";
+ });
diff --git a/test/e2e/templates/test.html b/test/e2e/templates/test.html
new file mode 100644
index 000000000000..2eec4e7ce621
--- /dev/null
+++ b/test/e2e/templates/test.html
@@ -0,0 +1,23 @@
+
+{% if eq(test.ngAppTag, "html") %}
+
+{% else %}
+
+{% endif %}
+
+ {% if scripts.jQuery %}
+
+ {% endif %}
+ {% for script in test.scripts %}
+
+ {% endfor %}
+ {{ test.head }}
+
+ {% if test.ngAppTag === "body" %}
+
+ {% else %}
+
+ {% endif %}
+ {% test.body %}
+
+
diff --git a/test/e2e/tests/.jshintrc b/test/e2e/tests/.jshintrc
new file mode 100644
index 000000000000..9ddc0db3923a
--- /dev/null
+++ b/test/e2e/tests/.jshintrc
@@ -0,0 +1,11 @@
+{
+ "node": true,
+ "globals": {
+ "describe": false,
+ "ddescribe": false,
+ "xdescribe": false,
+ "it": false,
+ "xit": false,
+ "iit": false
+ }
+}
diff --git a/test/e2e/tests/helpers/main.js b/test/e2e/tests/helpers/main.js
new file mode 100644
index 000000000000..116271f9136f
--- /dev/null
+++ b/test/e2e/tests/helpers/main.js
@@ -0,0 +1,23 @@
+var helper = {
+ andWaitForAngular: function() {
+ browser.waitForAngular();
+ },
+ loadFixture: function(fixture) {
+ var i = 0;
+ while (fixture[i] === '/') ++i;
+ fixture = fixture.slice(i);
+ if (!/\/(index\.html)?$/.test(fixture)) {
+ fixture += '/';
+ }
+
+ if (process.env.USE_JQUERY) {
+ fixture += '?jquery';
+ }
+
+ browser.get('/e2e/fixtures/' + fixture);
+ return helper;
+ }
+};
+
+global.test = helper;
+global.loadFixture = helper.loadFixture;
diff --git a/test/e2e/tests/sampleSpec.js b/test/e2e/tests/sampleSpec.js
new file mode 100644
index 000000000000..07cdec659e7e
--- /dev/null
+++ b/test/e2e/tests/sampleSpec.js
@@ -0,0 +1,12 @@
+// Sample E2E test:
+//
+describe('Sample', function() {
+ beforeEach(function() {
+ loadFixture("sample").andWaitForAngular();
+ });
+
+ it('should have the interpolated text', function() {
+ expect(element(by.binding('text')).getText())
+ .toBe('Hello, world!');
+ });
+});
diff --git a/test/e2e/tools/.jshintrc b/test/e2e/tools/.jshintrc
new file mode 100644
index 000000000000..6cf513eebee9
--- /dev/null
+++ b/test/e2e/tools/.jshintrc
@@ -0,0 +1,3 @@
+{
+ "node": true
+}
\ No newline at end of file
diff --git a/test/e2e/tools/fixture.js b/test/e2e/tools/fixture.js
new file mode 100644
index 000000000000..26723f032279
--- /dev/null
+++ b/test/e2e/tools/fixture.js
@@ -0,0 +1,83 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var $ = require('cheerio');
+var util = require('./util');
+
+var root = path.resolve(__dirname, '..');
+var fixtures = path.resolve(root, 'fixtures');
+
+var projectRoot = path.resolve(__dirname, '../../..');
+var build = path.resolve(projectRoot, 'build');
+
+function rewriteAngularSrc(src, query) {
+ if (query) {
+ if (query.build) {
+ return query.build + '/' + src;
+ } else if (query.cdn) {
+ return '//ajax.googleapis.com/ajax/libs/angularjs/' + query.cdn + '/' + src;
+ }
+ }
+ return '/build/' + src;
+}
+
+function generateFixture(test, query) {
+ var indexFile = path.resolve(fixtures, test, 'index.html');
+ var text = fs.readFileSync(indexFile, 'utf8');
+
+ var $$ = $.load(text);
+
+ var firstScript = null;
+ var jquery = null;
+ var angular = null;
+ $$('script').each(function(i, script) {
+ var src = $(script).attr('src');
+ if (src === 'jquery.js' && jquery === null) jquery = script;
+ else if (src === 'angular.js' && angular === null) angular = script;
+ if (firstScript === null) firstScript = script;
+ if (src) {
+ var s = util.stat(path.resolve(build, src));
+ if (s && s.isFile()) {
+ $(script).attr('src', rewriteAngularSrc(src, query));
+ } else {
+ $(script).attr('src', util.rewriteTestFile(test, src));
+ }
+ }
+ });
+
+ if (jquery && (!('jquery' in query) || (/^(0|no|false|off|n)$/i).test(query.jquery))) {
+ $(jquery).remove();
+ } else if ('jquery' in query) {
+ if ((/^(0|no|false|off|n)$/i).test(query.jquery)) {
+ if (jquery) {
+ $(jquery).remove();
+ }
+ } else {
+ if (!jquery) {
+ jquery = $.load('')('script')[0];
+ if (firstScript) {
+ $(firstScript).before(jquery);
+ } else {
+ var head = $$('head');
+ if (head.length) {
+ head.prepend(jquery);
+ } else {
+ $$.root().first().before(jquery);
+ }
+ }
+ }
+ if (!/^\d+\.\d+.*$/.test(query.jquery)) {
+ $(jquery).attr('src', '/bower_components/jquery/dist/jquery.js');
+ } else {
+ $(jquery).attr('src', '//ajax.googleapis.com/ajax/libs/jquery/' + query.jquery + '/jquery.js');
+ }
+ }
+ }
+
+ return $$.html();
+}
+
+module.exports = {
+ generate: generateFixture
+};
diff --git a/test/e2e/tools/index.js b/test/e2e/tools/index.js
new file mode 100644
index 000000000000..45f3d1284291
--- /dev/null
+++ b/test/e2e/tools/index.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = {
+ middleware: require('./middleware')
+};
diff --git a/test/e2e/tools/middleware.js b/test/e2e/tools/middleware.js
new file mode 100644
index 000000000000..39e88a176bab
--- /dev/null
+++ b/test/e2e/tools/middleware.js
@@ -0,0 +1,44 @@
+'use strict';
+
+var url = require('url');
+var util = require('./util');
+var fixture = require('./fixture');
+
+module.exports = middlewareFactory;
+
+function middlewareFactory(base) {
+ base = base || '/e2e';
+ while (base.length && base[base.length-1] === '/') base = base.slice(0, base.length-1);
+ var fixture_regexp = new RegExp('^' + base + '/fixtures/([a-zA-Z0-9_-]+)(/(index.html)?)?$');
+ var static_regexp = new RegExp('^' + base + '/fixtures/([a-zA-Z0-9_-]+)(/.*)$');
+
+ return function(req, res, next) {
+ var match;
+ var basicUrl = req.url;
+ var idx = basicUrl.indexOf('?');
+ if (idx >= 0) {
+ basicUrl = basicUrl.slice(0, idx);
+ }
+ if ((match = fixture_regexp.exec(basicUrl))) {
+ if (util.testExists(match[1])) {
+ try {
+ var query = url.parse(req.url, true).query;
+ res.write(fixture.generate(match[1], query));
+ res.end();
+ } catch (e) {
+ return next(e);
+ }
+ } else {
+ return next('Fixture ' + match[1] + ' not found.');
+ }
+ } else if ((match = static_regexp.exec(basicUrl))) {
+ var rewritten = util.rewriteTestFile(match[1], match[2]);
+ if (rewritten !== false) {
+ req.url = rewritten;
+ }
+ next();
+ } else {
+ return next();
+ }
+ };
+}
diff --git a/test/e2e/tools/util.js b/test/e2e/tools/util.js
new file mode 100644
index 000000000000..313e9bda66db
--- /dev/null
+++ b/test/e2e/tools/util.js
@@ -0,0 +1,41 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var url = require('url');
+
+var root = path.resolve(__dirname, '..');
+var tests = path.resolve(root, 'fixtures');
+
+function stat(path) {
+ try {
+ return fs.statSync(path);
+ } catch (e) {
+ // Ignore ENOENT.
+ if (e.code !== 'ENOENT') {
+ throw e;
+ }
+ }
+}
+
+function testExists(testname) {
+ var s = stat(path.resolve(tests, testname));
+ return s && s.isDirectory();
+}
+
+function rewriteTestFile(testname, testfile) {
+ var i = 0;
+ while (testfile[i] === '/') ++i;
+ testfile = testfile.slice(i);
+ var s = stat(path.resolve(tests, testname, testfile));
+ if (s && (s.isFile() || s.isDirectory())) {
+ return ['/test/e2e/fixtures', testname, testfile].join('/');
+ }
+ return false;
+}
+
+module.exports = {
+ stat: stat,
+ testExists: testExists,
+ rewriteTestFile: rewriteTestFile
+};