Major refactoring of Spec. Moved QueuedFunction to Block, WaitsBlock and WaitsForBlock. Waits and WaitsFor blocks now sequentially stackable

This commit is contained in:
ragaskar 2009-07-29 22:27:11 -07:00
parent 708d148d31
commit a1a278ee69
10 changed files with 912 additions and 754 deletions

View File

@ -1,8 +1,8 @@
desc 'Builds lib/jasmine from source'
task :build do
# these files must be better
sources = ["src/base.js", "src/util.js", "src/Env.js", "src/ActionCollection.js", "src/Reporter.js"]
# these files must be loaded first
sources = ["src/base.js", "src/util.js", "src/Env.js", "src/ActionCollection.js", "src/Reporter.js", "src/Block.js"]
sources += Dir.glob('src/*.js').reject{|f| sources.include?(f)}

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,9 @@
<script type="text/javascript" src="../src/Matchers.js"></script>
<script type="text/javascript" src="../src/NestedResults.js"></script>
<script type="text/javascript" src="../src/PrettyPrinter.js"></script>
<script type="text/javascript" src="../src/QueuedFunction.js"></script>
<script type="text/javascript" src="../src/Block.js"></script>
<script type="text/javascript" src="../src/WaitsBlock.js"></script>
<script type="text/javascript" src="../src/WaitsForBlock.js"></script>
<script type="text/javascript" src="../src/Reporters.js"></script>
<script type="text/javascript" src="../src/Runner.js"></script>
<script type="text/javascript" src="../src/Spec.js"></script>

View File

@ -4,8 +4,6 @@
<head>
<title>Jasmine Test Runner</title>
</head>
<script type="text/javascript" src="lib/json2.js"></script>
<script type="text/javascript" src="../src/base.js"></script>
<script type="text/javascript" src="../src/util.js"></script>
<script type="text/javascript" src="../src/Env.js"></script>
@ -15,7 +13,9 @@
<script type="text/javascript" src="../src/MultiReporter.js"></script>
<script type="text/javascript" src="../src/NestedResults.js"></script>
<script type="text/javascript" src="../src/PrettyPrinter.js"></script>
<script type="text/javascript" src="../src/QueuedFunction.js"></script>
<script type="text/javascript" src="../src/Block.js"></script>
<script type="text/javascript" src="../src/WaitsBlock.js"></script>
<script type="text/javascript" src="../src/WaitsForBlock.js"></script>
<script type="text/javascript" src="../src/Reporters.js"></script>
<script type="text/javascript" src="../src/Runner.js"></script>
<script type="text/javascript" src="../src/Spec.js"></script>

View File

