sweety-template.js 29KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. /*
  2. JavaScript for Sweety to wrap the standard template around the API.
  3. */
  4. /**
  5. * The UI Manager object for setting up the interface.
  6. * @author Chris Corbyn
  7. * @constructor
  8. */
  9. function SweetyUIManager() {
  10. var _this = this;
  11. /** Packages toggled on or off */
  12. var _pkgs = { };
  13. /** Test cases within packages */
  14. var _pkgTests = { };
  15. /** An element cache */
  16. var _cached = { };
  17. /**
  18. * Initialize the user interface.
  19. */
  20. this.initialize = function initialize() {
  21. _this.showFilterBox();
  22. _this.loadTestList();
  23. _this.resetMessageDiv();
  24. _this.resetTotals();
  25. }
  26. /**
  27. * Show or hide an entire package.
  28. * @param {String} pkg
  29. * @param {Boolean} onoff
  30. */
  31. this.togglePackage = function togglePackage(pkg, onOff) {
  32. if (typeof _pkgs[pkg] == "undefined") {
  33. _pkgs[pkg] = true;
  34. }
  35. //Toggle if not overridden
  36. _pkgs[pkg] = (typeof onOff == "undefined") ? !_pkgs[pkg] : onOff;
  37. var pkgState = _pkgs[pkg] ? "1" : "0";
  38. var date = new Date();
  39. date.setTime(date.getTime() + (30 * 24 * 60 * 60 * 1000));
  40. document.cookie = escape("sweetyPkg" + pkg) + "=" + pkgState +
  41. "; expires=" + date.toGMTString() +
  42. "; path=/";
  43. var pkgRegex = new RegExp("^" + pkg + "_[^_]+$");
  44. for (var testCase in sweetyTestCases) {
  45. if (testCase.match(pkgRegex)) {
  46. var testDiv = _getElementById(testCase);
  47. if (_pkgs[pkg]) {
  48. if (sweetyTestCases[testCase]) {
  49. testDiv.style.display = "block";
  50. }
  51. } else {
  52. testDiv.style.display = "none";
  53. }
  54. }
  55. }
  56. var headerImg;
  57. if (headerImg = _getElementById("sweety-pkg-img-" + pkg))
  58. {
  59. if (_pkgs[pkg]) {
  60. headerImg.src = "templates/sweety/images/darr.gif";
  61. } else {
  62. headerImg.src = "templates/sweety/images/rarr.gif";
  63. }
  64. }
  65. }
  66. /**
  67. * Enable or disable user input.
  68. * @param {Boolean} on
  69. */
  70. this.allowInteractivity = function allowInteractivity(on) {
  71. if (!on) {
  72. _paintFilterDisabled(true);
  73. _getRunButton().value = "Stop Tests";
  74. _getRunButton().onclick = function() {
  75. try {
  76. SweetyTestRunner.getCurrentInstance().cancelTesting(true);
  77. _this.allowInteractivity(true);
  78. } catch (e) { }
  79. };
  80. for (var testCase in sweetyTestCases) {
  81. _getElementById(testCase).onclick = function() {
  82. return false;
  83. };
  84. }
  85. } else {
  86. _paintFilterDisabled(false);
  87. _getRunButton().value = "Run Tests";
  88. _getRunButton().onclick = function() {
  89. _this.initialize();
  90. sweetyRunner.runAll();
  91. };
  92. for (var testCase in sweetyTestCases) {
  93. _getElementById(testCase).onclick = function() {
  94. _this.initialize();
  95. sweetyRunner.runTestCase(this.id);
  96. };
  97. }
  98. }
  99. }
  100. /**
  101. * Display the filter box.
  102. */
  103. this.showFilterBox = function showFilterBox() {
  104. _getFilter().style.visibility = 'visible';
  105. }
  106. /**
  107. * Restore the UI on a new page load or reload.
  108. */
  109. this.restore = function restore() {
  110. for (var testName in sweetyTestCases) {
  111. var pkgName = _pkgFor(testName);
  112. if (typeof _pkgTests[pkgName] == "undefined") {
  113. _pkgTests[pkgName] = { };
  114. }
  115. _pkgTests[pkgName][testName] = true;
  116. }
  117. this.hideCheckboxes();
  118. _loadPkgsFromCookie();
  119. for (var pkg in _pkgs) {
  120. this.togglePackage(pkg, _pkgs[pkg]);
  121. }
  122. }
  123. /**
  124. * Hide all the checkboxes which are only applicable to the non-JS version.
  125. */
  126. this.hideCheckboxes = function hideCheckboxes() {
  127. var inputs = document.getElementsByTagName("input");
  128. for (var i = 0, len = inputs.length; i < len; i++) {
  129. if (inputs.item(i).className == "sweety-check") {
  130. inputs.item(i).style.display = "none";
  131. }
  132. }
  133. delete inputs;
  134. }
  135. /**
  136. * Load the available test case list in the UI.
  137. */
  138. this.loadTestList = function loadTestList() {
  139. var caseBox = _getListContainer();
  140. //Show or hide any tests
  141. for (var testCase in sweetyTestCases) {
  142. var pkgName = _pkgFor(testCase);
  143. _pkgTests[pkgName][testCase] = sweetyTestCases[testCase];
  144. this.paintTestCaseIdle(testCase);
  145. var pkg = _pkgFor(testCase);
  146. this.paintPkgIdle(pkg);
  147. var testDiv = _getElementById(testCase);
  148. //Make it look idle
  149. testDiv.className = "sweety-test sweety-idle";
  150. if (sweetyTestCases[testCase]) {
  151. if (typeof _pkgs[pkg] == "undefined") {
  152. testDiv.style.display = "block";
  153. } else if (_pkgs[pkg]) {
  154. testDiv.style.display = "block";
  155. }
  156. } else {
  157. testDiv.style.display = "none";
  158. }
  159. }
  160. //Show or hide any packages
  161. for (var pkgName in _pkgTests) {
  162. var display = false;
  163. var len = 0;
  164. for (var testCase in _pkgTests[pkgName]) {
  165. if (_pkgTests[pkgName][testCase]) {
  166. display = true;
  167. //break;
  168. len++;
  169. }
  170. }
  171. this.showPkgCount(pkgName, len);
  172. this.showHidePkg(pkgName, display);
  173. }
  174. }
  175. /**
  176. * Shows or hides the headers for the given package.
  177. * @param {String} pkg
  178. * @param {Boolean} show
  179. */
  180. this.showHidePkg = function showHidePkg(pkg, show) {
  181. var pkgDiv = _getElementById("sweety-package-" + pkg);
  182. if (show) {
  183. pkgDiv.style.display = "block";
  184. } else {
  185. pkgDiv.style.display = "none";
  186. }
  187. }
  188. this.showPkgCount = function showPkgCount(pkg, n) {
  189. var countBox = _getElementById("sweety-pkg-count-" + pkg);
  190. _setContent(countBox, "(" + n + ")");
  191. }
  192. /**
  193. * Reset all the aggregate results in the UI.
  194. */
  195. this.resetTotals = function resetTotals() {
  196. _this.paintNumCases(0);
  197. _this.paintNumRun(0);
  198. _this.paintNumPasses(0);
  199. _this.paintNumFails(0);
  200. _this.paintNumExceptions(0);
  201. _this.paintAllIdle();
  202. }
  203. /**
  204. * Paint or unpaint the networking icon to indicate communication with the server.
  205. * @param {Boolean} on
  206. */
  207. this.paintNetworking = function paintNetworking(on) {
  208. if (on) {
  209. _getCommIcon().style.display = "block";
  210. } else {
  211. _getCommIcon().style.display = "none";
  212. }
  213. }
  214. /**
  215. * Flush the contents of the assertion message area.
  216. */
  217. this.resetMessageDiv = function resetMessageDiv() {
  218. _getMessages().innerHTML = "";
  219. _getElementById("sweety-smoke-images").innerHTML = "";
  220. }
  221. /**
  222. * Marks a given package as running (yellow) in the UI.
  223. * @param {String} pkg
  224. */
  225. this.paintPkgRunning = function paintPkgRunning(pkg) {
  226. _getElementById("sweety-package-" + pkg).className = "sweety-package-header sweety-running";
  227. }
  228. /**
  229. * Marks a given package as idle (grey) in the UI.
  230. * @param {String} pkg
  231. */
  232. this.paintPkgIdle = function paintPkgIdle(pkg) {
  233. _getElementById("sweety-package-" + pkg).className = "sweety-package-header sweety-pkg-idle";
  234. }
  235. /**
  236. * Marks a given package as passed (green) in the UI.
  237. * @param {String} pkg
  238. */
  239. this.paintPkgPassed = function paintPkgPassed(pkg) {
  240. _getElementById("sweety-package-" + pkg).className = "sweety-package-header sweety-pass";
  241. }
  242. /**
  243. * Marks a given package as failed (red) in the UI.
  244. * @param {String} pkg
  245. */
  246. this.paintPkgFailed = function paintPkgFailed(pkg) {
  247. _getElementById("sweety-package-" + pkg).className = "sweety-package-header sweety-fail";
  248. }
  249. /**
  250. * Marks a given test case as running (yellow) in the UI.
  251. * @param {String} testCase
  252. */
  253. this.paintTestCaseRunning = function paintTestCaseRunning(testCase) {
  254. _getElementById(testCase).className = "sweety-test sweety-running";
  255. }
  256. /**
  257. * Marks a given test case as idle (grey) in the UI.
  258. * @param {String} testCase
  259. */
  260. this.paintTestCaseIdle = function paintTestCaseIdle(testCase) {
  261. _getElementById(testCase).className = "sweety-test sweety-idle";
  262. }
  263. /**
  264. * Marks a given test case as failed (red) in the UI.
  265. * @param {String} testCase
  266. */
  267. this.paintTestCaseFailed = function paintTestCaseFailed(testCase) {
  268. _getElementById(testCase).className = "sweety-test sweety-fail";
  269. }
  270. /**
  271. * Marks a given test case as passed (green) in the UI.
  272. * @param {String} testCase
  273. */
  274. this.paintTestCasePassed = function paintTestCasePassed(testCase) {
  275. _getElementById(testCase).className = "sweety-test sweety-pass";
  276. }
  277. /**
  278. * Paints a skipped testcase message to the message area.
  279. * @param {String} message
  280. * @param {String} path
  281. */
  282. this.paintSkip = function paintSkip(message, path) {
  283. var skipDiv = document.createElement("div");
  284. skipDiv.className = "sweety-message";
  285. var skipLabel = _createSkipLabel("Skip");
  286. skipDiv.appendChild(skipLabel);
  287. var messageSpan = document.createElement("strong");
  288. _setContent(messageSpan, ": " + message);
  289. skipDiv.appendChild(messageSpan);
  290. var pathDiv = _createPathDiv(path);
  291. skipDiv.appendChild(pathDiv);
  292. _getMessages().appendChild(skipDiv);
  293. }
  294. /**
  295. * Paints an unexpected exception notice to the message area.
  296. * @param {String} message
  297. * @param {String} path
  298. */
  299. this.paintException = function paintException(message, path) {
  300. var exceptionDiv = document.createElement("div");
  301. exceptionDiv.className = "sweety-message";
  302. var exceptionLabel = _createFailLabel("Exception");
  303. exceptionDiv.appendChild(exceptionLabel);
  304. var messageSpan = document.createElement("strong");
  305. _setContent(messageSpan, ": " + message);
  306. exceptionDiv.appendChild(messageSpan);
  307. var pathDiv = _createPathDiv(path);
  308. exceptionDiv.appendChild(pathDiv);
  309. _getMessages().appendChild(exceptionDiv);
  310. }
  311. /**
  312. * Paints a failed assertion message to the message area.
  313. * @param {String} message
  314. * @param {String} path
  315. */
  316. this.paintFail = function paintFail(message, path) {
  317. var failDiv = document.createElement("div");
  318. failDiv.className = "sweety-message";
  319. var failLabel = _createFailLabel("Fail");
  320. failDiv.appendChild(failLabel);
  321. var messageSpan = document.createElement("span");
  322. _setContent(messageSpan, ": " + message);
  323. failDiv.appendChild(messageSpan);
  324. var pathDiv = _createPathDiv(path);
  325. failDiv.appendChild(pathDiv);
  326. _getMessages().appendChild(failDiv);
  327. }
  328. /**
  329. * Paints dump() output to the message area.
  330. * @param {String} output
  331. * @param {String} path
  332. */
  333. this.paintOutput = function paintOutput(output, path) {
  334. var refs;
  335. if (refs = /^\{image @ (.*?)\}$/.exec(output)) {
  336. this.paintSmokeImage(refs[1]);
  337. } else {
  338. var outputPane = document.createElement("pre");
  339. outputPane.className = "sweety-raw-output";
  340. _setContent(outputPane, output);
  341. _getMessages().appendChild(outputPane);
  342. }
  343. }
  344. this.paintSmokeImage = function paintSmokeImage(imageSrc) {
  345. var imagePane = _getElementById("sweety-smoke-images");
  346. var smokeImg = document.createElement("img");
  347. smokeImg.title = 'Smoke test image';
  348. smokeImg.src = imageSrc;
  349. smokeImg.style.cursor = 'pointer';
  350. smokeImg.onclick = function() { window.open(imageSrc); };
  351. imagePane.appendChild(smokeImg);
  352. }
  353. /**
  354. * Paints an internal message to the message area.
  355. * @param {String} message
  356. * @param {String} path
  357. */
  358. this.paintMessage = function paintMessage(message) {
  359. var messageDiv = document.createElement("div");
  360. messageDiv.className = "sweety-message sweety-running";
  361. _setContent(messageDiv, message);
  362. _getMessages().appendChild(messageDiv);
  363. }
  364. /**
  365. * Paints the current number of test cases to the summary bar.
  366. * @param {Number} num
  367. */
  368. this.paintNumCases = function paintNumCases(num) {
  369. _setContent(_getCases(), num);
  370. }
  371. /**
  372. * Paints the current number of finished test cases to the summary bar.
  373. * @param {Number} num
  374. */
  375. this.paintNumRun = function paintNumRun(num) {
  376. _setContent(_getComplete(), num);
  377. }
  378. /**
  379. * Paints the current number of passing assertions to the summary bar.
  380. * @param {Number} num
  381. */
  382. this.paintNumPasses = function paintNumPasses(num) {
  383. _setContent(_getPasses(), num);
  384. }
  385. /**
  386. * Paints the current number of failing assertions to the summary bar.
  387. * @param {Number} num
  388. */
  389. this.paintNumFails = function paintNumFails(num) {
  390. _setContent(_getFails(), num);
  391. }
  392. /**
  393. * Paints the current number of exceptions to the summary bar.
  394. * @param {Number} num
  395. */
  396. this.paintNumExceptions = function paintNumExceptions(num) {
  397. _setContent(_getExceptions(), num);
  398. }
  399. /**
  400. * Paints the summary bar (green) as passed.
  401. */
  402. this.paintConclusionPassed = function paintConclusionPassed() {
  403. _getResultsBar().className = "sweety-pass";
  404. }
  405. /**
  406. * Paints the summary bar (red) as failed.
  407. */
  408. this.paintConclusionFailed = function paintConclusionFailed() {
  409. _getResultsBar().className = "sweety-fail";
  410. }
  411. /**
  412. * Paints the summary bar (yellow) as running.
  413. */
  414. this.paintAllRunning = function paintAllRunning() {
  415. _getResultsBar().className = "sweety-running";
  416. }
  417. /**
  418. * Paints the summary bar (grey) as idle.
  419. */
  420. this.paintAllIdle = function paintAllIdle() {
  421. _getResultsBar().className = "sweety-idle";
  422. }
  423. /**
  424. * Puts the filter box in searching L&F.
  425. */
  426. this.paintSearching = function paintSearching() {
  427. _getFilter().className = "sweety-text sweety-waiting";
  428. }
  429. /**
  430. * Returns the filter box the idle L&F.
  431. */
  432. this.paintSearchComplete = function paintSearchComplete() {
  433. _getFilter().className = "sweety-text";
  434. }
  435. /**
  436. * Apply data to a page element.
  437. * @param {Element} el
  438. * @param {String} content
  439. */
  440. var _setContent = function _setContent(el, content) {
  441. if (typeof el.textContent != "undefined") {
  442. el.textContent = content;
  443. } else {
  444. el.innerHTML = content;
  445. }
  446. }
  447. /**
  448. * Create a label used at the start of a message to indicate a skipped test case.
  449. * @param {String} label
  450. * @returns HTMLSpanElement
  451. */
  452. var _createSkipLabel = function _createSkipLabel(label) {
  453. var skipLabel = document.createElement("span");
  454. skipLabel.className = "sweety-skip-text";
  455. _setContent(skipLabel, label);
  456. return skipLabel;
  457. }
  458. /**
  459. * Create a label used at the start of a message to indicate failure.
  460. * @param {String} label
  461. * @returns HTMLSpanElement
  462. */
  463. var _createFailLabel = function _createFailLabel(label) {
  464. var failLabel = document.createElement("span");
  465. failLabel.className = "sweety-fail-text";
  466. _setContent(failLabel, label);
  467. return failLabel;
  468. }
  469. /**
  470. * Creates the text which shows the complete pathway to a test method.
  471. * The path includes all groups, the test case and the test method.
  472. * @param {String} path
  473. * @returns HTMLDivElement
  474. */
  475. var _createPathDiv = function _createPathDiv(path) {
  476. var pathDiv = document.createElement("div");
  477. pathDiv.className = "sweety-test-path";
  478. _setContent(pathDiv, "in " + path);
  479. return pathDiv;
  480. }
  481. var _paintFilterDisabled = function _paintFilterDisabled(disabled) {
  482. if (disabled) {
  483. _getFilter().disabled = true;
  484. _getFilter().className = "sweety-text sweety-disabled";
  485. } else {
  486. _getFilter().disabled = false;
  487. _getFilter().className = "sweety-text";
  488. }
  489. }
  490. /**
  491. * A caching wrapper around document.getElementById().
  492. * @param {String} id
  493. * @returns Element
  494. */
  495. var _getElementById = function _getElementById(elId) {
  496. if (!_cached[elId]) {
  497. _cached[elId] = document.getElementById(elId);
  498. }
  499. return _cached[elId];
  500. }
  501. /**
  502. * Get the icon which shows network activity.
  503. * @returns Element
  504. */
  505. var _getCommIcon = function _getCommIcon() {
  506. return _getElementById("sweety-communication");
  507. }
  508. /**
  509. * Get the container which holds the list of test cases.
  510. * @returns Element
  511. */
  512. var _getListContainer = function _getListContainer() {
  513. return _getElementById("sweety-testlist-container");
  514. }
  515. /**
  516. * Get the container where all assertion messages go.
  517. * @returns Element
  518. */
  519. var _getMessages = function _getMessages() {
  520. return _getElementById("sweety-messages");
  521. }
  522. /**
  523. * Get the element for number of test cases.
  524. * @returns Element
  525. */
  526. var _getCases = function _getCases() {
  527. return _getElementById("sweety-num-cases");
  528. }
  529. /**
  530. * Get the container for number of test cases finished.
  531. * @returns Element
  532. */
  533. var _getComplete = function _getComplete() {
  534. return _getElementById("sweety-num-run");
  535. }
  536. /**
  537. * Get the container for number of exceptions.
  538. * @returns Element
  539. */
  540. var _getExceptions = function _getExceptions() {
  541. return _getElementById("sweety-num-exceptions");
  542. }
  543. /**
  544. * Get the container for number of fails.
  545. * @returns Element
  546. */
  547. var _getFails = function _getFails() {
  548. return _getElementById("sweety-num-fails");
  549. }
  550. /**
  551. * Get the container for number of passes.
  552. * @returns Element
  553. */
  554. var _getPasses = function _getPasses() {
  555. return _getElementById("sweety-num-passes");
  556. }
  557. /**
  558. * Get the bar showing aggregate results.
  559. * @returns Element
  560. */
  561. var _getResultsBar = function _getResutsBar() {
  562. return _getElementById("sweety-results");
  563. }
  564. /**
  565. * Get the filter input box.
  566. * @returns Element
  567. */
  568. var _getFilter = function _getFilter() {
  569. return _getElementById("sweety-filter");
  570. }
  571. /**
  572. * Get the button which operates the filter.
  573. * @returns Element
  574. */
  575. var _getRunButton = function _getRunButton() {
  576. return _getElementById("sweety-run-button");
  577. }
  578. var _loadPkgsFromCookie = function _loadPkgsFromCookie() {
  579. for (var testCase in sweetyTestCases) {
  580. var pkg = _pkgFor(testCase);
  581. _pkgs[pkg] = false;
  582. }
  583. var cookieBits = document.cookie.split(/\s*;\s*/g);
  584. for (var i in cookieBits) {
  585. if (cookieBits[i].substring(0, 9) != "sweetyPkg")
  586. {
  587. continue;
  588. }
  589. var nvp = cookieBits[i].substring(9).split('=');
  590. _pkgs[unescape(nvp[0])] = (nvp[1] == "0") ? false : true;
  591. //alert(unescape(nvp[0]) + " => " + _pkgs[unescape(nvp[0])]);
  592. }
  593. }
  594. var _pkgFor = function _pkgFor(testName) {
  595. return testName.replace(/_?[^_]+$/, "");
  596. }
  597. }
  598. //Create an instance of the UI Manager for usage
  599. var sweetyUI = new SweetyUIManager();
  600. /**
  601. * A filter to hide/show test cases in the list.
  602. * @author Chris Corbyn
  603. * @consructor
  604. */
  605. function SweetyFilter() {
  606. var _this = this;
  607. /** Asynchronous page timer (so nothing happens whilst typing) */
  608. var _timer;
  609. /** The sweety-filter element, lazy loaded */
  610. var _filter = null;
  611. /**
  612. * Update the display once the search is complete.
  613. */
  614. this.repaintUI = function repaintUI() {
  615. sweetyUI.initialize();
  616. sweetyUI.paintSearchComplete();
  617. }
  618. /**
  619. * Search for matching test cases.
  620. */
  621. this.search = function search() {
  622. sweetyUI.paintSearching();
  623. var query = _getFilterInput().value.toLowerCase();
  624. var queryBits = query.split(/[^\!a-zA-Z0-9_]+/g);
  625. //Cancel searching if still typing
  626. try {
  627. window.clearTimeout(_timer);
  628. } catch (e) { }
  629. for (var testCase in sweetyTestCases) {
  630. for (var i in queryBits) {
  631. var testFor = queryBits[i];
  632. var isNegated = ("!" == testFor.charAt(0));
  633. if (isNegated) {
  634. testFor = testFor.substring(1);
  635. }
  636. if (!isNegated && 0 > testCase.toLowerCase().indexOf(testFor)) {
  637. sweetyTestCases[testCase] = false;
  638. break;
  639. } else if (isNegated && 0 < testCase.toLowerCase().indexOf(testFor)) {
  640. sweetyTestCases[testCase] = false;
  641. break;
  642. } else {
  643. sweetyTestCases[testCase] = true;
  644. }
  645. }
  646. }
  647. //Only apply the search in 500ms, since user may be typing
  648. _timer = window.setTimeout(_this.repaintUI, 500);
  649. }
  650. /**
  651. * Get a lazy loaded reference to the input element.
  652. * @return HTMLInputElement
  653. */
  654. var _getFilterInput = function _getFilterInput() {
  655. if (!_filter) {
  656. _filter = document.getElementById("sweety-filter");
  657. }
  658. return _filter;
  659. }
  660. }
  661. //Create a new instance of the filter
  662. var sweetyFilter = new SweetyFilter();
  663. /**
  664. * The reporter which gathers aggregate results and displays a summary.
  665. * @author Chris Corbyn
  666. * @constructor
  667. * @param {Boolean} reportPkgs if package status should be reported
  668. */
  669. function SweetyTemplateAggregateReporter(testCaseList, reportPkgs) {
  670. var _this = this;
  671. /** True if this reporter instance is running now */
  672. var _started = false;
  673. /** Aggregate totals */
  674. var _aggregates = { cases : 0, run: 0, passes : 0, fails : 0, exceptions : 0 };
  675. /** Aggregates per-package */
  676. var _pkgs = { };
  677. /** Currently running package */
  678. var _currentPkg;
  679. /**
  680. * Creates a reporter for the given testCase.
  681. * @param {String} testCase
  682. * @returns SweetyReporter
  683. */
  684. this.getReporterFor = function getReporterFor(testCase) {
  685. _aggregates.cases++;
  686. if (reportPkgs) {
  687. var pkg = _getPkgName(testCase);
  688. sweetyUI.paintPkgRunning(pkg);
  689. _pkgs[pkg].cases++;
  690. if (_currentPkg && _currentPkg != pkg) {
  691. _updatePkgStatus(_currentPkg);
  692. }
  693. _currentPkg = pkg;
  694. }
  695. sweetyUI.paintNumCases(_aggregates.cases);
  696. var reporter = new SweetyTemplateCaseReporter(testCase, _this);
  697. return reporter;
  698. }
  699. /**
  700. * Updates the UI with the new aggregate totals.
  701. */
  702. this.notifyEnded = function notifyEnded(testCase) {
  703. _aggregates.run++;
  704. if (reportPkgs) {
  705. var pkg = _getPkgName(testCase);
  706. _pkgs[pkg].run++;
  707. }
  708. //Update the UI with new totals
  709. sweetyUI.paintNumRun(_aggregates.run);
  710. sweetyUI.paintNumPasses(_aggregates.passes);
  711. sweetyUI.paintNumFails(_aggregates.fails);
  712. sweetyUI.paintNumExceptions(_aggregates.exceptions);
  713. }
  714. /**
  715. * Returns true if this reporter instance is running.
  716. * @returns Boolean
  717. */
  718. this.isStarted = function isStarted() {
  719. return _started;
  720. }
  721. /**
  722. * Start reporting.
  723. */
  724. this.start = function start() {
  725. _started = true;
  726. if (reportPkgs)
  727. {
  728. for (var i = 0, len = testCaseList.length; i < len; i++) {
  729. var testCase = testCaseList[i];
  730. var pkg = _getPkgName(testCase);
  731. if (typeof _pkgs[pkg] == "undefined") {
  732. _pkgs[pkg] = { cases : 0, run : 0, passes : 0, fails : 0, exceptions : 0 };
  733. }
  734. }
  735. }
  736. sweetyUI.allowInteractivity(false);
  737. sweetyUI.paintNetworking(true);
  738. sweetyUI.paintAllRunning();
  739. }
  740. /**
  741. * Report a skipped test case.
  742. * @param {String} message
  743. * @param {String} path
  744. */
  745. this.reportSkip = function reportSkip(message, path) {
  746. sweetyUI.paintSkip(message, path);
  747. }
  748. /**
  749. * Report a passing assertion.
  750. * @param {String} message
  751. * @param {String} path
  752. */
  753. this.reportPass = function reportPass(message, path) {
  754. _aggregates.passes++;
  755. if (reportPkgs) {
  756. _pkgs[_currentPkg].passes++;
  757. }
  758. }
  759. /**
  760. * Report a failing assertion.
  761. * @param {String} message
  762. * @param {String} path
  763. */
  764. this.reportFail = function reportFail(message, path) {
  765. _aggregates.fails++;
  766. if (reportPkgs) {
  767. _pkgs[_currentPkg].fails++;
  768. }
  769. sweetyUI.paintFail(message, path);
  770. }
  771. /**
  772. * Report an unexpected exception.
  773. * @param {String} message
  774. * @param {String} path
  775. */
  776. this.reportException = function reportException(message, path) {
  777. _aggregates.exceptions++;
  778. if (reportPkgs) {
  779. _pkgs[_currentPkg].exceptions++;
  780. }
  781. sweetyUI.paintException(message, path);
  782. }
  783. /**
  784. * Handle test case output from something like a dump().
  785. * @param {String} output
  786. * @param {String} path
  787. */
  788. this.reportOutput = function reportOutput(output, path) {
  789. sweetyUI.paintOutput(output, path);
  790. }
  791. /**
  792. * End reporting.
  793. * This method is used to come to a conclusion about the test results in the UI.
  794. */
  795. this.finish = function finish() {
  796. _started = false;
  797. if (reportPkgs) {
  798. _updatePkgStatus(_currentPkg);
  799. }
  800. sweetyUI.allowInteractivity(true);
  801. sweetyUI.paintNetworking(false);
  802. if ((!_aggregates.fails && !_aggregates.exceptions)
  803. && (_aggregates.cases == _aggregates.run)) {
  804. sweetyUI.paintConclusionPassed();
  805. } else {
  806. sweetyUI.paintConclusionFailed();
  807. }
  808. var incompleteCount = _aggregates.cases - _aggregates.run;
  809. //Check if all tests actually got fully parsed (i.e. finished)
  810. if (0 < incompleteCount) {
  811. sweetyUI.paintMessage(
  812. incompleteCount + " test case(s) did not complete." +
  813. " This may be because invalid XML was output during the test run" +
  814. " and/or because an error occured." +
  815. " Incomplete test cases are shown in yellow. Click the HTML link " +
  816. "next to the test for more detail.");
  817. }
  818. }
  819. var _getPkgName = function _getPkgName(testCase) {
  820. return testCase.replace(/_?[^_]+$/, "");
  821. }
  822. var _updatePkgStatus = function _updatePkgStatus(pkg) {
  823. if ((!_pkgs[pkg].fails && !_pkgs[pkg].exceptions)
  824. && (_pkgs[pkg].cases == _pkgs[pkg].run)) {
  825. sweetyUI.paintPkgPassed(pkg);
  826. } else if (_pkgs[pkg].cases == _pkgs[pkg].run) {
  827. sweetyUI.paintPkgFailed(pkg);
  828. }
  829. }
  830. }
  831. SweetyTemplateAggregateReporter.prototype = new SweetyReporter();
  832. /**
  833. * The reporter class per-test case.
  834. * @author Chris Corbyn
  835. * @consructor
  836. */
  837. function SweetyTemplateCaseReporter(testCase, reporter) {
  838. var _this = this;
  839. /** Aggregate totals */
  840. var _aggregates = { passes : 0, fails : 0, exceptions : 0 };
  841. /** The DIV element showing this test case */
  842. var _testCaseDiv = document.getElementById(testCase);
  843. /** True only if this reporter is running */
  844. var _started = false;
  845. /**
  846. * Stubbed only to return itself.
  847. * @returns SweetyReporter
  848. */
  849. this.getReporterFor = function getReporterFor(testCase) {
  850. return _this;
  851. }
  852. /**
  853. * Returns true when the reporter is started.
  854. * @returns Boolean
  855. */
  856. this.isStarted = function isStarted() {
  857. return _started;
  858. }
  859. /**
  860. * Start reporting.
  861. */
  862. this.start = function start() {
  863. _started = true;
  864. sweetyUI.paintTestCaseRunning(testCase);
  865. }
  866. /**
  867. * Report a skipped test case.
  868. * @param {String} message
  869. * @param {String} path
  870. */
  871. this.reportSkip = function reportSkip(message, path) {
  872. reporter.reportSkip(message, path);
  873. }
  874. /**
  875. * Report a passing assertion.
  876. * @param {String} message
  877. * @param {String} path
  878. */
  879. this.reportPass = function reportPass(message, path) {
  880. _aggregates.passes++;
  881. reporter.reportPass(message, path);
  882. }
  883. /**
  884. * Report a failing assertion.
  885. * @param {String} message
  886. * @param {String} path
  887. */
  888. this.reportFail = function reportFail(message, path) {
  889. _aggregates.fails++;
  890. reporter.reportFail(message, path);
  891. }
  892. /**
  893. * Report an unexpected exception.
  894. * @param {String} message
  895. * @param {String} path
  896. */
  897. this.reportException = function reportException(message, path) {
  898. _aggregates.exceptions++;
  899. reporter.reportException(message, path);
  900. }
  901. /**
  902. * Handle output from a test case in the form of something like a dump().
  903. * @param {String} output
  904. * @param {string} path
  905. */
  906. this.reportOutput = function reportOutput(output, path) {
  907. reporter.reportOutput(output, path);
  908. }
  909. /**
  910. * End reporting.
  911. */
  912. this.finish = function finish() {
  913. _started = false;
  914. if (!_aggregates.fails && !_aggregates.exceptions) {
  915. sweetyUI.paintTestCasePassed(testCase);
  916. } else {
  917. sweetyUI.paintTestCaseFailed(testCase);
  918. }
  919. reporter.notifyEnded(testCase);
  920. }
  921. }
  922. SweetyTemplateCaseReporter.prototype = new SweetyReporter();
  923. /**
  924. * Wraps the invokation of SweetyTestRunner.
  925. * @author Chris Corbyn
  926. * @constructor
  927. */
  928. function SweetyTestWrapper() {
  929. var _this = this;
  930. /**
  931. * Run a single test case.
  932. * @param {String} testClass
  933. */
  934. this.runTestCase = function runTestCase(testClass) {
  935. var testCaseList = new Array();
  936. testCaseList.push(testClass);
  937. var reporter = new SweetyTemplateAggregateReporter(testCaseList);
  938. var runner = new SweetyTestRunner();
  939. runner.runTests(testCaseList, reporter);
  940. }
  941. /**
  942. * Run all selected test cases.
  943. */
  944. this.runAll = function runAll(pkg) {
  945. var pkgRegex;
  946. if (pkg) {
  947. pkgRegex = new RegExp("^" + pkg + "_[^_]+$");
  948. }
  949. var testCaseList = new Array();
  950. for (var testCase in sweetyTestCases) {
  951. if (!sweetyTestCases[testCase] || (pkg && !testCase.match(pkgRegex))) {
  952. continue;
  953. }
  954. testCaseList.push(testCase);
  955. }
  956. var reporter = new SweetyTemplateAggregateReporter(testCaseList, true);
  957. var runner = new SweetyTestRunner();
  958. runner.runTests(testCaseList, reporter);
  959. }
  960. }
  961. //Create an instance of the test runner for usage
  962. var sweetyRunner = new SweetyTestWrapper();
  963. if (typeof document.onreadystatechange != "undefined") { //IE 6/7
  964. document.onreadystatechange = function() {
  965. if (document.readyState == "complete") {
  966. sweetyUI.restore(); sweetyUI.initialize();
  967. }
  968. };
  969. } else { //Fallback
  970. window.onload = function() {
  971. sweetyUI.restore(); sweetyUI.initialize();
  972. };
  973. try { //FF
  974. document.addEventListener("DOMContentLoaded", window.onload, false);
  975. } catch (e) {
  976. }
  977. }