Skip to content

Commit 2240c11

Browse files
committed
chore(tests): implement e2e test harness outside of docs app
Included: - A sample test fixture - A sample test - Server middleware to serve the E2E harness - Convenient test helpers to simplify loading the right fixture Closes angular#9557 Closes angular#9527
1 parent fc56c9b commit 2240c11

16 files changed

+278
-1
lines changed

Gruntfile.js

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var files = require('./angularFiles').files;
44
var util = require('./lib/grunt/utils.js');
55
var versionInfo = require('./lib/versions/version-info');
66
var path = require('path');
7+
var e2e = require('./test/e2e/tools');
78

89
module.exports = function(grunt) {
910
//grunt plugins
@@ -50,6 +51,7 @@ module.exports = function(grunt) {
5051
return [
5152
util.conditionalCsp(),
5253
util.rewrite(),
54+
e2e.middleware(),
5355
connect.favicon('images/favicon.ico'),
5456
connect.static(base),
5557
connect.directory(base)
@@ -76,6 +78,7 @@ module.exports = function(grunt) {
7678
next();
7779
},
7880
util.conditionalCsp(),
81+
e2e.middleware(),
7982
connect.favicon('images/favicon.ico'),
8083
connect.static(base)
8184
];

protractor-conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
var config = require('./protractor-shared-conf').config;
44

55
config.specs = [
6+
'test/e2e/tests/**/*.js',
67
'build/docs/ptore2e/**/*.js',
78
'docs/app/e2e/**/*.scenario.js'
89
];

protractor-jenkins-conf.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exports.config = {
44
allScriptsTimeout: 11000,
55

66
specs: [
7+
'test/e2e/tests/**/*.js',
78
'build/docs/ptore2e/**/*.js',
89
'docs/app/e2e/*.scenario.js'
910
],
@@ -30,7 +31,7 @@ exports.config = {
3031

3132
require('jasmine-reporters');
3233
jasmine.getEnv().addReporter(
33-
new jasmine.JUnitXmlReporter('test_out/e2e-' + exports.config.capabilities.browserName + '-', true, true));
34+
new jasmine.JUnitXmlReporter('test_out/docs-e2e-' + exports.config.capabilities.browserName + '-', true, true));
3435
},
3536

3637
jasmineNodeOpts: {

scripts/travis/build.sh

+6
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ if [ $JOB = "unit" ]; then
1111
grunt tests:docs --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
1212
grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
1313
elif [ $JOB = "e2e" ]; then
14+
if [ $TEST_TARGET = "jquery" ]; then
15+
export USE_JQUERY=1
16+
fi
17+
1418
export TARGET_SPECS="build/docs/ptore2e/**/default_test.js"
1519
if [ $TEST_TARGET = "jquery" ]; then
1620
TARGET_SPECS="build/docs/ptore2e/**/jquery_test.js"
1721
fi
22+
23+
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
1824
grunt test:travis-protractor --specs "$TARGET_SPECS"
1925
else
2026
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."

test/e2e/fixtures/.jshintrc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"browser": true,
3+
"globals": {
4+
"angular": false,
5+
"jQuery": false,
6+
"$": false
7+
}
8+
}

test/e2e/fixtures/sample/index.html

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html ng-app="test">
3+
<div ng-controller="TestCtrl">
4+
<p>{{text}}</p>
5+
</div>
6+
7+
<script src="angular.js"></script>
8+
<script src="script.js"></script>
9+
</html>

test/e2e/fixtures/sample/script.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
angular.module("test", []).
2+
controller("TestCtrl", function($scope) {
3+
$scope.text = "Hello, world!";
4+
});

test/e2e/templates/test.html

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
{% if eq(test.ngAppTag, "html") %}
3+
<html ng-app="{{ test.ngApp }}">
4+
{% else %}
5+
<html>
6+
{% endif %}
7+
<head>
8+
{% if scripts.jQuery %}
9+
<script src="{{ scripts.jQuery }}"></script>
10+
{% endif %}
11+
{% for script in test.scripts %}
12+
<script src="{{ test.scripts[script] }}"></script>
13+
{% endfor %}
14+
{{ test.head }}
15+
</head>
16+
{% if test.ngAppTag === "body" %}
17+
<body ng-app="{{ test.ngApp }}">
18+
{% else %}
19+
<body>
20+
{% endif %}
21+
{% test.body %}
22+
</body>
23+
</html>

test/e2e/tests/.jshintrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"node": true,
3+
"globals": {
4+
"describe": false,
5+
"ddescribe": false,
6+
"xdescribe": false,
7+
"it": false,
8+
"xit": false,
9+
"iit": false
10+
}
11+
}

test/e2e/tests/helpers/main.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var helper = {
2+
andWaitForAngular: function() {
3+
browser.waitForAngular();
4+
},
5+
loadFixture: function(fixture) {
6+
var i = 0;
7+
while (fixture[i] === '/') ++i;
8+
fixture = fixture.slice(i);
9+
if (!/\/(index\.html)?$/.test(fixture)) {
10+
fixture += '/';
11+
}
12+
13+
if (process.env.USE_JQUERY) {
14+
fixture += '?jquery';
15+
}
16+
17+
browser.get('/e2e/fixtures/' + fixture);
18+
return helper;
19+
}
20+
};
21+
22+
global.test = helper;
23+
global.loadFixture = helper.loadFixture;

test/e2e/tests/sampleSpec.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Sample E2E test:
2+
//
3+
describe('Sample', function() {
4+
beforeEach(function() {
5+
loadFixture("sample").andWaitForAngular();
6+
});
7+
8+
it('should have the interpolated text', function() {
9+
expect(element(by.binding('text')).getText())
10+
.toBe('Hello, world!');
11+
});
12+
});

test/e2e/tools/.jshintrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"node": true
3+
}