@ -77,7 +77,7 @@ describe("jasmine spec running", function () {
it("should work without a runs block", function() {
var another_spec;
var currentSuite = env.describe('default current suite', function() {
env.describe('default current suite', function() {
another_spec = env.it('spec with an expectation', function () {
var foo = 'bar';
this.expect(foo).toEqual('bar');
@ -94,12 +94,33 @@ describe("jasmine spec running", function () {
expect(another_spec.results.description).toEqual('spec with an expectation'); // "In a spec without a run block, results did not include the spec's description";
});
it('should queue waits and runs that it encounters while executing specs', function() {
var specWithRunsAndWaits;
env.describe('test async spec', function() {
specWithRunsAndWaits = env.it('spec w/ queued statments', function () {
this.runs(function () {
});
this.waits(500);
this.runs(function () {
});
this.waits(500);
this.runs(function () {
});
});
});
expect(specWithRunsAndWaits.queue.length).toEqual(1);
specWithRunsAndWaits.execute();
expect(specWithRunsAndWaits.queue.length).toEqual(6);
});
it("should run asynchronous tests", function () {
var foo = 0;
//set a bogus suite for the spec to attach to
// jasmine.getEnv().currentSuite = {specs: []};
var a_spec;
env.describe('test async spec', function() {
a_spec = env.it('simple queue test', function () {
@ -113,11 +134,11 @@ describe("jasmine spec running", function () {
});
expect(a_spec.queue.length).toEqual(1,
'Expected spec queue length to be 1, was ' + a_spec.queue.length);
'Expected spec queue length to be 1, was ' + a_spec.queue.length);
a_spec.execute();
expect(a_spec.queue.length).toEqual(3,
'Expected spec queue length to be 3, was ' + a_spec.queue.length);
'Expected spec queue length to be 3, was ' + a_spec.queue.length);
foo = 0;
env.describe('test async spec', function() {
@ -185,16 +206,16 @@ describe("jasmine spec running", function () {
});
});
expect(another_spec.queue.length).toEqual(1); // 'Calling 2 waits(): Expected queue length to be 1, got ' + another_spec.queue.length;
expect(another_spec.queue.length).toEqual(1);
another_spec.execute();
fakeTimer.tick(1000);
expect(another_spec.queue.length).toEqual(4); // 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length;
expect(another_spec.queue.length).toEqual(6);
expect(another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions';
expect(another_spec.results.getItems().length).toEqual(1);
expect(another_spec.results.getItems()[0].passed).toEqual(true); // 'Calling 2 waits(): Queued expectation failed';
expect(another_spec.results.getItems()[0].passed).toEqual(true);
var baz = 0;
var yet_another_spec;
@ -216,9 +237,9 @@ describe("jasmine spec running", function () {
yet_another_spec.execute();
fakeTimer.tick(250);
expect(yet_another_spec.queue.length).toEqual(3); // 'Calling 2 waits(): Expected queue length to be 3, got ' + another_spec.queue.length);
expect(yet_another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions');
expect(yet_another_spec.results.getItems()[0].passed).toEqual(false); // 'Calling 2 waits(): Queued expectation failed');
expect(yet_another_spec.queue.length).toEqual(4);
expect(yet_another_spec.results.getItems().length).toEqual(1);
expect(yet_another_spec.results.getItems()[0].passed).toEqual(false);
});
it("testAsyncSpecsWithMockSuite", function () {
@ -246,9 +267,9 @@ describe("jasmine spec running", function () {
another_spec.execute();
fakeTimer.tick(2000);
expect(another_spec.queue.length).toEqual(4); // 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length);
expect(another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions');
expect(another_spec.results.getItems()[0].passed).toEqual(true); // 'Calling 2 waits(): Queued expectation failed');
expect(another_spec.queue.length).toEqual(6);
expect(another_spec.results.getItems().length).toEqual(1);
expect(another_spec.results.getItems()[0].passed).toEqual(true);
});
it("testWaitsFor", function() {
@ -302,7 +323,7 @@ describe("jasmine spec running", function () {
expect(actual).toEqual(expected);
});
it("testWaitsForFailsIfTimeout", function() {
it("waitsFor fails and skips the rest of the spec if timeout is reached and the latch function is still false", function() {
var runsBlockExecuted = false;
var spec;
@ -312,7 +333,7 @@ describe("jasmine spec running", function () {
});
this.waitsFor(500, function() {
return false; // force a timeout
return false;
});
this.runs(function() {
@ -322,11 +343,11 @@ describe("jasmine spec running", function () {
});
spec.execute();
expect(runsBlockExecuted).toEqual(false, 'should not have executed runs block yet');
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(100);
expect(runsBlockExecuted).toEqual(false, 'should not have executed runs block yet');
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(400);
expect(runsBlockExecuted).toEqual(false, 'should have timed out, so the second runs block should not have been called');
expect(runsBlockExecuted).toEqual(false);
var actual = spec.results.getItems()[0].message;
var expected = 'timeout: timed out after 500 msec waiting for something to happen';
expect(actual).toEqual(expected,
@ -356,11 +377,12 @@ describe("jasmine spec running", function () {
describe('test suite declaration', function() {
var suite;
var dummyFunction = function() {};
var dummyFunction = function() {
};
it('should give the suite a description', function() {
suite = env.describe('one suite description', dummyFunction);
expect(suite.description).toEqual('one suite description'); // 'Suite did not get a description');
expect(suite.description).toEqual('one suite description');
});
it('should add tests to suites declared by the passed function', function() {
@ -368,8 +390,8 @@ describe("jasmine spec running", function () {
env.it('should be a test');
});
expect(suite.specs.length).toEqual(1); // 'Suite did not get a spec pushed');
expect(suite.specs[0].queue.length).toEqual(0); // "Suite's Spec should not have queuedFunctions");
expect(suite.specs.length).toEqual(1);
expect(suite.specs[0].queue.length).toEqual(0);
});
it('should enqueue functions for multipart tests', function() {
@ -382,7 +404,7 @@ describe("jasmine spec running", function () {
});
});
expect(suite.specs[0].queue.length).toEqual(1); // "Suite's spec did not get a function pushed");
expect(suite.specs[0].queue.length).toEqual(1);
});
it('should enqueue functions for multipart tests and support waits, and run any ready runs() blocks', function() {
@ -401,9 +423,9 @@ describe("jasmine spec running", function () {
});
});
expect(suite.specs[0].queue.length).toEqual(1); // "Suite's spec length should have been 1, was " + suite.specs[0].queue.length);
expect(suite.specs[0].queue.length).toEqual(1);
suite.execute();
expect(suite.specs[0].queue.length).toEqual(3); // "Suite's spec length should have been 3, was " + suite.specs[0].queue.length);
expect(suite.specs[0].queue.length).toEqual(4);
expect(foo).toEqual(1);
expect(bar).toEqual(0);
@ -466,7 +488,7 @@ describe("jasmine spec running", function () {
});
suiteWithAfter.execute();
var suite = suiteWithAfter;
suite = suiteWithAfter;
expect(suite.afterEachFunction); // "testBeforeAndAfterCallbacks: Suite's afterEach was not defined");
expect(suite.specs[0].results.getItems()[0].passed).toEqual(true); // "testBeforeAndAfterCallbacks: afterEach failure: " + suite.results.getItems()[0].results[0].message);
expect(suite.specs[0].foo).toEqual(0); // "testBeforeAndAfterCallbacks: afterEach failure: foo was not reset to 0");
@ -475,6 +497,131 @@ describe("jasmine spec running", function () {
});
it('#waits should allow consecutive waits calls', function () {
var foo = 0;
var waitsSuite = env.describe('suite that waits', function () {
env.it('should wait', function() {
this.waits(500);
this.waits(500);
this.runs(function () {
foo++;
});
});
});
waitsSuite.execute();
expect(foo).toEqual(0);
fakeTimer.tick(500);
expect(foo).toEqual(0);
fakeTimer.tick(500);
expect(foo).toEqual(1);
});
describe('#waitsFor should allow consecutive calls', function () {
var foo;
beforeEach(function () {
foo = 0;
});
it('exits immediately (does not stack) if the latchFunction times out', function () {
var reachedFirstWaitsFor = false;
var reachedSecondWaitsFor = false;
var waitsSuite = env.describe('suite that waits', function () {
env.it('should stack timeouts', function() {
this.waitsFor(500, function () { reachedFirstWaitsFor = true; return false; });
this.waitsFor(500, function () { reachedSecondWaitsFor = true;});
this.runs(function () {
foo++;
});
});
});
expect(reachedFirstWaitsFor).toEqual(false);
waitsSuite.execute();
expect(reachedFirstWaitsFor).toEqual(true);
expect(foo).toEqual(0);
expect(reachedSecondWaitsFor).toEqual(false);
fakeTimer.tick(500);
expect(reachedSecondWaitsFor).toEqual(false);
expect(foo).toEqual(0);
fakeTimer.tick(500);
expect(reachedSecondWaitsFor).toEqual(false);
expect(foo).toEqual(0);
});
it('stacks latchFunctions', function () {
var firstWaitsResult = false;
var secondWaitsResult = false;
var waitsSuite = env.describe('suite that waits', function () {
env.it('spec with waitsFors', function() {
this.waitsFor(600, function () { fakeTimer.setTimeout(function () {firstWaitsResult = true; }, 300); return firstWaitsResult; });
this.waitsFor(600, function () { fakeTimer.setTimeout(function () {secondWaitsResult = true; }, 300); return secondWaitsResult; });
this.runs(function () {
foo++;
});
});
});
expect(firstWaitsResult).toEqual(false);
expect(secondWaitsResult).toEqual(false);
waitsSuite.execute();
expect(firstWaitsResult).toEqual(false);
expect(secondWaitsResult).toEqual(false);
expect(foo).toEqual(0);
fakeTimer.tick(300);
expect(firstWaitsResult).toEqual(true);
expect(secondWaitsResult).toEqual(false);
expect(foo).toEqual(0);
fakeTimer.tick(300);
expect(firstWaitsResult).toEqual(true);
expect(secondWaitsResult).toEqual(true);
expect(foo).toEqual(1);
});
});
xit("#beforeEach should be able to eval runs and waits blocks", function () {
var foo = 0;
var bar = 0;
var suiteWithBefore = env.describe('one suite with a before', function () {
this.beforeEach(function () {
this.runs(function () {
foo++;
});
this.waits(500);
this.runs(function () {
foo++;
});
this.waits(500);
});
env.it('should be a spec', function () {
bar = 1;
foo++;
});
});
//expect(foo).toEqual(0);
expect(bar).toEqual(0);
suiteWithBefore.execute();
expect(bar).toEqual(0);
//expect(foo).toEqual(1);
fakeTimer.tick(500);
expect(bar).toEqual(0);
//expect(foo).toEqual(2);
fakeTimer.tick(500);
expect(bar).toEqual(1);
//expect(foo).toEqual(3);
});
it("testBeforeExecutesSafely", function() {
var report = "";
var suite = env.describe('before fails on first test, passes on second', function() {
@ -634,7 +781,8 @@ describe("jasmine spec running", function () {
env.describe('Test Subject', function() {
env.describe('when under circumstance A', function() {
env.describe('and circumstance B', function() {
nestedSpec = env.it('behaves thusly', function() {});
nestedSpec = env.it('behaves thusly', function() {
});
});
});
});
@ -740,7 +888,7 @@ describe("jasmine spec running", function () {
});
spec1 = env.it('spec with an expectation').runs(function () {
this.addMatchers( { matcherForSpec: function(expected) {
this.addMatchers({ matcherForSpec: function(expected) {
return "matcherForSpec: actual: " + this.actual + "; expected: " + expected;
} });
spec1Matcher = this.expect("xxx");

31
src/Block.js Normal file
View File

@ -0,0 +1,31 @@
/**
* Blocks are functions with executable code that make up a spec.
*
* @constructor
* @param {jasmine.Env} env
* @param {Function} func
* @param {jasmine.Spec} spec
*/
jasmine.Block = function(env, func, spec) {
this.env = env;
this.func = func;
this.spec = spec;
};
jasmine.Block.prototype.next = function() {
this.spec.finish(); // default value is to be done after one function
};
jasmine.Block.prototype.execute = function() {
this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...');
try {
this.func.apply(this.spec);
} catch (e) {
this.fail(e);
}
this.next();
};
jasmine.Block.prototype.fail = function(e) {
this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null));
};

View File

@ -1,84 +0,0 @@
/**
* QueuedFunction is how ActionCollections' actions are implemented
*
* @constructor
* @param {jasmine.Env} env
* @param {Function} func
* @param {Number} timeout
* @param {Function} latchFunction
* @param {jasmine.Spec} spec
*/
jasmine.QueuedFunction = function(env, func, timeout, latchFunction, spec) {
this.env = env;
this.func = func;
this.timeout = timeout;
this.latchFunction = latchFunction;
this.spec = spec;
this.totalTimeSpentWaitingForLatch = 0;
this.latchTimeoutIncrement = 100;
};
jasmine.QueuedFunction.prototype.next = function() {
this.spec.finish(); // default value is to be done after one function
};
jasmine.QueuedFunction.prototype.safeExecute = function() {
this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...');
try {
this.func.apply(this.spec);
} catch (e) {
this.fail(e);
}
};
jasmine.QueuedFunction.prototype.execute = function() {
var self = this;
var executeNow = function() {
self.safeExecute();
self.next();
};
var executeLater = function() {
self.env.setTimeout(executeNow, self.timeout);
};
var executeNowOrLater = function() {
var latchFunctionResult;
try {
latchFunctionResult = self.latchFunction.apply(self.spec);
} catch (e) {
self.fail(e);
self.next();
return;
}
if (latchFunctionResult) {
executeNow();
} else if (self.totalTimeSpentWaitingForLatch >= self.timeout) {
var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.latchFunction.description || 'something to happen');
self.fail({
name: 'timeout',
message: message
});
self.next();
} else {
self.totalTimeSpentWaitingForLatch += self.latchTimeoutIncrement;
self.env.setTimeout(executeNowOrLater, self.latchTimeoutIncrement);
}
};
if (this.latchFunction !== undefined) {
executeNowOrLater();
} else if (this.timeout > 0) {
executeLater();
} else {
executeNow();
}
};
jasmine.QueuedFunction.prototype.fail = function(e) {
this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null));
};

View File

@ -12,8 +12,6 @@ jasmine.Spec = function(env, suite, description) {
this.suite = suite;
this.description = description;
this.queue = [];
this.currentTimeout = 0;
this.currentLatchFunction = undefined;
this.finished = false;
this.afterCallbacks = [];
this.spies_ = [];
@ -33,17 +31,12 @@ jasmine.Spec.prototype.getResults = function() {
};
jasmine.Spec.prototype.addToQueue = function(func) {
var queuedFunction = new jasmine.QueuedFunction(this.env, func, this.currentTimeout, this.currentLatchFunction, this);
this.queue.push(queuedFunction);
var block = new jasmine.Block(this.env, func, this);
if (this.queue.length > 1) {
var previousQueuedFunction = this.queue[this.queue.length - 2];
previousQueuedFunction.next = function() {
queuedFunction.execute();
};
}
this.setNextOnLastInQueue(block);
this.queue.push(block);
this.resetTimeout();
return this;
};
@ -56,31 +49,48 @@ jasmine.Spec.prototype.expects_that = function(actual) {
};
/**
* @private
* @private
*/
jasmine.Spec.prototype.expect = function(actual) {
return new (this.getMatchersClass_())(this.env, actual, this.results);
};
/**
* @private
*/
jasmine.Spec.prototype.setNextOnLastInQueue = function (block) {
if (this.queue.length > 0) {
var previousBlock = this.queue[this.queue.length - 1];
previousBlock.next = function() {
block.execute();
};
}
};
/**
* @private
*/
jasmine.Spec.prototype.waits = function(timeout) {
this.currentTimeout = timeout;
this.currentLatchFunction = undefined;
var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
this.setNextOnLastInQueue(waitsFunc);
this.queue.push(waitsFunc);
return this;
};
/**
* @private
*/
jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, message) {
this.currentTimeout = timeout;
this.currentLatchFunction = latchFunction;
this.currentLatchFunction.description = message;
jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) {
var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this);
this.setNextOnLastInQueue(waitsForFunc);
this.queue.push(waitsForFunc);
return this;
};
jasmine.Spec.prototype.failWithException = function (e) {
this.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null));
};
jasmine.Spec.prototype.getMatchersClass_ = function() {
return this.matchersClass || jasmine.Matchers;
};
@ -97,11 +107,6 @@ jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
this.matchersClass = newMatchersClass;
};
jasmine.Spec.prototype.resetTimeout = function() {
this.currentTimeout = 0;
this.currentLatchFunction = undefined;
};
jasmine.Spec.prototype.finishCallback = function() {
this.env.reporter.reportSpecResults(this);
};

14
src/WaitsBlock.js Normal file
View File

@ -0,0 +1,14 @@
jasmine.WaitsBlock = function(env, timeout, spec) {
this.timeout = timeout;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
jasmine.WaitsBlock.prototype.execute = function () {
var self = this;
self.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
self.env.setTimeout(function () {
self.next();
}, self.timeout);
};

38
src/WaitsForBlock.js Normal file
View File

@ -0,0 +1,38 @@
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
this.timeout = timeout;
this.latchFunction = latchFunction;
this.message = message;
this.totalTimeSpentWaitingForLatch = 0;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 100;
jasmine.WaitsForBlock.prototype.execute = function () {
var self = this;
self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen'));
var latchFunctionResult;
try {
latchFunctionResult = self.latchFunction.apply(self.spec);
} catch (e) {
self.fail(e);
self.next();
return;
}
if (latchFunctionResult) {
self.next();
} else if (self.totalTimeSpentWaitingForLatch >= self.timeout) {
var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen');
self.fail({
name: 'timeout',
message: message
});
self.spec.next();
} else {
self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
self.env.setTimeout(function () { self.execute(); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
}
};