/src/test/js/TestRunner.js

https://github.com/nonano/yui3 · JavaScript · 1009 lines · 444 code · 152 blank · 413 comment · 110 complexity · 12ee416da4483e26744ab14af31b1be2 MD5 · raw file

  1. /*
  2. * Runs test suites and test cases, providing events to allowing for the
  3. * interpretation of test results.
  4. * @namespace Test
  5. * @class Runner
  6. * @static
  7. */
  8. Y.Test.Runner = (function(){
  9. /* (intentionally not documented)
  10. * A node in the test tree structure. May represent a TestSuite, TestCase, or
  11. * test function.
  12. * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
  13. * @class TestNode
  14. * @constructor
  15. * @private
  16. */
  17. function TestNode(testObject){
  18. /* (intentionally not documented)
  19. * The TestSuite, TestCase, or test function represented by this node.
  20. * @type Variant
  21. * @property testObject
  22. */
  23. this.testObject = testObject;
  24. /* (intentionally not documented)
  25. * Pointer to this node's first child.
  26. * @type TestNode
  27. * @property firstChild
  28. */
  29. this.firstChild = null;
  30. /* (intentionally not documented)
  31. * Pointer to this node's last child.
  32. * @type TestNode
  33. * @property lastChild
  34. */
  35. this.lastChild = null;
  36. /* (intentionally not documented)
  37. * Pointer to this node's parent.
  38. * @type TestNode
  39. * @property parent
  40. */
  41. this.parent = null;
  42. /* (intentionally not documented)
  43. * Pointer to this node's next sibling.
  44. * @type TestNode
  45. * @property next
  46. */
  47. this.next = null;
  48. /* (intentionally not documented)
  49. * Test results for this test object.
  50. * @type object
  51. * @property results
  52. */
  53. this.results = {
  54. passed : 0,
  55. failed : 0,
  56. total : 0,
  57. ignored : 0,
  58. duration: 0
  59. };
  60. //initialize results
  61. if (testObject instanceof Y.Test.Suite){
  62. this.results.type = "testsuite";
  63. this.results.name = testObject.name;
  64. } else if (testObject instanceof Y.Test.Case){
  65. this.results.type = "testcase";
  66. this.results.name = testObject.name;
  67. }
  68. }
  69. TestNode.prototype = {
  70. /* (intentionally not documented)
  71. * Appends a new test object (TestSuite, TestCase, or test function name) as a child
  72. * of this node.
  73. * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
  74. * @return {Void}
  75. */
  76. appendChild : function (testObject){
  77. var node = new TestNode(testObject);
  78. if (this.firstChild === null){
  79. this.firstChild = this.lastChild = node;
  80. } else {
  81. this.lastChild.next = node;
  82. this.lastChild = node;
  83. }
  84. node.parent = this;
  85. return node;
  86. }
  87. };
  88. /**
  89. * Runs test suites and test cases, providing events to allowing for the
  90. * interpretation of test results.
  91. * @namespace Test
  92. * @class Runner
  93. * @static
  94. */
  95. function TestRunner(){
  96. //inherit from EventProvider
  97. TestRunner.superclass.constructor.apply(this,arguments);
  98. /**
  99. * Suite on which to attach all TestSuites and TestCases to be run.
  100. * @type Y.Test.Suite
  101. * @property masterSuite
  102. * @static
  103. * @private
  104. */
  105. this.masterSuite /*:Y.Test.Suite*/ = new Y.Test.Suite("yuitests" + (new Date()).getTime());
  106. /**
  107. * Pointer to the current node in the test tree.
  108. * @type TestNode
  109. * @private
  110. * @property _cur
  111. * @static
  112. */
  113. this._cur = null;
  114. /**
  115. * Pointer to the root node in the test tree.
  116. * @type TestNode
  117. * @private
  118. * @property _root
  119. * @static
  120. */
  121. this._root = null;
  122. /**
  123. * Indicates if the TestRunner will log events or not.
  124. * @type Boolean
  125. * @property _log
  126. * @private
  127. * @static
  128. */
  129. this._log = true;
  130. /**
  131. * Indicates if the TestRunner is waiting as a result of
  132. * wait() being called.
  133. * @type Boolean
  134. * @property _waiting
  135. * @private
  136. * @static
  137. */
  138. this._waiting = false;
  139. /**
  140. * Indicates if the TestRunner is currently running tests.
  141. * @type Boolean
  142. * @private
  143. * @property _running
  144. * @static
  145. */
  146. this._running = false;
  147. /**
  148. * Holds copy of the results object generated when all tests are
  149. * complete.
  150. * @type Object
  151. * @private
  152. * @property _lastResults
  153. * @static
  154. */
  155. this._lastResults = null;
  156. //create events
  157. var events = [
  158. this.TEST_CASE_BEGIN_EVENT,
  159. this.TEST_CASE_COMPLETE_EVENT,
  160. this.TEST_SUITE_BEGIN_EVENT,
  161. this.TEST_SUITE_COMPLETE_EVENT,
  162. this.TEST_PASS_EVENT,
  163. this.TEST_FAIL_EVENT,
  164. this.TEST_IGNORE_EVENT,
  165. this.COMPLETE_EVENT,
  166. this.BEGIN_EVENT
  167. ];
  168. for (var i=0; i < events.length; i++){
  169. this.on(events[i], this._logEvent, this, true);
  170. }
  171. }
  172. Y.extend(TestRunner, Y.Event.Target, {
  173. //-------------------------------------------------------------------------
  174. // Constants
  175. //-------------------------------------------------------------------------
  176. /**
  177. * Fires when a test case is opened but before the first
  178. * test is executed.
  179. * @event testcasebegin
  180. * @static
  181. */
  182. TEST_CASE_BEGIN_EVENT : "testcasebegin",
  183. /**
  184. * Fires when all tests in a test case have been executed.
  185. * @event testcasecomplete
  186. * @static
  187. */
  188. TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
  189. /**
  190. * Fires when a test suite is opened but before the first
  191. * test is executed.
  192. * @event testsuitebegin
  193. * @static
  194. */
  195. TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
  196. /**
  197. * Fires when all test cases in a test suite have been
  198. * completed.
  199. * @event testsuitecomplete
  200. * @static
  201. */
  202. TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
  203. /**
  204. * Fires when a test has passed.
  205. * @event pass
  206. * @static
  207. */
  208. TEST_PASS_EVENT : "pass",
  209. /**
  210. * Fires when a test has failed.
  211. * @event fail
  212. * @static
  213. */
  214. TEST_FAIL_EVENT : "fail",
  215. /**
  216. * Fires when a test has been ignored.
  217. * @event ignore
  218. * @static
  219. */
  220. TEST_IGNORE_EVENT : "ignore",
  221. /**
  222. * Fires when all test suites and test cases have been completed.
  223. * @event complete
  224. * @static
  225. */
  226. COMPLETE_EVENT : "complete",
  227. /**
  228. * Fires when the run() method is called.
  229. * @event begin
  230. * @static
  231. */
  232. BEGIN_EVENT : "begin",
  233. //-------------------------------------------------------------------------
  234. // Logging-Related Methods
  235. //-------------------------------------------------------------------------
  236. /**
  237. * Disable logging via Y.log(). Test output will not be visible unless
  238. * TestRunner events are subscribed to.
  239. * @return {Void}
  240. * @method disableLogging
  241. * @static
  242. */
  243. disableLogging: function(){
  244. this._log = false;
  245. },
  246. /**
  247. * Enable logging via Y.log(). Test output is published and can be read via
  248. * logreader.
  249. * @return {Void}
  250. * @method enableLogging
  251. * @static
  252. */
  253. enableLogging: function(){
  254. this._log = true;
  255. },
  256. /**
  257. * Logs TestRunner events using Y.log().
  258. * @param {Object} event The event object for the event.
  259. * @return {Void}
  260. * @method _logEvent
  261. * @private
  262. * @static
  263. */
  264. _logEvent: function(event){
  265. //data variables
  266. var message = "";
  267. var messageType = "";
  268. switch(event.type){
  269. case this.BEGIN_EVENT:
  270. message = "Testing began at " + (new Date()).toString() + ".";
  271. messageType = "info";
  272. break;
  273. case this.COMPLETE_EVENT:
  274. message = Y.substitute("Testing completed at " +
  275. (new Date()).toString() + ".\n" +
  276. "Passed:{passed} Failed:{failed} " +
  277. "Total:{total} ({ignored} ignored)",
  278. event.results);
  279. messageType = "info";
  280. break;
  281. case this.TEST_FAIL_EVENT:
  282. message = event.testName + ": failed.\n" + event.error.getMessage();
  283. messageType = "fail";
  284. break;
  285. case this.TEST_IGNORE_EVENT:
  286. message = event.testName + ": ignored.";
  287. messageType = "ignore";
  288. break;
  289. case this.TEST_PASS_EVENT:
  290. message = event.testName + ": passed.";
  291. messageType = "pass";
  292. break;
  293. case this.TEST_SUITE_BEGIN_EVENT:
  294. message = "Test suite \"" + event.testSuite.name + "\" started.";
  295. messageType = "info";
  296. break;
  297. case this.TEST_SUITE_COMPLETE_EVENT:
  298. message = Y.substitute("Test suite \"" +
  299. event.testSuite.name + "\" completed" + ".\n" +
  300. "Passed:{passed} Failed:{failed} " +
  301. "Total:{total} ({ignored} ignored)",
  302. event.results);
  303. messageType = "info";
  304. break;
  305. case this.TEST_CASE_BEGIN_EVENT:
  306. message = "Test case \"" + event.testCase.name + "\" started.";
  307. messageType = "info";
  308. break;
  309. case this.TEST_CASE_COMPLETE_EVENT:
  310. message = Y.substitute("Test case \"" +
  311. event.testCase.name + "\" completed.\n" +
  312. "Passed:{passed} Failed:{failed} " +
  313. "Total:{total} ({ignored} ignored)",
  314. event.results);
  315. messageType = "info";
  316. break;
  317. default:
  318. message = "Unexpected event " + event.type;
  319. message = "info";
  320. }
  321. //only log if required
  322. if (this._log){
  323. Y.log(message, messageType, "TestRunner");
  324. }
  325. },
  326. //-------------------------------------------------------------------------
  327. // Test Tree-Related Methods
  328. //-------------------------------------------------------------------------
  329. /**
  330. * Adds a test case to the test tree as a child of the specified node.
  331. * @param {TestNode} parentNode The node to add the test case to as a child.
  332. * @param {Test.Case} testCase The test case to add.
  333. * @return {Void}
  334. * @static
  335. * @private
  336. * @method _addTestCaseToTestTree
  337. */
  338. _addTestCaseToTestTree : function (parentNode, testCase /*:Y.Test.Case*/){
  339. //add the test suite
  340. var node = parentNode.appendChild(testCase),
  341. prop,
  342. testName;
  343. //iterate over the items in the test case
  344. for (prop in testCase){
  345. if ((prop.indexOf("test") === 0 || (prop.toLowerCase().indexOf("should") > -1 && prop.indexOf(" ") > -1 ))&& Y.Lang.isFunction(testCase[prop])){
  346. node.appendChild(prop);
  347. }
  348. }
  349. },
  350. /**
  351. * Adds a test suite to the test tree as a child of the specified node.
  352. * @param {TestNode} parentNode The node to add the test suite to as a child.
  353. * @param {Test.Suite} testSuite The test suite to add.
  354. * @return {Void}
  355. * @static
  356. * @private
  357. * @method _addTestSuiteToTestTree
  358. */
  359. _addTestSuiteToTestTree : function (parentNode, testSuite /*:Y.Test.Suite*/) {
  360. //add the test suite
  361. var node = parentNode.appendChild(testSuite);
  362. //iterate over the items in the master suite
  363. for (var i=0; i < testSuite.items.length; i++){
  364. if (testSuite.items[i] instanceof Y.Test.Suite) {
  365. this._addTestSuiteToTestTree(node, testSuite.items[i]);
  366. } else if (testSuite.items[i] instanceof Y.Test.Case) {
  367. this._addTestCaseToTestTree(node, testSuite.items[i]);
  368. }
  369. }
  370. },
  371. /**
  372. * Builds the test tree based on items in the master suite. The tree is a hierarchical
  373. * representation of the test suites, test cases, and test functions. The resulting tree
  374. * is stored in _root and the pointer _cur is set to the root initially.
  375. * @return {Void}
  376. * @static
  377. * @private
  378. * @method _buildTestTree
  379. */
  380. _buildTestTree : function () {
  381. this._root = new TestNode(this.masterSuite);
  382. //this._cur = this._root;
  383. //iterate over the items in the master suite
  384. for (var i=0; i < this.masterSuite.items.length; i++){
  385. if (this.masterSuite.items[i] instanceof Y.Test.Suite) {
  386. this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
  387. } else if (this.masterSuite.items[i] instanceof Y.Test.Case) {
  388. this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
  389. }
  390. }
  391. },
  392. //-------------------------------------------------------------------------
  393. // Private Methods
  394. //-------------------------------------------------------------------------
  395. /**
  396. * Handles the completion of a test object's tests. Tallies test results
  397. * from one level up to the next.
  398. * @param {TestNode} node The TestNode representing the test object.
  399. * @return {Void}
  400. * @method _handleTestObjectComplete
  401. * @private
  402. */
  403. _handleTestObjectComplete : function (node) {
  404. if (Y.Lang.isObject(node.testObject)){
  405. if (node.parent){
  406. node.parent.results.passed += node.results.passed;
  407. node.parent.results.failed += node.results.failed;
  408. node.parent.results.total += node.results.total;
  409. node.parent.results.ignored += node.results.ignored;
  410. //node.parent.results.duration += node.results.duration;
  411. node.parent.results[node.testObject.name] = node.results;
  412. }
  413. if (node.testObject instanceof Y.Test.Suite){
  414. node.testObject.tearDown();
  415. node.results.duration = (new Date()) - node._start;
  416. this.fire(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
  417. } else if (node.testObject instanceof Y.Test.Case){
  418. node.results.duration = (new Date()) - node._start;
  419. this.fire(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
  420. }
  421. }
  422. },
  423. //-------------------------------------------------------------------------
  424. // Navigation Methods
  425. //-------------------------------------------------------------------------
  426. /**
  427. * Retrieves the next node in the test tree.
  428. * @return {TestNode} The next node in the test tree or null if the end is reached.
  429. * @private
  430. * @static
  431. * @method _next
  432. */
  433. _next : function () {
  434. if (this._cur === null){
  435. this._cur = this._root;
  436. } else if (this._cur.firstChild) {
  437. this._cur = this._cur.firstChild;
  438. } else if (this._cur.next) {
  439. this._cur = this._cur.next;
  440. } else {
  441. while (this._cur && !this._cur.next && this._cur !== this._root){
  442. this._handleTestObjectComplete(this._cur);
  443. this._cur = this._cur.parent;
  444. }
  445. this._handleTestObjectComplete(this._cur);
  446. if (this._cur == this._root){
  447. this._cur.results.type = "report";
  448. this._cur.results.timestamp = (new Date()).toLocaleString();
  449. this._cur.results.duration = (new Date()) - this._cur._start;
  450. this._lastResults = this._cur.results;
  451. this._running = false;
  452. this.fire(this.COMPLETE_EVENT, { results: this._lastResults});
  453. this._cur = null;
  454. } else {
  455. this._cur = this._cur.next;
  456. }
  457. }
  458. return this._cur;
  459. },
  460. /**
  461. * Runs a test case or test suite, returning the results.
  462. * @param {Test.Case|Test.Suite} testObject The test case or test suite to run.
  463. * @return {Object} Results of the execution with properties passed, failed, and total.
  464. * @private
  465. * @method _run
  466. * @static
  467. */
  468. _run : function () {
  469. //flag to indicate if the TestRunner should wait before continuing
  470. var shouldWait = false;
  471. //get the next test node
  472. var node = this._next();
  473. if (node !== null) {
  474. //set flag to say the testrunner is running
  475. this._running = true;
  476. //eliminate last results
  477. this._lastResult = null;
  478. var testObject = node.testObject;
  479. //figure out what to do
  480. if (Y.Lang.isObject(testObject)){
  481. if (testObject instanceof Y.Test.Suite){
  482. this.fire(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
  483. node._start = new Date();
  484. testObject.setUp();
  485. } else if (testObject instanceof Y.Test.Case){
  486. this.fire(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
  487. node._start = new Date();
  488. }
  489. //some environments don't support setTimeout
  490. if (typeof setTimeout != "undefined"){
  491. setTimeout(function(){
  492. Y.Test.Runner._run();
  493. }, 0);
  494. } else {
  495. this._run();
  496. }
  497. } else {
  498. this._runTest(node);
  499. }
  500. }
  501. },
  502. _resumeTest : function (segment) {
  503. //get relevant information
  504. var node = this._cur;
  505. //we know there's no more waiting now
  506. this._waiting = false;
  507. //if there's no node, it probably means a wait() was called after resume()
  508. if (!node){
  509. //TODO: Handle in some way?
  510. //console.log("wait() called after resume()");
  511. //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
  512. return;
  513. }
  514. var testName = node.testObject;
  515. var testCase /*:Y.Test.Case*/ = node.parent.testObject;
  516. //cancel other waits if available
  517. if (testCase.__yui_wait){
  518. clearTimeout(testCase.__yui_wait);
  519. delete testCase.__yui_wait;
  520. }
  521. //get the "should" test cases
  522. var shouldFail = (testCase._should.fail || {})[testName];
  523. var shouldError = (testCase._should.error || {})[testName];
  524. //variable to hold whether or not the test failed
  525. var failed = false;
  526. var error = null;
  527. //try the test
  528. try {
  529. //run the test
  530. segment.apply(testCase);
  531. //if it should fail, and it got here, then it's a fail because it didn't
  532. if (shouldFail){
  533. error = new Y.Assert.ShouldFail();
  534. failed = true;
  535. } else if (shouldError){
  536. error = new Y.Assert.ShouldError();
  537. failed = true;
  538. }
  539. } catch (thrown){
  540. //cancel any pending waits, the test already failed
  541. if (testCase.__yui_wait){
  542. clearTimeout(testCase.__yui_wait);
  543. delete testCase.__yui_wait;
  544. }
  545. //figure out what type of error it was
  546. if (thrown instanceof Y.Assert.Error) {
  547. if (!shouldFail){
  548. error = thrown;
  549. failed = true;
  550. }
  551. } else if (thrown instanceof Y.Test.Wait){
  552. if (Y.Lang.isFunction(thrown.segment)){
  553. if (Y.Lang.isNumber(thrown.delay)){
  554. //some environments don't support setTimeout
  555. if (typeof setTimeout != "undefined"){
  556. testCase.__yui_wait = setTimeout(function(){
  557. Y.Test.Runner._resumeTest(thrown.segment);
  558. }, thrown.delay);
  559. this._waiting = true;
  560. } else {
  561. throw new Error("Asynchronous tests not supported in this environment.");
  562. }
  563. }
  564. }
  565. return;
  566. } else {
  567. //first check to see if it should error
  568. if (!shouldError) {
  569. error = new Y.Assert.UnexpectedError(thrown);
  570. failed = true;
  571. } else {
  572. //check to see what type of data we have
  573. if (Y.Lang.isString(shouldError)){
  574. //if it's a string, check the error message
  575. if (thrown.message != shouldError){
  576. error = new Y.Assert.UnexpectedError(thrown);
  577. failed = true;
  578. }
  579. } else if (Y.Lang.isFunction(shouldError)){
  580. //if it's a function, see if the error is an instance of it
  581. if (!(thrown instanceof shouldError)){
  582. error = new Y.Assert.UnexpectedError(thrown);
  583. failed = true;
  584. }
  585. } else if (Y.Lang.isObject(shouldError)){
  586. //if it's an object, check the instance and message
  587. if (!(thrown instanceof shouldError.constructor) ||
  588. thrown.message != shouldError.message){
  589. error = new Y.Assert.UnexpectedError(thrown);
  590. failed = true;
  591. }
  592. }
  593. }
  594. }
  595. }
  596. //fire appropriate event
  597. if (failed) {
  598. this.fire(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
  599. } else {
  600. this.fire(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
  601. }
  602. //run the tear down
  603. testCase.tearDown();
  604. //calculate duration
  605. var duration = (new Date()) - node._start;
  606. //update results
  607. node.parent.results[testName] = {
  608. result: failed ? "fail" : "pass",
  609. message: error ? error.getMessage() : "Test passed",
  610. type: "test",
  611. name: testName,
  612. duration: duration
  613. };
  614. if (failed){
  615. node.parent.results.failed++;
  616. } else {
  617. node.parent.results.passed++;
  618. }
  619. node.parent.results.total++;
  620. //set timeout not supported in all environments
  621. if (typeof setTimeout != "undefined"){
  622. setTimeout(function(){
  623. Y.Test.Runner._run();
  624. }, 0);
  625. } else {
  626. this._run();
  627. }
  628. },
  629. /**
  630. * Handles an error as if it occurred within the currently executing
  631. * test. This is for mock methods that may be called asynchronously
  632. * and therefore out of the scope of the TestRunner. Previously, this
  633. * error would bubble up to the browser. Now, this method is used
  634. * to tell TestRunner about the error. This should never be called
  635. * by anyplace other than the Mock object.
  636. * @param {Error} error The error object.
  637. * @return {Void}
  638. * @method _handleError
  639. * @private
  640. * @static
  641. */
  642. _handleError: function(error){
  643. if (this._waiting){
  644. this._resumeTest(function(){
  645. throw error;
  646. });
  647. } else {
  648. throw error;
  649. }
  650. },
  651. /**
  652. * Runs a single test based on the data provided in the node.
  653. * @param {TestNode} node The TestNode representing the test to run.
  654. * @return {Void}
  655. * @static
  656. * @private
  657. * @method _runTest
  658. */
  659. _runTest : function (node) {
  660. //get relevant information
  661. var testName = node.testObject;
  662. var testCase /*:Y.Test.Case*/ = node.parent.testObject;
  663. var test = testCase[testName];
  664. //get the "should" test cases
  665. var shouldIgnore = (testCase._should.ignore || {})[testName];
  666. //figure out if the test should be ignored or not
  667. if (shouldIgnore){
  668. //update results
  669. node.parent.results[testName] = {
  670. result: "ignore",
  671. message: "Test ignored",
  672. type: "test",
  673. name: testName
  674. };
  675. node.parent.results.ignored++;
  676. node.parent.results.total++;
  677. this.fire(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
  678. //some environments don't support setTimeout
  679. if (typeof setTimeout != "undefined"){
  680. setTimeout(function(){
  681. Y.Test.Runner._run();
  682. }, 0);
  683. } else {
  684. this._run();
  685. }
  686. } else {
  687. //mark the start time
  688. node._start = new Date();
  689. //run the setup
  690. testCase.setUp();
  691. //now call the body of the test
  692. this._resumeTest(test);
  693. }
  694. },
  695. //-------------------------------------------------------------------------
  696. // Misc Methods
  697. //-------------------------------------------------------------------------
  698. /**
  699. * Retrieves the name of the current result set.
  700. * @return {String} The name of the result set.
  701. * @method getName
  702. */
  703. getName: function(){
  704. return this.masterSuite.name;
  705. },
  706. /**
  707. * The name assigned to the master suite of the TestRunner. This is the name
  708. * that is output as the root's name when results are retrieved.
  709. * @param {String} name The name of the result set.
  710. * @return {Void}
  711. * @method setName
  712. */
  713. setName: function(name){
  714. this.masterSuite.name = name;
  715. },
  716. //-------------------------------------------------------------------------
  717. // Protected Methods
  718. //-------------------------------------------------------------------------
  719. /*
  720. * Fires events for the TestRunner. This overrides the default fire()
  721. * method from EventProvider to add the type property to the data that is
  722. * passed through on each event call.
  723. * @param {String} type The type of event to fire.
  724. * @param {Object} data (Optional) Data for the event.
  725. * @method fire
  726. * @static
  727. * @protected
  728. */
  729. fire : function (type, data) {
  730. data = data || {};
  731. data.type = type;
  732. TestRunner.superclass.fire.call(this, type, data);
  733. },
  734. //-------------------------------------------------------------------------
  735. // Public Methods
  736. //-------------------------------------------------------------------------
  737. /**
  738. * Adds a test suite or test case to the list of test objects to run.
  739. * @param testObject Either a TestCase or a TestSuite that should be run.
  740. * @return {Void}
  741. * @method add
  742. * @static
  743. */
  744. add : function (testObject) {
  745. this.masterSuite.add(testObject);
  746. return this;
  747. },
  748. /**
  749. * Removes all test objects from the runner.
  750. * @return {Void}
  751. * @method clear
  752. * @static
  753. */
  754. clear : function () {
  755. this.masterSuite = new Y.Test.Suite("yuitests" + (new Date()).getTime());
  756. },
  757. /**
  758. * Indicates if the TestRunner is waiting for a test to resume
  759. * @return {Boolean} True if the TestRunner is waiting, false if not.
  760. * @method isWaiting
  761. * @static
  762. */
  763. isWaiting: function() {
  764. return this._waiting;
  765. },
  766. /**
  767. * Indicates that the TestRunner is busy running tests and therefore can't
  768. * be stopped and results cannot be gathered.
  769. * @return {Boolean} True if the TestRunner is running, false if not.
  770. * @method isRunning
  771. */
  772. isRunning: function(){
  773. return this._running;
  774. },
  775. /**
  776. * Returns the last complete results set from the TestRunner. Null is returned
  777. * if the TestRunner is running or no tests have been run.
  778. * @param {Function} format (Optional) A test format to return the results in.
  779. * @return {Object|String} Either the results object or, if a test format is
  780. * passed as the argument, a string representing the results in a specific
  781. * format.
  782. * @method getResults
  783. */
  784. getResults: function(format){
  785. if (!this._running && this._lastResults){
  786. if (Y.Lang.isFunction(format)){
  787. return format(this._lastResults);
  788. } else {
  789. return this._lastResults;
  790. }
  791. } else {
  792. return null;
  793. }
  794. },
  795. /**
  796. * Returns the coverage report for the files that have been executed.
  797. * This returns only coverage information for files that have been
  798. * instrumented using YUI Test Coverage and only those that were run
  799. * in the same pass.
  800. * @param {Function} format (Optional) A coverage format to return results in.
  801. * @return {Object|String} Either the coverage object or, if a coverage
  802. * format is specified, a string representing the results in that format.
  803. * @method getCoverage
  804. */
  805. getCoverage: function(format){
  806. if (!this._running && typeof _yuitest_coverage == "object"){
  807. if (Y.Lang.isFunction(format)){
  808. return format(_yuitest_coverage);
  809. } else {
  810. return _yuitest_coverage;
  811. }
  812. } else {
  813. return null;
  814. }
  815. },
  816. /**
  817. * Resumes the TestRunner after wait() was called.
  818. * @param {Function} segment The function to run as the rest
  819. * of the haulted test.
  820. * @return {Void}
  821. * @method resume
  822. * @static
  823. */
  824. resume : function (segment) {
  825. if (Y.Test.Runner._waiting){
  826. this._resumeTest(segment || function(){});
  827. } else {
  828. throw new Error("resume() called without wait().");
  829. }
  830. },
  831. /**
  832. * Runs the test suite.
  833. * @param {Boolean} oldMode (Optional) Specifies that the <= 2.8 way of
  834. * internally managing test suites should be used.
  835. * @return {Void}
  836. * @method run
  837. * @static
  838. */
  839. run : function (oldMode) {
  840. //pointer to runner to avoid scope issues
  841. var runner = Y.Test.Runner;
  842. //if there's only one suite on the masterSuite, move it up
  843. if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof Y.Test.Suite){
  844. this.masterSuite = this.masterSuite.items[0];
  845. }
  846. //build the test tree
  847. runner._buildTestTree();
  848. //set when the test started
  849. runner._root._start = new Date();
  850. //fire the begin event
  851. runner.fire(runner.BEGIN_EVENT);
  852. //begin the testing
  853. runner._run();
  854. }
  855. });
  856. return new TestRunner();
  857. })();