mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'test-in-browser-with-finer-deps' into devel
This commit is contained in:
@@ -4,11 +4,13 @@
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
{{> test_table}}
|
||||
{{> navBars}}
|
||||
{{> failedTests}}
|
||||
{{> testTable}}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<template name="test_table">
|
||||
<template name="navBars">
|
||||
<div class="navbar navbar-fixed-top navbar-inverse">
|
||||
<div class="navbar-inner">
|
||||
<div class="row-fluid">
|
||||
@@ -37,19 +39,6 @@
|
||||
</div>
|
||||
</div>
|
||||
{{> groupNav}}
|
||||
<div class="row-fluid"><div class="span12">
|
||||
<ul class="failedTests">
|
||||
{{#each failedTests}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<div class="test_table">
|
||||
{{#each data}}
|
||||
{{> test_group this}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div></div>
|
||||
</template>
|
||||
|
||||
<template name="progressBar">
|
||||
@@ -85,7 +74,28 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="failedTests">
|
||||
<div class="row-fluid"><div class="span12">
|
||||
<ul class="failedTests">
|
||||
{{#each failedTests}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div></div>
|
||||
</template>
|
||||
|
||||
<template name="testTable">
|
||||
<div class="row-fluid"><div class="span12">
|
||||
<div class="test_table">
|
||||
{{#each data}}
|
||||
{{> test_group this}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div></div>
|
||||
</template>
|
||||
|
||||
<template name="test_group">
|
||||
{{groupDep}}
|
||||
<div class="group">
|
||||
<div class="groupname"><a>{{name}}</a></div>
|
||||
{{#each tests}}
|
||||
@@ -98,6 +108,7 @@
|
||||
</template>
|
||||
|
||||
<template name="test">
|
||||
{{testDep}}
|
||||
<div class="test {{test_class}}">
|
||||
<div class="testrow">
|
||||
<div class="teststatus">
|
||||
|
||||
@@ -1,12 +1,39 @@
|
||||
var running = true;
|
||||
////
|
||||
//// Setup
|
||||
////
|
||||
|
||||
var resultTree = [];
|
||||
var failedTests = [];
|
||||
var resultsDeps = new Deps.Dependency;
|
||||
var countDeps = new Deps.Dependency;
|
||||
|
||||
// dependency for the count of tests running/passed/failed, etc. drives
|
||||
// the navbar and the like.
|
||||
var countDep = new Deps.Dependency;
|
||||
// things that change on countDep
|
||||
var running = true;
|
||||
var totalCount = 0;
|
||||
var passedCount = 0;
|
||||
var failedCount = 0;
|
||||
var failedTests = [];
|
||||
|
||||
// Dependency for when a new top level group is added. Each group and
|
||||
// each test have their own dependency objects.
|
||||
var topLevelGroupsDep = new Deps.Dependency;
|
||||
|
||||
// An array of top-level groups.
|
||||
//
|
||||
// Each group is an object with:
|
||||
// - name: string
|
||||
// - path: array of strings (names of parent groups)
|
||||
// - parent: parent group object (back reference)
|
||||
// - dep: Deps.Dependency object for this group. fires when new tests added.
|
||||
// - groups: list of sub-groups
|
||||
// - tests: list of tests in this group
|
||||
//
|
||||
// Each test is an object with:
|
||||
// - name: string
|
||||
// - parent: parent group object (back reference)
|
||||
// - server: boolean
|
||||
// - fullName: string
|
||||
// - dep: Deps.Dependency object for this test. fires when the test completes.
|
||||
var resultTree = [];
|
||||
|
||||
|
||||
Session.setDefault("groupPath", ["tinytest"]);
|
||||
@@ -17,7 +44,7 @@ Meteor.startup(function () {
|
||||
Meteor._runTestsEverywhere(reportResults, function () {
|
||||
running = false;
|
||||
Meteor.onTestsComplete && Meteor.onTestsComplete();
|
||||
resultsDeps.changed();
|
||||
countDep.changed();
|
||||
Deps.flush();
|
||||
|
||||
Meteor.default_connection._unsubscribeAll();
|
||||
@@ -25,38 +52,245 @@ Meteor.startup(function () {
|
||||
|
||||
});
|
||||
|
||||
|
||||
////
|
||||
//// Take incoming results and drive resultsTree
|
||||
////
|
||||
|
||||
// report a series of events in a single test, or just the existence of
|
||||
// that test if no events. this is the entry point for test results to
|
||||
// this module.
|
||||
var reportResults = function(results) {
|
||||
var test = _findTestForResults(results);
|
||||
|
||||
if (_.isArray(results.events)) {
|
||||
// append events, if present
|
||||
Array.prototype.push.apply((test.events || (test.events = [])),
|
||||
results.events);
|
||||
// sort and de-duplicate, based on sequence number
|
||||
test.events.sort(function (a, b) {
|
||||
return a.sequence - b.sequence;
|
||||
});
|
||||
var out = [];
|
||||
_.each(test.events, function (e) {
|
||||
if (out.length === 0 || out[out.length - 1].sequence !== e.sequence)
|
||||
out.push(e);
|
||||
});
|
||||
test.events = out;
|
||||
}
|
||||
var status = _testStatus(test);
|
||||
if (status === "failed") {
|
||||
failedCount++;
|
||||
// Expand a failed test (but only set this if the user hasn't clicked on the
|
||||
// test name yet).
|
||||
if (test.expanded === undefined)
|
||||
test.expanded = true;
|
||||
if (!_.contains(failedTests, test.fullName))
|
||||
failedTests.push(test.fullName);
|
||||
|
||||
countDep.changed();
|
||||
test.dep.changed();
|
||||
} else if (status === "succeeded") {
|
||||
passedCount++;
|
||||
countDep.changed();
|
||||
test.dep.changed();
|
||||
} else if (test.expanded) {
|
||||
// re-render the test if new results come in and the test is
|
||||
// currently expanded.
|
||||
test.dep.changed();
|
||||
}
|
||||
};
|
||||
|
||||
// forget all of the events for a particular test
|
||||
var forgetEvents = function (results) {
|
||||
var test = _findTestForResults(results);
|
||||
var status = _testStatus(test);
|
||||
if (status === "failed") {
|
||||
failedCount--;
|
||||
countDep.changed();
|
||||
} else if (status === "succeeded") {
|
||||
passedCount--;
|
||||
countDep.changed();
|
||||
}
|
||||
delete test.events;
|
||||
test.dep.changed();
|
||||
};
|
||||
|
||||
// given a 'results' as delivered via reportResults, find the
|
||||
// corresponding leaf object in resultTree, creating one if it doesn't
|
||||
// exist. it will be an object with attributes 'name', 'parent', and
|
||||
// possibly 'events'.
|
||||
var _findTestForResults = function (results) {
|
||||
var groupPath = results.groupPath; // array
|
||||
if ((! _.isArray(groupPath)) || (groupPath.length < 1)) {
|
||||
throw new Error("Test must be part of a group");
|
||||
}
|
||||
|
||||
var group;
|
||||
var i = 0;
|
||||
_.each(groupPath, function(gname) {
|
||||
var array = (group ? (group.groups || (group.groups = []))
|
||||
: resultTree);
|
||||
var newGroup = _.find(array, function(g) { return g.name === gname; });
|
||||
if (! newGroup) {
|
||||
newGroup = {
|
||||
name: gname,
|
||||
parent: (group || null),
|
||||
path: groupPath.slice(0, i+1),
|
||||
dep: new Deps.Dependency
|
||||
}; // create group
|
||||
array.push(newGroup);
|
||||
|
||||
if (group)
|
||||
group.dep.changed();
|
||||
else
|
||||
topLevelGroupsDep.changed();
|
||||
}
|
||||
group = newGroup;
|
||||
i++;
|
||||
});
|
||||
|
||||
var testName = results.test;
|
||||
var server = !!results.server;
|
||||
var test = _.find(group.tests || (group.tests = []),
|
||||
function(t) { return t.name === testName &&
|
||||
t.server === server; });
|
||||
if (! test) {
|
||||
// create test
|
||||
var nameParts = _.clone(groupPath);
|
||||
nameParts.push(testName);
|
||||
var fullName = nameParts.join(' - ');
|
||||
test = {
|
||||
name: testName,
|
||||
parent: group,
|
||||
server: server,
|
||||
fullName: fullName,
|
||||
dep: new Deps.Dependency
|
||||
};
|
||||
group.tests.push(test);
|
||||
group.dep.changed();
|
||||
totalCount++;
|
||||
countDep.changed();
|
||||
}
|
||||
|
||||
return test;
|
||||
};
|
||||
|
||||
|
||||
|
||||
////
|
||||
//// Helpers on test objects
|
||||
////
|
||||
|
||||
var _testTime = function(t) {
|
||||
if (t.events && t.events.length > 0) {
|
||||
var lastEvent = _.last(t.events);
|
||||
if (lastEvent.type === "finish") {
|
||||
if ((typeof lastEvent.timeMs) === "number") {
|
||||
return lastEvent.timeMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var _testStatus = function(t) {
|
||||
var events = t.events || [];
|
||||
if (_.find(events, function(x) { return x.type === "exception"; })) {
|
||||
// "exception" should be last event, except race conditions on the
|
||||
// server can make this not the case. Technically we can't tell
|
||||
// if the test is still running at this point, but it can only
|
||||
// result in FAIL.
|
||||
return "failed";
|
||||
} else if (events.length == 0 || (_.last(events).type != "finish")) {
|
||||
return "running";
|
||||
} else if (_.any(events, function(e) {
|
||||
return e.type == "fail" || e.type == "exception"; })) {
|
||||
return "failed";
|
||||
} else {
|
||||
return "succeeded";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
////
|
||||
//// Templates
|
||||
////
|
||||
|
||||
//// Template - navBars
|
||||
|
||||
Template.navBars.running = function() {
|
||||
countDep.depend();
|
||||
return running;
|
||||
};
|
||||
|
||||
Template.navBars.passed = function() {
|
||||
countDep.depend();
|
||||
return failedCount === 0;
|
||||
};
|
||||
|
||||
Template.navBars.total_test_time = function() {
|
||||
countDep.depend();
|
||||
|
||||
// walk whole tree to get all tests
|
||||
var walk = function (groups) {
|
||||
var total = 0;
|
||||
|
||||
_.each(groups || [], function (group) {
|
||||
_.each(group.tests || [], function (t) {
|
||||
total += _testTime(t);
|
||||
});
|
||||
|
||||
total += walk(group.groups);
|
||||
});
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
return walk(resultTree);
|
||||
};
|
||||
|
||||
|
||||
//// Template - progressBar
|
||||
|
||||
Template.progressBar.running = function () {
|
||||
countDeps.depend();
|
||||
return passedCount + failedCount < totalCount;
|
||||
countDep.depend();
|
||||
return running;
|
||||
};
|
||||
|
||||
Template.progressBar.percentPass = function () {
|
||||
countDeps.depend();
|
||||
countDep.depend();
|
||||
if (totalCount === 0)
|
||||
return 0;
|
||||
return 100*passedCount/totalCount;
|
||||
};
|
||||
|
||||
Template.progressBar.totalCount = function () {
|
||||
countDep.depend();
|
||||
return totalCount;
|
||||
};
|
||||
|
||||
Template.progressBar.passedCount = function () {
|
||||
countDep.depend();
|
||||
return passedCount;
|
||||
};
|
||||
|
||||
Template.progressBar.percentFail = function () {
|
||||
countDeps.depend();
|
||||
countDep.depend();
|
||||
if (totalCount === 0)
|
||||
return 0;
|
||||
return 100*failedCount/totalCount;
|
||||
};
|
||||
|
||||
Template.progressBar.anyFail = function () {
|
||||
countDeps.depend();
|
||||
countDep.depend();
|
||||
return failedCount > 0;
|
||||
};
|
||||
|
||||
|
||||
//// Template - groupNav
|
||||
|
||||
Template.groupNav.groupPaths = function () {
|
||||
var groupPath = Session.get("groupPath");
|
||||
var ret = [];
|
||||
@@ -88,76 +322,47 @@ Template.groupNav.events({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//// Template - failedTests
|
||||
|
||||
Template.failedTests.failedTests = function() {
|
||||
countDep.depend();
|
||||
return failedTests;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//// Template - testTable
|
||||
|
||||
Template.testTable.data = function() {
|
||||
topLevelGroupsDep.depend();
|
||||
return resultTree;
|
||||
};
|
||||
|
||||
|
||||
//// Template - test_group
|
||||
|
||||
Template.test_group.groupDep = function () {
|
||||
// this template just establishes a dependency. It doesn't actually
|
||||
// render anything.
|
||||
this.dep.depend();
|
||||
return "";
|
||||
};
|
||||
|
||||
Template.test_group.events({
|
||||
"click .groupname": function () {
|
||||
changeToPath(this.path);
|
||||
}
|
||||
});
|
||||
|
||||
Template.test_table.running = function() {
|
||||
resultsDeps.depend();
|
||||
return running;
|
||||
};
|
||||
|
||||
Template.test_table.passed = function() {
|
||||
resultsDeps.depend();
|
||||
//// Template - test
|
||||
|
||||
// walk whole tree to look for failed tests
|
||||
var walk = function (groups) {
|
||||
var ret = true;
|
||||
|
||||
_.each(groups || [], function (group) {
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
_.each(group.tests || [], function (t) {
|
||||
if (!ret)
|
||||
return;
|
||||
if (_testStatus(t) === "failed")
|
||||
ret = false;
|
||||
});
|
||||
|
||||
if (!walk(group.groups))
|
||||
ret = false;
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
return walk(resultTree);
|
||||
};
|
||||
|
||||
|
||||
Template.test_table.total_test_time = function() {
|
||||
resultsDeps.depend();
|
||||
|
||||
// walk whole tree to get all tests
|
||||
var walk = function (groups) {
|
||||
var total = 0;
|
||||
|
||||
_.each(groups || [], function (group) {
|
||||
_.each(group.tests || [], function (t) {
|
||||
total += _testTime(t);
|
||||
});
|
||||
|
||||
total += walk(group.groups);
|
||||
});
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
return walk(resultTree);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Template.test_table.data = function() {
|
||||
resultsDeps.depend();
|
||||
return resultTree;
|
||||
};
|
||||
Template.test_table.failedTests = function() {
|
||||
resultsDeps.depend();
|
||||
return failedTests;
|
||||
Template.test.testDep = function () {
|
||||
// this template just establishes a dependency. It doesn't actually
|
||||
// render anything.
|
||||
this.dep.depend();
|
||||
return "";
|
||||
};
|
||||
|
||||
Template.test.test_status_display = function() {
|
||||
@@ -192,7 +397,7 @@ Template.test.test_class = function() {
|
||||
Template.test.events({
|
||||
'click .testname': function() {
|
||||
this.expanded = ! this.expanded;
|
||||
resultsDeps.changed();
|
||||
this.dep.changed();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -231,6 +436,9 @@ Template.test.eventsArray = function() {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//// Template - event
|
||||
|
||||
Template.event.events({
|
||||
'click .debug': function () {
|
||||
// the way we manage groupPath, shortName, cookies, etc, is really
|
||||
@@ -285,136 +493,3 @@ Template.event.is_debuggable = function() {
|
||||
return !!this.cookie;
|
||||
};
|
||||
|
||||
var _testTime = function(t) {
|
||||
if (t.events && t.events.length > 0) {
|
||||
var lastEvent = _.last(t.events);
|
||||
if (lastEvent.type === "finish") {
|
||||
if ((typeof lastEvent.timeMs) === "number") {
|
||||
return lastEvent.timeMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var _testStatus = function(t) {
|
||||
var events = t.events || [];
|
||||
if (_.find(events, function(x) { return x.type === "exception"; })) {
|
||||
// "exception" should be last event, except race conditions on the
|
||||
// server can make this not the case. Technically we can't tell
|
||||
// if the test is still running at this point, but it can only
|
||||
// result in FAIL.
|
||||
return "failed";
|
||||
} else if (events.length == 0 || (_.last(events).type != "finish")) {
|
||||
return "running";
|
||||
} else if (_.any(events, function(e) {
|
||||
return e.type == "fail" || e.type == "exception"; })) {
|
||||
return "failed";
|
||||
} else {
|
||||
return "succeeded";
|
||||
}
|
||||
};
|
||||
|
||||
// given a 'results' as delivered via setReporter, find the
|
||||
// corresponding leaf object in resultTree, creating one if it doesn't
|
||||
// exist. it will be an object with attributes 'name', 'parent', and
|
||||
// possibly 'events'.
|
||||
var _findTestForResults = function (results) {
|
||||
var groupPath = results.groupPath; // array
|
||||
if ((! _.isArray(groupPath)) || (groupPath.length < 1)) {
|
||||
throw new Error("Test must be part of a group");
|
||||
}
|
||||
|
||||
var group;
|
||||
var i = 0;
|
||||
_.each(groupPath, function(gname) {
|
||||
var array = (group ? (group.groups || (group.groups = []))
|
||||
: resultTree);
|
||||
var newGroup = _.find(array, function(g) { return g.name === gname; });
|
||||
if (! newGroup) {
|
||||
newGroup = {
|
||||
name: gname,
|
||||
parent: (group || null),
|
||||
path: groupPath.slice(0, i+1)
|
||||
}; // create group
|
||||
array.push(newGroup);
|
||||
}
|
||||
group = newGroup;
|
||||
i++;
|
||||
});
|
||||
|
||||
var testName = results.test;
|
||||
var server = !!results.server;
|
||||
var test = _.find(group.tests || (group.tests = []),
|
||||
function(t) { return t.name === testName &&
|
||||
t.server === server; });
|
||||
if (! test) {
|
||||
// create test
|
||||
var nameParts = _.clone(groupPath);
|
||||
nameParts.push(testName);
|
||||
var fullName = nameParts.join(' - ');
|
||||
test = {name: testName, parent: group, server: server, fullName: fullName};
|
||||
group.tests.push(test);
|
||||
totalCount++;
|
||||
countDeps.changed();
|
||||
}
|
||||
|
||||
return test;
|
||||
};
|
||||
|
||||
// report a series of events in a single test, or just
|
||||
// the existence of that test if no events
|
||||
var reportResults = function(results) {
|
||||
var test = _findTestForResults(results);
|
||||
|
||||
if (_.isArray(results.events)) {
|
||||
// append events, if present
|
||||
Array.prototype.push.apply((test.events || (test.events = [])),
|
||||
results.events);
|
||||
// sort and de-duplicate, based on sequence number
|
||||
test.events.sort(function (a, b) {
|
||||
return a.sequence - b.sequence;
|
||||
});
|
||||
var out = [];
|
||||
_.each(test.events, function (e) {
|
||||
if (out.length === 0 || out[out.length - 1].sequence !== e.sequence)
|
||||
out.push(e);
|
||||
});
|
||||
test.events = out;
|
||||
}
|
||||
var status = _testStatus(test);
|
||||
if (status === "failed") {
|
||||
failedCount++;
|
||||
countDeps.changed();
|
||||
// Expand a failed test (but only set this if the user hasn't clicked on the
|
||||
// test name yet).
|
||||
if (test.expanded === undefined)
|
||||
test.expanded = true;
|
||||
if (!_.contains(failedTests, test.fullName))
|
||||
failedTests.push(test.fullName);
|
||||
} else if (status === "succeeded") {
|
||||
passedCount++;
|
||||
countDeps.changed();
|
||||
}
|
||||
|
||||
_throttled_update();
|
||||
};
|
||||
|
||||
// forget all of the events for a particular test
|
||||
var forgetEvents = function (results) {
|
||||
var test = _findTestForResults(results);
|
||||
var status = _testStatus(test);
|
||||
if (status === "failed") {
|
||||
failedCount--;
|
||||
countDeps.changed();
|
||||
} else if (status === "succeeded") {
|
||||
passedCount--;
|
||||
countDeps.changed();
|
||||
}
|
||||
delete test.events;
|
||||
resultsDeps.changed();
|
||||
};
|
||||
|
||||
var _throttled_update = _.throttle(function() {
|
||||
resultsDeps.changed();
|
||||
}, 1000);
|
||||
|
||||
Reference in New Issue
Block a user