API Docs for: 3.18.1
Show:

File: test/js/TestRunner.js

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