test/e2e/tools/fixture.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var path = require('path');
5+
var $ = require('cheerio');
6+
var util = require('./util');
7+
8+
var root = path.resolve(__dirname, '..');
9+
var fixtures = path.resolve(root, 'fixtures');
10+
11+
var projectRoot = path.resolve(__dirname, '../../..');
12+
var build = path.resolve(projectRoot, 'build');
13+
14+
function rewriteAngularSrc(src, query) {
15+
if (query) {
16+
if (query.build) {
17+
return query.build + '/' + src;
18+
} else if (query.cdn) {
19+
return '//ajax.googleapis.com/ajax/libs/angularjs/' + query.cdn + '/' + src;
20+
}
21+
}
22+
return '/build/' + src;
23+
}
24+
25+
function generateFixture(test, query) {
26+
var indexFile = path.resolve(fixtures, test, 'index.html');
27+
var text = fs.readFileSync(indexFile, 'utf8');
28+
29+
var $$ = $.load(text);
30+
31+
var firstScript = null;
32+
var jquery = null;
33+
var angular = null;
34+
$$('script').each(function(i, script) {
35+
var src = $(script).attr('src');
36+
if (src === 'jquery.js' && jquery === null) jquery = script;
37+
else if (src === 'angular.js' && angular === null) angular = script;
38+
if (firstScript === null) firstScript = script;
39+
if (src) {
40+
var s = util.stat(path.resolve(build, src));
41+
if (s && s.isFile()) {
42+
$(script).attr('src', rewriteAngularSrc(src, query));
43+
} else {
44+
$(script).attr('src', util.rewriteTestFile(test, src));
45+
}
46+
}
47+
});
48+
49+
if (jquery && (!('jquery' in query) || (/^(0|no|false|off|n)$/i).test(query.jquery))) {
50+
$(jquery).remove();
51+
} else if ('jquery' in query) {
52+
if ((/^(0|no|false|off|n)$/i).test(query.jquery)) {
53+
if (jquery) {
54+
$(jquery).remove();
55+
}
56+
} else {
57+
if (!jquery) {
58+
jquery = $.load('<script></script>')('script')[0];
59+
if (firstScript) {
60+
$(firstScript).before(jquery);
61+
} else {
62+
var head = $$('head');
63+
if (head.length) {
64+
head.prepend(jquery);
65+
} else {
66+
$$.root().first().before(jquery);
67+
}
68+
}
69+
}
70+
if (!/^\d+\.\d+.*$/.test(query.jquery)) {
71+
$(jquery).attr('src', '/bower_components/jquery/dist/jquery.js');
72+
} else {
73+
$(jquery).attr('src', '//ajax.googleapis.com/ajax/libs/jquery/' + query.jquery + '/jquery.js');
74+
}
75+
}
76+
}
77+
78+
return $$.html();
79+
}
80+
81+
module.exports = {
82+
generate: generateFixture
83+
};

test/e2e/tools/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = {
4+
middleware: require('./middleware')
5+
};

test/e2e/tools/middleware.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
var url = require('url');
4+
var util = require('./util');
5+
var fixture = require('./fixture');
6+
7+
module.exports = middlewareFactory;
8+
9+
function middlewareFactory(base) {
10+
base = base || '/e2e';
11+
while (base.length && base[base.length-1] === '/') base = base.slice(0, base.length-1);
12+
var fixture_regexp = new RegExp('^' + base + '/fixtures/([a-zA-Z0-9_-]+)(/(index.html)?)?$');
13+
var static_regexp = new RegExp('^' + base + '/fixtures/([a-zA-Z0-9_-]+)(/.*)$');
14+
15+
return function(req, res, next) {
16+
var match;
17+
var basicUrl = req.url;
18+
var idx = basicUrl.indexOf('?');
19+
if (idx >= 0) {
20+
basicUrl = basicUrl.slice(0, idx);
21+
}
22+
if ((match = fixture_regexp.exec(basicUrl))) {
23+
if (util.testExists(match[1])) {
24+
try {
25+
var query = url.parse(req.url, true).query;
26+
res.write(fixture.generate(match[1], query));
27+
res.end();
28+
} catch (e) {
29+
return next(e);
30+
}
31+
} else {
32+
return next('Fixture ' + match[1] + ' not found.');
33+
}
34+
} else if ((match = static_regexp.exec(basicUrl))) {
35+
var rewritten = util.rewriteTestFile(match[1], match[2]);
36+
if (rewritten !== false) {
37+
req.url = rewritten;
38+
}
39+
next();
40+
} else {
41+
return next();
42+
}
43+
};
44+
}

test/e2e/tools/util.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var path = require('path');
5+
var url = require('url');
6+
7+
var root = path.resolve(__dirname, '..');
8+
var tests = path.resolve(root, 'fixtures');
9+
10+
function stat(path) {
11+
try {
12+
return fs.statSync(path);
13+
} catch (e) {
14+
// Ignore ENOENT.
15+
if (e.code !== 'ENOENT') {
16+
throw e;
17+
}
18+
}
19+
}
20+
21+
function testExists(testname) {
22+
var s = stat(path.resolve(tests, testname));
23+
return s && s.isDirectory();
24+
}
25+
26+
function rewriteTestFile(testname, testfile) {
27+
var i = 0;
28+
while (testfile[i] === '/') ++i;
29+
testfile = testfile.slice(i);
30+
var s = stat(path.resolve(tests, testname, testfile));
31+
if (s && (s.isFile() || s.isDirectory())) {
32+
return ['/test/e2e/fixtures', testname, testfile].join('/');
33+
}
34+
return false;
35+
}
36+
37+
module.exports = {
38+
stat: stat,
39+
testExists: testExists,
40+
rewriteTestFile: rewriteTestFile
41+
};

0 commit comments

Comments
 (0)