├── .gitmodules ├── README.markdown ├── Rakefile ├── dist └── evidence.js └── src ├── assertion_helpers ├── assertion_failed_error.js ├── assertion_message.js └── assertion_skipped_error.js ├── assertions.js ├── auto_runner.js ├── constants.yml ├── evidence.js ├── helpers.js ├── test_case.js ├── test_loader.js ├── test_result.js ├── test_result_tree.js ├── test_runner.js ├── test_suite.js ├── ui.js └── ui ├── ascii_view_builder.js ├── console.js ├── console ├── command_line_logger.js ├── logger.js ├── popup_logger.js ├── test_result.js └── test_runner.js ├── web.js └── web ├── abstract_widget.js ├── gui.js ├── labelled_text.js ├── list.js ├── list_element.js ├── progress_bar.js ├── test_result.js └── test_runner.js /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/sprockets"] 2 | path = vendor/sprockets 3 | url = git://github.com/sstephenson/sprockets.git 4 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobie/Evidence/16fa654f1321d8a900bbf40f957e5327d3974219/README.markdown -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | EVIDENCE_ROOT_DIR = File.expand_path(File.dirname(__FILE__)) 2 | EVIDENCE_DIST_DIR = File.join(EVIDENCE_ROOT_DIR, 'dist') 3 | EVIDENCE_SRC_DIR = File.join(EVIDENCE_ROOT_DIR, 'src') 4 | 5 | $:.unshift File.join(EVIDENCE_ROOT_DIR, 'vendor', 'sprockets', 'lib') 6 | 7 | def sprocketize(path, source, destination, strip_comments = true) 8 | begin 9 | require 'sprockets' 10 | rescue LoadError => e 11 | puts "\nYou'll need Sprockets to build Evidence. Just run:\n\n" 12 | puts " $ git submodule init" 13 | puts " $ git submodule update" 14 | puts "\nand you should be all set.\n\n" 15 | end 16 | 17 | secretary = Sprockets::Secretary.new( 18 | :root => File.join(EVIDENCE_ROOT_DIR, path), 19 | :load_path => [EVIDENCE_SRC_DIR], 20 | :source_files => [source], 21 | :strip_comments => strip_comments 22 | ) 23 | 24 | secretary.concatenation.save_to(destination) 25 | end 26 | 27 | desc 'Builds the distribution.' 28 | task :dist do 29 | dest = File.join(EVIDENCE_DIST_DIR, 'evidence.js') 30 | sprocketize('src', 'evidence.js', dest) 31 | end 32 | 33 | -------------------------------------------------------------------------------- /dist/evidence.js: -------------------------------------------------------------------------------- 1 | /* evidence.js, version 0.6 2 | * 3 | * Copyright (c) 2009 Tobie Langel (http://tobielangel.com) 4 | * 5 | * evidence.js is freely distributable under the terms of an MIT-style license. 6 | *--------------------------------------------------------------------------*/ 7 | 8 | (function(global) { 9 | var originalEvidence = global.Evidence, 10 | originalOnload = global.onload; 11 | 12 | function Evidence() { 13 | TestCase.extend.apply(TestCase, arguments); 14 | } 15 | 16 | function noConflict() { 17 | global.Evidence = originalEvidence; 18 | return Evidence; 19 | } 20 | 21 | Evidence.noConflict = noConflict; 22 | Evidence.VERSION = '0.6'; 23 | 24 | var FILE_REGEXP = /.*?\/(\w+\.html)(.*)/; 25 | 26 | function getNameFromFile() { 27 | return (global.location || '').toString().replace(FILE_REGEXP, '$1'); 28 | } 29 | 30 | function chain(subclass, superclass) { 31 | function Subclass() {} 32 | Subclass.prototype = superclass.prototype; 33 | subclass.prototype = new Subclass(); 34 | subclass.prototype.constructor = subclass; 35 | return subclass; 36 | } 37 | 38 | function defer(block, context) { 39 | if ('setTimeout' in global) { 40 | global.setTimeout(function() { 41 | block.call(context); 42 | }, 0); 43 | } else { 44 | block.call(context); 45 | } 46 | } 47 | function AssertionSkippedError(message) { 48 | this.message = message; 49 | } 50 | 51 | AssertionSkippedError.displayName = 'AssertionSkippedError'; 52 | 53 | (function(p) { 54 | p.name = 'AssertionSkippedError'; 55 | })(AssertionSkippedError.prototype); 56 | Evidence.AssertionSkippedError = AssertionSkippedError; 57 | function AssertionFailedError(message, template, args) { 58 | this.message = message; 59 | this.template = template || ''; 60 | this.args = args; 61 | } 62 | 63 | AssertionFailedError.displayName = 'AssertionFailedError'; 64 | 65 | (function(p) { 66 | p.name = 'AssertionFailedError'; 67 | })(AssertionFailedError.prototype); 68 | Evidence.AssertionFailedError = AssertionFailedError; 69 | function AssertionMessage(message, template, args) { 70 | this.message = message.replace(/%/g, '%%'); 71 | this.template = template || ''; 72 | this.args = args; 73 | } 74 | 75 | AssertionMessage.displayName = 'AssertionMessage'; 76 | 77 | (function(p) { 78 | function toString() { 79 | return UI.printf(this.message + this.template, this.args); 80 | } 81 | p.toString = toString; 82 | })(AssertionMessage.prototype); 83 | Evidence.AssertionMessage = AssertionMessage; 84 | 85 | var Assertions = (function() { 86 | function _assertExpression(expression, message, template) { 87 | if (expression) { 88 | this.addAssertion(); 89 | } else { 90 | var args = Array.prototype.slice.call(arguments, 3); 91 | throw new AssertionFailedError(message, template, args); 92 | } 93 | } 94 | 95 | function skip(message) { 96 | throw new AssertionSkippedError(message || 'Skipped!'); 97 | } 98 | 99 | function fail(message) { 100 | this._assertExpression(false, message || 'Flunked!'); 101 | } 102 | 103 | function assert(test, message) { 104 | this._assertExpression( 105 | !!test, 106 | message || 'Failed assertion.', 107 | 'Expected %o to evaluate to true.', test 108 | ); 109 | } 110 | 111 | function refute(test, message) { 112 | this._assertExpression( 113 | !test, 114 | message || 'Failed refutation.', 115 | 'Expected %o to evaluate to false.', test 116 | ); 117 | } 118 | 119 | function assertTrue(test, message) { 120 | this._assertExpression( 121 | (test === true), 122 | message || 'Failed assertion.', 123 | 'Expected %o to be true.', test 124 | ); 125 | } 126 | 127 | function refuteTrue(test, message) { 128 | this._assertExpression( 129 | (test !== true), 130 | message || 'Failed refutation.', 131 | 'Expected %o to not be true.', test 132 | ); 133 | } 134 | 135 | function assertNull(test, message) { 136 | this._assertExpression( 137 | (test === null), 138 | message || 'Failed assertion.', 139 | 'Expected %o to be null.', test 140 | ); 141 | } 142 | 143 | function refuteNull(test, message) { 144 | this._assertExpression( 145 | (test !== null), 146 | message || 'Failed refutation.', 147 | 'Expected %o to not be null.', test 148 | ); 149 | } 150 | 151 | function assertUndefined(test, message) { 152 | this._assertExpression( 153 | (typeof test === 'undefined'), 154 | message || 'Failed assertion.', 155 | 'Expected %o to be undefined.', test 156 | ); 157 | } 158 | 159 | function refuteUndefined(test, message) { 160 | this._assertExpression( 161 | (typeof test !== 'undefined'), 162 | message || 'Failed refutation.', 163 | 'Expected %o to not be undefined.', test 164 | ); 165 | } 166 | 167 | function assertFalse(test, message) { 168 | this._assertExpression( 169 | (test === false), 170 | message || 'Failed assertion.', 171 | 'Expected %o to be false.', test 172 | ); 173 | } 174 | 175 | function refuteFalse(test, message) { 176 | this._assertExpression( 177 | (test !== false), 178 | message || 'Failed refutation.', 179 | 'Expected %o to not be false.', test 180 | ); 181 | } 182 | 183 | function assertEqual(expected, actual, message) { 184 | this._assertExpression( 185 | (expected == actual), 186 | message || 'Failed assertion.', 187 | 'Expected %o to be == to %o.', actual, expected 188 | ); 189 | } 190 | 191 | function refuteEqual(expected, actual, message) { 192 | this._assertExpression( 193 | (expected != actual), 194 | message || 'Failed refutation.', 195 | 'Expected %o to be != to %o.', actual, expected 196 | ); 197 | } 198 | 199 | function assertIdentical(expected, actual, message) { 200 | this._assertExpression( 201 | (expected === actual), 202 | message || 'Failed assertion.', 203 | 'Expected %o to be === to %o.', actual, expected 204 | ); 205 | } 206 | 207 | function refuteIdentical(expected, actual, message) { 208 | this._assertExpression( 209 | (expected !== actual), 210 | message || 'Failed refutation.', 211 | 'Expected %o to be !== to %o.', actual, expected 212 | ); 213 | } 214 | 215 | function assertIn(property, object, message) { 216 | this._assertExpression( 217 | (property in object), 218 | message || 'Failed assertion.', 219 | 'Expected "%s" to be a property of %o.', property, object 220 | ); 221 | } 222 | 223 | function refuteIn(property, object, message) { 224 | this._assertExpression( 225 | !(property in object), 226 | message || 'Failed refutation.', 227 | 'Expected "%s" to not be a property of %o.', property, object 228 | ); 229 | } 230 | 231 | return { 232 | _assertExpression: _assertExpression, 233 | skip: skip, 234 | assert: assert, 235 | refute: refute, 236 | assertNot: refute, 237 | assertTrue: assertTrue, 238 | assertNull: assertNull, 239 | assertUndefined: assertUndefined, 240 | assertFalse: assertFalse, 241 | assertIdentical: assertIdentical, 242 | refuteIdentical: refuteIdentical, 243 | assertEqual: assertEqual, 244 | refuteEqual: refuteEqual, 245 | assertIn: assertIn, 246 | refuteIn: refuteIn, 247 | fail: fail, 248 | flunk: fail 249 | }; 250 | })(); 251 | Evidence.Assertions = Assertions; 252 | function TestCase(methodName) { 253 | this._methodName = methodName; 254 | this.name = methodName; 255 | } 256 | 257 | (function() { 258 | function extend(name, methods) { 259 | function TestCaseSubclass(methodName) { 260 | TestCase.call(this, methodName); 261 | } 262 | 263 | if (!methods) { 264 | methods = name; 265 | name = getNameFromFile(); 266 | } 267 | 268 | chain(TestCaseSubclass, this); 269 | TestCaseSubclass.displayName = name; 270 | TestCaseSubclass.extend = extend; 271 | 272 | for(var prop in methods) { 273 | TestCaseSubclass.prototype[prop] = methods[prop]; 274 | } 275 | TestCase.subclasses.push(TestCaseSubclass); 276 | return TestCaseSubclass; 277 | } 278 | 279 | function AssertionsMixin() {} 280 | AssertionsMixin.prototype = Assertions; 281 | TestCase.prototype = new AssertionsMixin(); 282 | TestCase.constructor = TestCase; 283 | 284 | TestCase.displayName = 'TestCase'; 285 | TestCase.extend = extend; 286 | TestCase.subclasses = []; 287 | TestCase.defaultTimeout = 10000; 288 | })(); 289 | 290 | (function(p) { 291 | function run(result) { 292 | this._result = result; 293 | defer(this._run, this); 294 | } 295 | function _run() { 296 | try { 297 | if (this._nextAssertions) { 298 | this._result.restartTest(this); 299 | this._nextAssertions(this); 300 | } else { 301 | /*this._globalProperties = objectKeys(global);*/ 302 | this._result.startTest(this); 303 | this.setUp(this); 304 | this[this._methodName](this); 305 | } 306 | } catch(e) { 307 | this._filterException(e); 308 | } finally { 309 | if (this._paused) { 310 | this._result.pauseTest(this); 311 | } else { 312 | try { 313 | this.tearDown(this); 314 | } catch(e) { 315 | this._filterException(e); 316 | } finally { 317 | this._nextAssertions = null; 318 | this._result.stopTest(this); 319 | this.parent.next(); 320 | } 321 | } 322 | } 323 | } 324 | 325 | function _filterException(e) { 326 | var name = e.name; 327 | switch(name) { 328 | case 'AssertionFailedError': 329 | this._result.addFailure(this, e); 330 | break; 331 | case 'AssertionSkippedError': 332 | this._result.addSkip(this, e); 333 | break; 334 | default: 335 | this._result.addError(this, e); 336 | } 337 | } 338 | 339 | function pause(assertions) { 340 | this._paused = true; 341 | var self = this; 342 | if (assertions) { this._nextAssertions = assertions; } 343 | self._timeoutId = global.setTimeout(function() { 344 | self.resume(function() { 345 | self.fail('Test timed out. Testing was not resumed after being paused.'); 346 | }); 347 | }, TestCase.defaultTimeout); 348 | } 349 | 350 | function resume(assertions) { 351 | if (this._paused) { // avoid race conditions 352 | this._paused = false; 353 | global.clearTimeout(this._timeoutId); 354 | if (assertions) { this._nextAssertions = assertions; } 355 | this._run(); 356 | } 357 | } 358 | 359 | function size() { 360 | return 1; 361 | } 362 | 363 | function toString() { 364 | return this.constructor.displayName + '#' + this.name; 365 | } 366 | 367 | function addAssertion() { 368 | this._result.addAssertion(); 369 | } 370 | 371 | p.run = run; 372 | p._run = _run; 373 | p.addAssertion = addAssertion; 374 | p._filterException = _filterException; 375 | p.pause = pause; 376 | p.resume = resume; 377 | p.size = size; 378 | p.toString = toString; 379 | p.setUp = function() {}; 380 | p.tearDown = function() {}; 381 | })(TestCase.prototype); 382 | Evidence.TestCase = TestCase; 383 | function TestSuite(name, tests) { 384 | this.name = name; 385 | this._tests = []; 386 | if (tests) { 387 | this.push.apply(this, tests); 388 | } 389 | } 390 | 391 | TestSuite.displayName = 'TestSuite'; 392 | 393 | (function(p) { 394 | function run(result) { 395 | this._index = 0; 396 | this._result = result; 397 | result.startSuite(this); 398 | this.next(); 399 | return result; 400 | } 401 | 402 | function next() { 403 | var next = this._tests[this._index]; 404 | if (next) { 405 | this._index++; 406 | next.run(this._result); 407 | } else { 408 | this._result.stopSuite(this); 409 | if (this.parent) { 410 | this.parent.next(); 411 | } else { 412 | this._result.stop(new Date()); 413 | } 414 | } 415 | } 416 | 417 | function push() { 418 | for (var i = 0, length = arguments.length; i < length; i++) { 419 | var test = arguments[i]; 420 | test.parent = this; 421 | this._tests.push(test); 422 | } 423 | } 424 | 425 | function addTest(test) { 426 | test.parent = this; 427 | this._tests.push(test); 428 | } 429 | 430 | function addTests(tests) { 431 | for (var i = 0, length = tests.length; i < length; i++) { 432 | this.addTest(tests[i]); 433 | } 434 | } 435 | 436 | function size() { 437 | var tests = this._tests, 438 | length = tests.length, 439 | sum = 0; 440 | 441 | for (var i = 0; i < length; i++) { 442 | sum += tests[i].size(); 443 | } 444 | return sum; 445 | } 446 | 447 | function isEmpty() { 448 | return this.size() === 0; 449 | } 450 | 451 | function toString() { 452 | return this.name; 453 | } 454 | p.run = run; 455 | p.next = next; 456 | p.push = push; 457 | p.size = size; 458 | p.isEmpty = isEmpty; 459 | p.toString = toString; 460 | })(TestSuite.prototype); 461 | Evidence.TestSuite = TestSuite; 462 | function TestRunner() { 463 | } 464 | 465 | TestRunner.displayName = 'TestRunner'; 466 | 467 | (function(p) { 468 | function run(suite) { 469 | suite.parent = null; 470 | var result = this._makeResult(); 471 | result.start(new Date()); 472 | suite.run(result); 473 | return result; 474 | } 475 | 476 | function _makeResult() { 477 | return new TestResult(); 478 | } 479 | 480 | p.run = run; 481 | p._makeResult = _makeResult; 482 | })(TestRunner.prototype); 483 | Evidence.TestRunner = TestRunner; 484 | function TestLoader() { 485 | } 486 | 487 | TestLoader.displayName = 'TestLoader'; 488 | 489 | (function(p) { 490 | function loadTestsFromTestCase(testcaseClass) { 491 | var suite = new TestSuite(testcaseClass.displayName), 492 | props = this.getTestCaseNames(testcaseClass); 493 | for (var i=0; i < props.length; i++) { 494 | suite.push(new testcaseClass(props[i])); 495 | } 496 | return suite; 497 | } 498 | 499 | function loadTestsFromTestCases(testcases) { 500 | var suite = new TestSuite(getNameFromFile()); 501 | for (var i = 0; i < testcases.length; i++) { 502 | var testcase = testcases[i]; 503 | var subSuite = defaultLoader.loadTestsFromTestCase(testcase); 504 | if (!subSuite.isEmpty()) { suite.push(subSuite); } 505 | } 506 | return suite; 507 | } 508 | 509 | function getTestCaseNames(testcaseClass) { 510 | var results = [], 511 | proto = testcaseClass.prototype, 512 | prefix = this.testMethodPrefix; 513 | 514 | for (var property in proto) { 515 | if (property.indexOf(prefix) === 0) { 516 | results.push(property); 517 | } 518 | } 519 | return results.sort(); 520 | } 521 | 522 | function loadRegisteredTestCases() { 523 | return loadTestsFromTestCases(TestCase.subclasses); 524 | } 525 | 526 | p.loadTestsFromTestCase = loadTestsFromTestCase; 527 | p.loadRegisteredTestCases = loadRegisteredTestCases; 528 | p.loadTestsFromTestCases = loadTestsFromTestCases; 529 | p.testMethodPrefix = 'test'; 530 | p.getTestCaseNames = getTestCaseNames; 531 | 532 | })(TestLoader.prototype); 533 | Evidence.TestLoader = TestLoader; 534 | function AutoRunner() { 535 | if (global.console && global.console.log) { 536 | this.logger = Logger; 537 | } else if (Object.prototype.toString.call(global.environment) === '[object Environment]' && global.print) { 538 | this.logger = CommandLineLogger; 539 | } else { 540 | this.logger = PopupLogger; 541 | } 542 | this.autoRun = true; 543 | this.verbosity = Logger.INFO; 544 | this.runner = ConsoleTestRunner; 545 | } 546 | 547 | (function() { 548 | function run(options) { 549 | var autoRunner = new this(); 550 | options = options || autoRunner.retrieveOptions(); 551 | autoRunner.processOptions(options); 552 | if (autoRunner.autoRun) { autoRunner.run() }; 553 | } 554 | 555 | AutoRunner.run = run; 556 | AutoRunner.displayName = 'AutoRunner'; 557 | AutoRunner.LOGGERS = { 558 | console: Logger, 559 | popup: PopupLogger, 560 | command_line: CommandLineLogger 561 | }; 562 | 563 | AutoRunner.RUNNERS = { 564 | console: ConsoleTestRunner 565 | }; 566 | })(); 567 | 568 | (function(p) { 569 | function run() { 570 | var logger = new this.logger(this.verbosity), 571 | runner = new this.runner(logger), 572 | suite = defaultLoader.loadRegisteredTestCases(); 573 | if (suite._tests.length <= 1) { 574 | suite = suite._tests[0]; 575 | } 576 | return runner.run(suite); 577 | } 578 | 579 | function processQueryString(str) { 580 | var results = {}; 581 | str = (str + '').match(/^(?:[^?#]*\?)([^#]+?)(?:#.*)?$/); 582 | str = str && str[1]; 583 | 584 | if (!str) { return results; } 585 | 586 | var pairs = str.split('&'), 587 | length = pairs.length; 588 | if (!length) { return results; } 589 | 590 | for (var i = 0; i < length; i++) { 591 | var pair = pairs[i].split('='), 592 | key = decodeURIComponent(pair[0]), 593 | value = pair[1]; 594 | value = value ? decodeURIComponent(value) : true; 595 | results[key] = value; 596 | } 597 | return results; 598 | } 599 | 600 | function processArguments(args) { // RHINO 601 | var results = {}; 602 | 603 | for (var i = 0; i < args.length; i++) { 604 | var arg = args[i]; 605 | if (arg.indexOf('-') === 0) { 606 | var value = args[i + 1]; 607 | if (value && value.indexOf('-') !== 0) { 608 | i++; 609 | } else { 610 | value = true; 611 | } 612 | results[arg.substr(1)] = value; 613 | } 614 | } 615 | return results; 616 | } 617 | 618 | function retrieveOptions() { 619 | if (global.location) { 620 | return this.processQueryString(global.location); 621 | } 622 | if (global.arguments) { 623 | return this.processArguments(global.arguments); 624 | } 625 | return {}; 626 | } 627 | 628 | function processOptions(options) { 629 | for(var key in options) { 630 | var value = options[key]; 631 | switch(key) { 632 | case 'timeout': 633 | TestCase.defaultTimeout = global.parseFloat(value) * 1000; 634 | break; 635 | case 'run': 636 | this.autoRun = value === 'false' ? false : true; 637 | break; 638 | case 'logger': 639 | this.logger = AutoRunner.LOGGERS[value]; 640 | break; 641 | case 'verbosity': 642 | var i = global.parseInt(value); 643 | this.verbosity = global.isNaN(i) ? Logger[value] : i; 644 | break; 645 | case 'runner': 646 | this.runner = AutoRunner.RUNNERS[value]; 647 | break; 648 | } 649 | } 650 | } 651 | 652 | p.run = run; 653 | p.processQueryString = processQueryString; 654 | p.processArguments = processArguments; 655 | p.retrieveOptions = retrieveOptions; 656 | p.processOptions = processOptions; 657 | })(AutoRunner.prototype); 658 | Evidence.AutoRunner = AutoRunner; 659 | function TestResult() { 660 | this.testCount = 0; 661 | this.assertionCount = 0; 662 | this.skipCount = 0; 663 | this.skips = []; 664 | this.failureCount = 0; 665 | this.failures = []; 666 | this.errors = []; 667 | this.errorCount = 0; 668 | this.testCount = 0; 669 | } 670 | 671 | TestResult.displayName = 'TestResult'; 672 | 673 | (function(p) { 674 | function addAssertion() { 675 | this.assertionCount++; 676 | } 677 | 678 | function addSkip(testcase, reason) { 679 | this.skipCount++; 680 | this.skips.push(reason); 681 | } 682 | 683 | function addFailure(testcase, reason) { 684 | this.failureCount++; 685 | this.failures.push(reason); 686 | } 687 | 688 | function addError(testcase, error) { 689 | this.errorCount++; 690 | this.errors.push(error); 691 | } 692 | 693 | function startTest(testcase) { 694 | this.testCount++; 695 | } 696 | 697 | function stopTest(testcase) {} 698 | 699 | function pauseTest(testcase) {} 700 | 701 | function restartTest(testcase) {} 702 | 703 | function startSuite(suite) {} 704 | 705 | function stopSuite(suite) {} 706 | 707 | function start(t0) { 708 | this.t0 = t0; 709 | } 710 | 711 | function stop(t1) { 712 | this.t1 = t1; 713 | } 714 | 715 | function toString() { 716 | return this.testCount + ' tests, ' + 717 | this.assertionCount + ' assertions, ' + 718 | this.failureCount + ' failures, ' + 719 | this.errorCount + ' errors, ' + 720 | this.skipCount + ' skips'; 721 | } 722 | 723 | p.addAssertion = addAssertion; 724 | p.addSkip = addSkip; 725 | p.addFailure = addFailure; 726 | p.addError = addError; 727 | p.startTest = startTest; 728 | p.stopTest = stopTest; 729 | p.pauseTest = pauseTest; 730 | p.restartTest = restartTest; 731 | p.startSuite = startSuite; 732 | p.stopSuite = stopSuite; 733 | p.start = start; 734 | p.stop = stop; 735 | p.toString = toString; 736 | })(TestResult.prototype); 737 | Evidence.TestResult = TestResult; 738 | function TestResultTree(name) { 739 | this.testCount = 0; 740 | this.assertionCount = 0; 741 | this.skipCount = 0; 742 | this.skips = []; 743 | this.failureCount = 0; 744 | this.failures = []; 745 | this.errors = []; 746 | this.errorCount = 0; 747 | this.testCount = 0; 748 | this.name = name; 749 | } 750 | 751 | TestResultTree.displayName = 'TestResultTree'; 752 | 753 | (function(p) { 754 | function addAssertion() { 755 | var node = this.currentNode; 756 | do { 757 | node.assertionCount++; 758 | } while (node = node.parent); 759 | } 760 | 761 | function addSkip(testcase, reason) { 762 | var node = this.currentNode; 763 | do { 764 | node.skipCount++; 765 | node.skips.push(reason); 766 | } while (node = node.parent); 767 | } 768 | 769 | function addFailure(testcase, reason) { 770 | var node = this.currentNode; 771 | do { 772 | node.failureCount++; 773 | node.failures.push(reason); 774 | } while (node = node.parent); 775 | } 776 | 777 | function addError(testcase, error) { 778 | var node = this.currentNode; 779 | do { 780 | node.errorCount++; 781 | node.errors.push(error); 782 | } while (node = node.parent); 783 | } 784 | 785 | function startTest(testcase) { 786 | var node = this.createChildNode(testcase.name); 787 | do { 788 | node.testCount++; 789 | } while (node = node.parent); 790 | } 791 | 792 | function stopTest(testcase) { 793 | this.currentNode = this.currentNode.parent || this; 794 | } 795 | 796 | function pauseTest(testcase) {} 797 | 798 | function restartTest(testcase) {} 799 | 800 | function startSuite(suite) { 801 | this.createChildNode(suite.name); 802 | } 803 | 804 | function stopSuite(suite) { 805 | this.currentNode = this.currentNode.parent || this; 806 | } 807 | 808 | function start(t0) { 809 | this.t0 = t0; 810 | this.currentNode = this; 811 | } 812 | 813 | function stop(t1) { 814 | this.t1 = t1; 815 | this.currentNode = null; 816 | } 817 | 818 | function toString() { 819 | var results = ''; 820 | if (this.children) { 821 | results += this.testCount; 822 | results += ' tests, '; 823 | } 824 | return results + 825 | this.assertionCount + ' assertions, ' + 826 | this.failureCount + ' failures, ' + 827 | this.errorCount + ' errors, ' + 828 | this.skipCount + ' skips'; 829 | } 830 | 831 | function createChildNode(name) { 832 | var node = new this.constructor(name); 833 | this.currentNode.appendChild(node); 834 | this.currentNode = node; 835 | return node; 836 | } 837 | 838 | function appendChild(child) { 839 | this.children = this.children || []; 840 | this.children.push(child); 841 | child.parent = this; 842 | } 843 | 844 | function toASCIITree(prefix) { 845 | var str = '', 846 | results = this.toString(), 847 | name = this.name || 'Test results', 848 | childLength = this.children && this.children.length, 849 | rest, 850 | max; 851 | 852 | prefix = prefix || ''; 853 | max = 100 - results.length - prefix.length; 854 | max = Math.max(max, 0); 855 | 856 | if (name.length > max) { 857 | name = '...' + name.substr(name.length - max + 3); 858 | } 859 | 860 | rest = (max - name.length); 861 | str += name; 862 | str += ' '; 863 | for (var i = 0; i < rest; i++) { str += '_'; } 864 | str += ' '; 865 | str += results; 866 | 867 | if (this.errorCount > 0) { 868 | str += ' E'; 869 | } else if (this.failureCount > 0) { 870 | str += ' F'; 871 | } else if (this.skipCount > 0) { 872 | str += ' S'; 873 | } 874 | 875 | str += '\n'; 876 | 877 | if (childLength) { 878 | for (var i = 0; i < childLength; i++) { 879 | str += prefix; 880 | if (i == childLength - 1) { // last 881 | str += '\'-- '; 882 | str += this.children[i].toASCIITree(prefix + ' '); 883 | str += prefix; 884 | str += '\n'; 885 | } else { 886 | str += '|-- '; 887 | str += this.children[i].toASCIITree(prefix + '| '); 888 | } 889 | } 890 | } 891 | return str; 892 | } 893 | 894 | p.toASCIITree = toASCIITree; 895 | p.createChildNode = createChildNode; 896 | p.appendChild = appendChild; 897 | p.addAssertion = addAssertion; 898 | p.addSkip = addSkip; 899 | p.addFailure = addFailure; 900 | p.addError = addError; 901 | p.startTest = startTest; 902 | p.stopTest = stopTest; 903 | p.pauseTest = pauseTest; 904 | p.restartTest = restartTest; 905 | p.startSuite = startSuite; 906 | p.stopSuite = stopSuite; 907 | p.start = start; 908 | p.stop = stop; 909 | p.toString = toString; 910 | })(TestResultTree.prototype); 911 | Evidence.TestResultTree = TestResultTree; 912 | var Console = {}; 913 | 914 | function Logger(level) { 915 | if (typeof level !== 'undefined') { 916 | this.level = level; 917 | } 918 | } 919 | 920 | Logger.displayName = 'Logger'; 921 | Logger.LEVELS = ['NOTSET', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']; 922 | Logger.CRITICAL = 5; 923 | Logger.ERROR = 4; 924 | Logger.WARN = 3; 925 | Logger.INFO = 2; 926 | Logger.DEBUG = 1; 927 | Logger.NOTSET = 0; 928 | 929 | (function(p) { 930 | function critical(template, params) { 931 | this.log(Logger.CRITICAL, template, params); 932 | } 933 | 934 | function error(template, params) { 935 | this.log(Logger.ERROR, template, params); 936 | } 937 | 938 | function warn(template, params) { 939 | this.log(Logger.WARN, template, params); 940 | } 941 | 942 | function info(template, params) { 943 | this.log(Logger.INFO, template, params); 944 | } 945 | 946 | function debug(template, params) { 947 | this.log(Logger.DEBUG, template, params); 948 | } 949 | 950 | function log(level, template, params) { 951 | level = level || Logger.NOTSET; 952 | var c = global.console; 953 | 954 | var method = Logger.LEVELS[level].toLowerCase(); 955 | if (method === 'critical') { method = 'error'; } 956 | method = (method in c) ? method : 'log'; 957 | 958 | if (level >= this.level) { 959 | if (params) { 960 | params = params.slice(0); 961 | params.unshift(template); 962 | c[method].apply(c, params); 963 | } else { 964 | c[method](template); 965 | } 966 | } 967 | } 968 | 969 | p.log = log; 970 | p.critical = critical; 971 | p.error = error; 972 | p.warn = warn; 973 | p.info = info; 974 | p.debug = debug; 975 | p.level = 0; 976 | })(Logger.prototype); 977 | Console.Logger = Logger; 978 | function PopupLogger(level) { 979 | Logger.call(this, level); 980 | } 981 | 982 | chain(PopupLogger, Logger); 983 | PopupLogger.displayName = 'PopupLogger'; 984 | 985 | (function(p) { 986 | var BASIC_STYLES = 'color: #333; background-color: #fff; font-family: monospace; border-bottom: 1px solid #ccc;'; 987 | var STYLES = { 988 | WARN: 'color: #000; background-color: #fc6;', 989 | ERROR: 'color: #f00; background-color: #fcc;', 990 | CRITICAL: 'color: #fff; background-color: #000;' 991 | }; 992 | 993 | function _cleanup(html) { 994 | return html.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&').replace(/[\n\r]+/, '
'); 995 | } 996 | 997 | function _makePopup() { 998 | var popup = global.open('','popup','height=400,width=400'); 999 | var doc = popup.document; 1000 | doc.write('\ 1001 | \ 1002 | \ 1003 | \ 1004 | Console\ 1005 | \ 1006 |
\ 1007 | '); 1008 | doc.close(); 1009 | popup.focus(); 1010 | return popup; 1011 | } 1012 | 1013 | function _appendLine(level, msg) { 1014 | this.popup = this.popup || this._makePopup(); 1015 | var levelName = Logger.LEVELS[level]; 1016 | 1017 | var html = '
'; 1021 | if (level > Logger.INFO) { 1022 | html += ''; 1023 | html += levelName; 1024 | html += ': '; 1025 | } 1026 | html += _cleanup(msg); 1027 | html += '
'; 1028 | var doc = this.popup.document, 1029 | div = doc.createElement('div'); 1030 | div.innerHTML = html; 1031 | html = div.firstChild; 1032 | div = null; 1033 | doc.getElementById('evidence_console').appendChild(html); 1034 | } 1035 | 1036 | function log(level, msg, params) { 1037 | level = level || Logger.NOTSET; 1038 | if (level >= this.level) { 1039 | if (params) { 1040 | msg = UI.printf(msg, params); 1041 | } 1042 | this._appendLine(level, msg); 1043 | } 1044 | } 1045 | 1046 | p.log = log; 1047 | p._makePopup = _makePopup; 1048 | p._appendLine = _appendLine; 1049 | })(PopupLogger.prototype); 1050 | Console.PopupLogger = PopupLogger; 1051 | function CommandLineLogger(level) { 1052 | Logger.call(this, level); 1053 | } 1054 | 1055 | chain(CommandLineLogger, Logger); 1056 | CommandLineLogger.displayName = 'CommandLineLogger'; 1057 | 1058 | (function(p) { 1059 | 1060 | function log(level, msg, params) { 1061 | level = level || Logger.NOTSET; 1062 | if (level >= this.level) { 1063 | var prefix = ''; 1064 | if (level > Logger.INFO) { 1065 | prefix = Logger.LEVELS[level]+ ': '; 1066 | } 1067 | if (params) { 1068 | msg = UI.printf(msg, params); 1069 | } 1070 | global.print(prefix + msg); 1071 | } 1072 | } 1073 | 1074 | p.log = log; 1075 | })(CommandLineLogger.prototype); 1076 | Console.CommandLineLogger = CommandLineLogger; 1077 | function ConsoleTestRunner(logger) { 1078 | TestRunner.call(this); 1079 | this.logger = logger; 1080 | } 1081 | 1082 | chain(ConsoleTestRunner, TestRunner); 1083 | ConsoleTestRunner.displayName = 'ConsoleTestRunner'; 1084 | 1085 | (function(p) { 1086 | function _makeResult() { 1087 | return new ConsoleTestResult(this.logger); 1088 | } 1089 | 1090 | p._makeResult = _makeResult; 1091 | })(ConsoleTestRunner.prototype); 1092 | Console.TestRunner = ConsoleTestRunner; 1093 | function ConsoleTestResult(logger) { 1094 | TestResult.call(this); 1095 | this.logger = logger; 1096 | } 1097 | 1098 | chain(ConsoleTestResult, TestResult); 1099 | ConsoleTestResult.displayName = 'ConsoleTestResult'; 1100 | 1101 | (function(p) { 1102 | var _super = TestResult.prototype; 1103 | 1104 | function addAssertion() { 1105 | this.assertionCount++; 1106 | } 1107 | 1108 | function addSkip(testcase, msg) { 1109 | _super.addSkip.call(this, testcase, msg); 1110 | this.logger.warn('Skipping testcase ' + testcase + ': ' + msg.message); 1111 | } 1112 | 1113 | function addFailure(testcase, msg) { 1114 | _super.addFailure.call(this, testcase, msg); 1115 | this.logger.error(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); 1116 | } 1117 | 1118 | function addError(testcase, error) { 1119 | _super.addError.call(this, testcase, error); 1120 | this.logger.error(testcase + ' threw an error. ' + error); 1121 | } 1122 | 1123 | function startTest(testcase) { 1124 | _super.startTest.call(this, testcase); 1125 | this.logger.debug('Started testcase ' + testcase + '.'); 1126 | } 1127 | 1128 | function stopTest(testcase) { 1129 | this.logger.debug('Completed testcase ' + testcase + '.'); 1130 | } 1131 | 1132 | function pauseTest(testcase) { 1133 | this.logger.info('Paused testcase ' + testcase + '.'); 1134 | } 1135 | 1136 | function restartTest(testcase) { 1137 | this.logger.info('Restarted testcase ' + testcase + '.'); 1138 | } 1139 | 1140 | function startSuite(suite) { 1141 | this.logger.info('Started suite ' + suite + '.'); 1142 | } 1143 | 1144 | function stopSuite(suite) { 1145 | this.logger.info('Completed suite ' + suite + '.'); 1146 | } 1147 | 1148 | function start(t0) { 1149 | _super.start.call(this, t0); 1150 | this.logger.info('Started tests.'); 1151 | } 1152 | 1153 | function stop(t1) { 1154 | _super.stop.call(this, t1); 1155 | this.logger.info('Completed tests in ' + ((t1 - this.t0)/1000) + 's.'); 1156 | this.logger.info(this.toString() + '.'); 1157 | } 1158 | 1159 | p.addAssertion = addAssertion; 1160 | p.addSkip = addSkip; 1161 | p.addFailure = addFailure; 1162 | p.addError = addError; 1163 | p.startTest = startTest; 1164 | p.stopTest = stopTest; 1165 | p.pauseTest = pauseTest; 1166 | p.restartTest = restartTest; 1167 | p.startSuite = startSuite; 1168 | p.stopSuite = stopSuite; 1169 | p.start = start; 1170 | p.stop = stop; 1171 | })(ConsoleTestResult.prototype); 1172 | 1173 | 1174 | Console.TestResult = ConsoleTestResult; 1175 | var Web = {}; 1176 | function AbstractWidget(doc) { 1177 | this.doc = doc || document; 1178 | } 1179 | 1180 | AbstractWidget.displayName = 'Widget'; 1181 | 1182 | (function(p) { 1183 | function escapeHTML(html) { 1184 | return (html + '').replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); 1185 | } 1186 | 1187 | function toElement() { 1188 | return this.element; 1189 | } 1190 | 1191 | function appendChild(child) { 1192 | var element = child && child.toElement ? child.toElement() : child; 1193 | this.element.appendChild(element); 1194 | return child; 1195 | } 1196 | 1197 | p.appendChild = appendChild; 1198 | p.escapeHTML = escapeHTML; 1199 | p.toElement = toElement; 1200 | })(AbstractWidget.prototype); 1201 | Web.AbstractWidget = AbstractWidget; 1202 | function LabelledText(label, doc) { 1203 | AbstractWidget.call(this, doc) 1204 | this.label = label; 1205 | this.element = this.doc.createElement('p'); 1206 | } 1207 | 1208 | chain(LabelledText, AbstractWidget); 1209 | LabelledText.displayName = 'LabelledText'; 1210 | 1211 | (function(p) { 1212 | function update(content) { 1213 | content = this.escapeHTML(content); 1214 | content = TEMPLATE.replace('{{ label }}', this.label).replace('{{ content }}', content); 1215 | defer(function() { this.element.innerHTML = content; }, this); 1216 | return this; 1217 | } 1218 | 1219 | var TEMPLATE = '{{ label }}: {{ content }}'; 1220 | 1221 | p.update = update; 1222 | })(LabelledText.prototype); 1223 | Web.LabelledText = LabelledText; 1224 | function ProgressBar(width, doc) { 1225 | this.width = width; 1226 | this.level = 0; 1227 | AbstractWidget.call(this, doc); 1228 | this.build(); 1229 | } 1230 | 1231 | chain(ProgressBar, AbstractWidget); 1232 | ProgressBar.displayName = 'ProgressBar'; 1233 | 1234 | (function(p) { 1235 | function build() { 1236 | this.element = this.createDiv(this.width); 1237 | this.element.id = 'evidence_progress_bar_container'; 1238 | this.progressBar = this.createDiv(0); 1239 | this.progressBar.id = 'evidence_progress_bar'; 1240 | this.element.appendChild(this.progressBar); 1241 | return this; 1242 | } 1243 | 1244 | function createDiv(width) { 1245 | var element = this.doc.createElement('div'); 1246 | element.style.width = width + 'px'; 1247 | return element; 1248 | } 1249 | 1250 | function update(ratio) { 1251 | var value = Math.floor(ratio * this.width); 1252 | defer(function() { 1253 | this.progressBar.style.width = value + 'px'; 1254 | }, this); 1255 | return this; 1256 | } 1257 | 1258 | function setLevel(level) { 1259 | if (level > this.level) { 1260 | this.level = level; 1261 | this.progressBar.className = (Logger.LEVELS[level] || '').toLowerCase(); 1262 | } 1263 | return this; 1264 | } 1265 | 1266 | p.build = build; 1267 | p.createDiv = createDiv; 1268 | p.update = update; 1269 | p.setLevel = setLevel; 1270 | })(ProgressBar.prototype); 1271 | Web.ProgressBar = ProgressBar; 1272 | function WebGUI(doc) { 1273 | AbstractWidget.call(this, doc); 1274 | } 1275 | 1276 | chain(WebGUI, AbstractWidget); 1277 | WebGUI.displayName = 'WebGUI'; 1278 | 1279 | (function(p) { 1280 | function build() { 1281 | this.element = this.doc.createElement('div'); 1282 | this.element.id = 'evidence'; 1283 | this.appendChild(new LabelledText('User agent string').update(global.navigator.userAgent)) 1284 | this.status = this.appendChild(new LabelledText('Status').update('Idle.')); 1285 | this.progressBar = this.appendChild(new ProgressBar(300)); 1286 | this.results = this.appendChild(new LabelledText('Results')); 1287 | return this; 1288 | } 1289 | 1290 | 1291 | function updateResults(txt) { 1292 | txt = this.appendFullStop(txt); 1293 | this.results.update(txt); 1294 | return this; 1295 | } 1296 | 1297 | function updateStatus(txt) { 1298 | txt = this.appendFullStop(txt); 1299 | this.status.update(txt); 1300 | return this; 1301 | } 1302 | 1303 | function updateProgressBar(ratio) { 1304 | this.progressBar.update(ratio); 1305 | return this; 1306 | } 1307 | 1308 | function setLevel(level) { 1309 | this.progressBar.setLevel(level); 1310 | return this; 1311 | } 1312 | 1313 | function appendFullStop(txt) { 1314 | return (txt + '').replace(/\.?\s*$/, '.'); 1315 | } 1316 | 1317 | p.build = build; 1318 | p.updateResults = updateResults; 1319 | p.updateStatus = updateStatus; 1320 | p.updateProgressBar = updateProgressBar; 1321 | p.setLevel = setLevel; 1322 | p.appendFullStop = appendFullStop; 1323 | })(WebGUI.prototype); 1324 | Web.GUI = WebGUI; 1325 | function WebTestRunner(logger) { 1326 | TestRunner.call(this); 1327 | this.logger = logger; 1328 | } 1329 | 1330 | chain(WebTestRunner, TestRunner); 1331 | WebTestRunner.displayName = 'WebTestRunner'; 1332 | 1333 | (function(p) { 1334 | function _makeResult() { 1335 | return new WebTestResult(); 1336 | } 1337 | 1338 | p._makeResult = _makeResult; 1339 | })(WebTestRunner.prototype); 1340 | Web.TestRunner = WebTestRunner; 1341 | function WebTestResult(name) { 1342 | TestResultTree.call(this, name); 1343 | } 1344 | 1345 | chain(WebTestResult, TestResultTree); 1346 | WebTestResult.displayName = 'WebTestResult'; 1347 | 1348 | (function(p) { 1349 | var _super = TestResultTree.prototype; 1350 | 1351 | function addAssertion() { 1352 | _super.addAssertion.call(this); 1353 | this.gui.updateResults(this); 1354 | } 1355 | 1356 | function addSkip(testcase, msg) { 1357 | _super.addSkip.call(this, testcase, msg); 1358 | var gui = this.gui; 1359 | gui.updateResults(this); 1360 | gui.setLevel(Logger.WARN); 1361 | gui.updateStatus('Skipping testcase ' + testcase + ': ' + msg.message); 1362 | } 1363 | 1364 | function addFailure(testcase, msg) { 1365 | _super.addFailure.call(this, testcase, msg); 1366 | var gui = this.gui; 1367 | gui.updateResults(this); 1368 | gui.setLevel(Logger.ERROR); 1369 | gui.updateStatus(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); 1370 | } 1371 | 1372 | function addError(testcase, error) { 1373 | _super.addError.call(this, testcase, error); 1374 | var gui = this.gui; 1375 | gui.updateResults(this); 1376 | gui.setLevel(Logger.ERROR); 1377 | gui.updateStatus(testcase + ' threw an error. ' + error); 1378 | } 1379 | 1380 | function startTest(testcase) { 1381 | _super.startTest.call(this, testcase); 1382 | this.gui.updateStatus('Started testcase ' + testcase); 1383 | } 1384 | 1385 | function stopTest(testcase) { 1386 | _super.stopTest.call(this, testcase); 1387 | var gui = this.gui; 1388 | gui.updateProgressBar(this.testCount / this.size); 1389 | gui.updateStatus('Completed testcase ' + testcase); 1390 | } 1391 | 1392 | function pauseTest(testcase) { 1393 | this.gui.updateStatus('Paused testcase ' + testcase + '...'); 1394 | } 1395 | 1396 | function restartTest(testcase) { 1397 | this.gui.updateStatus('Restarted testcase ' + testcase); 1398 | } 1399 | 1400 | function startSuite(suite) { 1401 | _super.startSuite.call(this, suite); 1402 | if (!this.size) { this.size = suite.size(); } 1403 | this.gui.updateStatus('Started suite ' + suite); 1404 | } 1405 | 1406 | function stopSuite(suite) { 1407 | _super.stopSuite.call(this, suite); 1408 | this.gui.updateStatus('Completed suite ' + suite); 1409 | } 1410 | 1411 | function start(t0) { 1412 | _super.start.call(this, t0); 1413 | var gui = new WebGUI(document); 1414 | this.gui = gui; 1415 | document.body.appendChild(gui.build().toElement()); 1416 | gui.updateResults(this); 1417 | } 1418 | 1419 | function stop(t1) { 1420 | _super.stop.call(this, t1); 1421 | this.gui.updateStatus('Completed tests in ' + ((t1 - this.t0)/1000) + 's'); 1422 | } 1423 | 1424 | p.addAssertion = addAssertion; 1425 | p.addSkip = addSkip; 1426 | p.addFailure = addFailure; 1427 | p.addError = addError; 1428 | p.startTest = startTest; 1429 | p.stopTest = stopTest; 1430 | p.pauseTest = pauseTest; 1431 | p.restartTest = restartTest; 1432 | p.startSuite = startSuite; 1433 | p.stopSuite = stopSuite; 1434 | p.start = start; 1435 | p.stop = stop; 1436 | })(WebTestResult.prototype); 1437 | 1438 | 1439 | Web.TestResult = WebTestResult; 1440 | var UI = (function() { 1441 | function printf(template, args, inspector) { 1442 | var parts = [], 1443 | regexp = /(^%|.%)([a-zA-Z])/, 1444 | args = args.splice(0); // clone args 1445 | 1446 | inspector = inspector || String; 1447 | 1448 | if (template.length <= 0) { 1449 | return ''; 1450 | } 1451 | while (m = regexp.exec(template)) { 1452 | var match = m[0], index = m.index, type, arg; 1453 | 1454 | if (match.indexOf('%%') === 0) { 1455 | parts.push(template.substr(0, index)); 1456 | parts.push(match.substr(1)); 1457 | } else { 1458 | parts.push(template.substr(0, match.indexOf('%' === 0) ? index + 1 : index)); 1459 | type = m[2]; 1460 | arg = args.shift(); 1461 | arg = inspector(arg, type); 1462 | parts.push(arg); 1463 | } 1464 | template = template.substr(index + match.length); 1465 | } 1466 | parts.push(template); 1467 | return parts.join(''); 1468 | } 1469 | 1470 | return { 1471 | printf: printf, 1472 | Console: Console, 1473 | Web: Web 1474 | }; 1475 | })(); 1476 | Evidence.UI = UI; 1477 | 1478 | var defaultLoader = new TestLoader(); 1479 | Evidence.defaultLoader = defaultLoader; 1480 | 1481 | global.Evidence = Evidence; 1482 | 1483 | if (global.location) { 1484 | global.onload = function() { 1485 | if (typeof originalOnload === 'function') { 1486 | originalOnload.call(global); 1487 | } 1488 | AutoRunner.run(); 1489 | }; 1490 | } else if (global.arguments) { 1491 | var runtime = java.lang.Runtime.getRuntime(); 1492 | var thread = new java.lang.Thread(function() { 1493 | AutoRunner.run(); 1494 | }); 1495 | runtime.addShutdownHook(thread); 1496 | } 1497 | 1498 | })(this); 1499 | -------------------------------------------------------------------------------- /src/assertion_helpers/assertion_failed_error.js: -------------------------------------------------------------------------------- 1 | function AssertionFailedError(message, template, args) { 2 | this.message = message; 3 | this.template = template || ''; 4 | this.args = args; 5 | } 6 | 7 | AssertionFailedError.displayName = 'AssertionFailedError'; 8 | 9 | (function(p) { 10 | p.name = 'AssertionFailedError'; 11 | })(AssertionFailedError.prototype); 12 | -------------------------------------------------------------------------------- /src/assertion_helpers/assertion_message.js: -------------------------------------------------------------------------------- 1 | function AssertionMessage(message, template, args) { 2 | this.message = message.replace(/%/g, '%%'); 3 | this.template = template || ''; 4 | this.args = args; 5 | } 6 | 7 | AssertionMessage.displayName = 'AssertionMessage'; 8 | 9 | (function(p) { 10 | function toString() { 11 | return UI.printf(this.message + this.template, this.args); 12 | } 13 | p.toString = toString; 14 | })(AssertionMessage.prototype); 15 | -------------------------------------------------------------------------------- /src/assertion_helpers/assertion_skipped_error.js: -------------------------------------------------------------------------------- 1 | function AssertionSkippedError(message) { 2 | this.message = message; 3 | } 4 | 5 | AssertionSkippedError.displayName = 'AssertionSkippedError'; 6 | 7 | (function(p) { 8 | p.name = 'AssertionSkippedError'; 9 | })(AssertionSkippedError.prototype); 10 | -------------------------------------------------------------------------------- /src/assertions.js: -------------------------------------------------------------------------------- 1 | //= require "assertion_helpers/assertion_skipped_error" 2 | Evidence.AssertionSkippedError = AssertionSkippedError; 3 | //= require "assertion_helpers/assertion_failed_error" 4 | Evidence.AssertionFailedError = AssertionFailedError; 5 | //= require "assertion_helpers/assertion_message" 6 | Evidence.AssertionMessage = AssertionMessage; 7 | 8 | var Assertions = (function() { 9 | function _assertExpression(expression, message, template) { 10 | if (expression) { 11 | this.addAssertion(); 12 | } else { 13 | var args = Array.prototype.slice.call(arguments, 3); 14 | throw new AssertionFailedError(message, template, args); 15 | } 16 | } 17 | 18 | function skip(message) { 19 | throw new AssertionSkippedError(message || 'Skipped!'); 20 | } 21 | 22 | function fail(message) { 23 | this._assertExpression(false, message || 'Flunked!'); 24 | } 25 | 26 | function assert(test, message) { 27 | this._assertExpression( 28 | !!test, 29 | message || 'Failed assertion.', 30 | 'Expected %o to evaluate to true.', test 31 | ); 32 | } 33 | 34 | function refute(test, message) { 35 | this._assertExpression( 36 | !test, 37 | message || 'Failed refutation.', 38 | 'Expected %o to evaluate to false.', test 39 | ); 40 | } 41 | 42 | function assertTrue(test, message) { 43 | this._assertExpression( 44 | (test === true), 45 | message || 'Failed assertion.', 46 | 'Expected %o to be true.', test 47 | ); 48 | } 49 | 50 | function refuteTrue(test, message) { 51 | this._assertExpression( 52 | (test !== true), 53 | message || 'Failed refutation.', 54 | 'Expected %o to not be true.', test 55 | ); 56 | } 57 | 58 | function assertNull(test, message) { 59 | this._assertExpression( 60 | (test === null), 61 | message || 'Failed assertion.', 62 | 'Expected %o to be null.', test 63 | ); 64 | } 65 | 66 | function refuteNull(test, message) { 67 | this._assertExpression( 68 | (test !== null), 69 | message || 'Failed refutation.', 70 | 'Expected %o to not be null.', test 71 | ); 72 | } 73 | 74 | function assertUndefined(test, message) { 75 | this._assertExpression( 76 | (typeof test === 'undefined'), 77 | message || 'Failed assertion.', 78 | 'Expected %o to be undefined.', test 79 | ); 80 | } 81 | 82 | function refuteUndefined(test, message) { 83 | this._assertExpression( 84 | (typeof test !== 'undefined'), 85 | message || 'Failed refutation.', 86 | 'Expected %o to not be undefined.', test 87 | ); 88 | } 89 | 90 | function assertFalse(test, message) { 91 | this._assertExpression( 92 | (test === false), 93 | message || 'Failed assertion.', 94 | 'Expected %o to be false.', test 95 | ); 96 | } 97 | 98 | function refuteFalse(test, message) { 99 | this._assertExpression( 100 | (test !== false), 101 | message || 'Failed refutation.', 102 | 'Expected %o to not be false.', test 103 | ); 104 | } 105 | 106 | function assertEqual(expected, actual, message) { 107 | this._assertExpression( 108 | (expected == actual), 109 | message || 'Failed assertion.', 110 | 'Expected %o to be == to %o.', actual, expected 111 | ); 112 | } 113 | 114 | function refuteEqual(expected, actual, message) { 115 | this._assertExpression( 116 | (expected != actual), 117 | message || 'Failed refutation.', 118 | 'Expected %o to be != to %o.', actual, expected 119 | ); 120 | } 121 | 122 | function assertIdentical(expected, actual, message) { 123 | this._assertExpression( 124 | (expected === actual), 125 | message || 'Failed assertion.', 126 | 'Expected %o to be === to %o.', actual, expected 127 | ); 128 | } 129 | 130 | function refuteIdentical(expected, actual, message) { 131 | this._assertExpression( 132 | (expected !== actual), 133 | message || 'Failed refutation.', 134 | 'Expected %o to be !== to %o.', actual, expected 135 | ); 136 | } 137 | 138 | function assertIn(property, object, message) { 139 | this._assertExpression( 140 | (property in object), 141 | message || 'Failed assertion.', 142 | 'Expected "%s" to be a property of %o.', property, object 143 | ); 144 | } 145 | 146 | function refuteIn(property, object, message) { 147 | this._assertExpression( 148 | !(property in object), 149 | message || 'Failed refutation.', 150 | 'Expected "%s" to not be a property of %o.', property, object 151 | ); 152 | } 153 | 154 | return { 155 | _assertExpression: _assertExpression, 156 | skip: skip, 157 | assert: assert, 158 | refute: refute, 159 | assertNot: refute, 160 | assertTrue: assertTrue, 161 | assertNull: assertNull, 162 | assertUndefined: assertUndefined, 163 | assertFalse: assertFalse, 164 | assertIdentical: assertIdentical, 165 | refuteIdentical: refuteIdentical, 166 | assertEqual: assertEqual, 167 | refuteEqual: refuteEqual, 168 | assertIn: assertIn, 169 | refuteIn: refuteIn, 170 | fail: fail, 171 | flunk: fail 172 | }; 173 | })(); -------------------------------------------------------------------------------- /src/auto_runner.js: -------------------------------------------------------------------------------- 1 | function AutoRunner() { 2 | if (global.console && global.console.log) { 3 | this.logger = Logger; 4 | } else if (Object.prototype.toString.call(global.environment) === '[object Environment]' && global.print) { 5 | // most probably Rhino 6 | this.logger = CommandLineLogger; 7 | } else { 8 | this.logger = PopupLogger; 9 | } 10 | this.autoRun = true; 11 | this.verbosity = Logger.INFO; 12 | this.runner = ConsoleTestRunner; 13 | //this.root = '/'; 14 | //this.pages = []; // ???? 15 | } 16 | 17 | (function() { 18 | function run(options) { 19 | var autoRunner = new this(); 20 | options = options || autoRunner.retrieveOptions(); 21 | autoRunner.processOptions(options); 22 | if (autoRunner.autoRun) { autoRunner.run() }; 23 | } 24 | 25 | AutoRunner.run = run; 26 | AutoRunner.displayName = 'AutoRunner'; 27 | AutoRunner.LOGGERS = { 28 | console: Logger, 29 | popup: PopupLogger, 30 | command_line: CommandLineLogger 31 | }; 32 | 33 | AutoRunner.RUNNERS = { 34 | console: ConsoleTestRunner 35 | }; 36 | })(); 37 | 38 | (function(p) { 39 | function run() { 40 | var logger = new this.logger(this.verbosity), 41 | runner = new this.runner(logger), 42 | suite = defaultLoader.loadRegisteredTestCases(); 43 | if (suite._tests.length <= 1) { 44 | suite = suite._tests[0]; 45 | } 46 | return runner.run(suite); 47 | } 48 | 49 | function processQueryString(str) { 50 | var results = {}; 51 | str = (str + '').match(/^(?:[^?#]*\?)([^#]+?)(?:#.*)?$/); 52 | str = str && str[1]; 53 | 54 | if (!str) { return results; } 55 | 56 | var pairs = str.split('&'), 57 | length = pairs.length; 58 | if (!length) { return results; } 59 | 60 | for (var i = 0; i < length; i++) { 61 | var pair = pairs[i].split('='), 62 | key = decodeURIComponent(pair[0]), 63 | value = pair[1]; 64 | value = value ? decodeURIComponent(value) : true; 65 | results[key] = value; 66 | } 67 | return results; 68 | } 69 | 70 | function processArguments(args) { // RHINO 71 | var results = {}; 72 | 73 | for (var i = 0; i < args.length; i++) { 74 | var arg = args[i]; 75 | if (arg.indexOf('-') === 0) { 76 | var value = args[i + 1]; 77 | if (value && value.indexOf('-') !== 0) { 78 | i++; 79 | } else { 80 | value = true; 81 | } 82 | results[arg.substr(1)] = value; 83 | } 84 | } 85 | return results; 86 | } 87 | 88 | function retrieveOptions() { 89 | if (global.location) { 90 | return this.processQueryString(global.location); 91 | } 92 | if (global.arguments) { 93 | return this.processArguments(global.arguments); 94 | } 95 | return {}; 96 | } 97 | 98 | function processOptions(options) { 99 | for(var key in options) { 100 | var value = options[key]; 101 | switch(key) { 102 | case 'timeout': 103 | TestCase.defaultTimeout = global.parseFloat(value) * 1000; 104 | break; 105 | case 'run': 106 | this.autoRun = value === 'false' ? false : true; 107 | break; 108 | case 'logger': 109 | this.logger = AutoRunner.LOGGERS[value]; 110 | break; 111 | case 'verbosity': 112 | var i = global.parseInt(value); 113 | this.verbosity = global.isNaN(i) ? Logger[value] : i; 114 | break; 115 | case 'runner': 116 | this.runner = AutoRunner.RUNNERS[value]; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | p.run = run; 123 | p.processQueryString = processQueryString; 124 | p.processArguments = processArguments; 125 | p.retrieveOptions = retrieveOptions; 126 | p.processOptions = processOptions; 127 | })(AutoRunner.prototype); -------------------------------------------------------------------------------- /src/constants.yml: -------------------------------------------------------------------------------- 1 | EVIDENCE_VERSION: 0.6 -------------------------------------------------------------------------------- /src/evidence.js: -------------------------------------------------------------------------------- 1 | /* evidence.js, version <%= EVIDENCE_VERSION %> 2 | * 3 | * Copyright (c) 2009 Tobie Langel (http://tobielangel.com) 4 | * 5 | * evidence.js is freely distributable under the terms of an MIT-style license. 6 | *--------------------------------------------------------------------------*/ 7 | 8 | (function(global) { 9 | var originalEvidence = global.Evidence, 10 | originalOnload = global.onload; 11 | 12 | /** 13 | * Evidence(tests) -> Evidence.TestResult 14 | * Library namespace. Doubles-up as a shortcut for 15 | * running tests and collect tests results. 16 | * 17 | **/ 18 | function Evidence() { 19 | TestCase.extend.apply(TestCase, arguments); 20 | } 21 | 22 | /** 23 | * Evidence.noConflict() -> evidence 24 | * Restores the global namespace and returns the evidence object. 25 | * 26 | **/ 27 | function noConflict() { 28 | global.Evidence = originalEvidence; 29 | return Evidence; 30 | } 31 | 32 | Evidence.noConflict = noConflict; 33 | Evidence.VERSION = '<%= EVIDENCE_VERSION %>'; 34 | 35 | //= require "helpers" 36 | //= require "assertions" 37 | Evidence.Assertions = Assertions; 38 | //= require "test_case" 39 | Evidence.TestCase = TestCase; 40 | //= require "test_suite" 41 | Evidence.TestSuite = TestSuite; 42 | //= require "test_runner" 43 | Evidence.TestRunner = TestRunner; 44 | //= require "test_loader" 45 | Evidence.TestLoader = TestLoader; 46 | //= require "auto_runner" 47 | Evidence.AutoRunner = AutoRunner; 48 | //= require "test_result" 49 | Evidence.TestResult = TestResult; 50 | //= require "test_result_tree" 51 | Evidence.TestResultTree = TestResultTree; 52 | //= require "ui" 53 | Evidence.UI = UI; 54 | 55 | var defaultLoader = new TestLoader(); 56 | Evidence.defaultLoader = defaultLoader; 57 | 58 | global.Evidence = Evidence; 59 | 60 | if (global.location) { 61 | global.onload = function() { 62 | if (typeof originalOnload === 'function') { 63 | originalOnload.call(global); 64 | } 65 | AutoRunner.run(); 66 | }; 67 | } else if (global.arguments) { 68 | var runtime = java.lang.Runtime.getRuntime(); 69 | var thread = new java.lang.Thread(function() { 70 | AutoRunner.run(); 71 | }); 72 | runtime.addShutdownHook(thread); 73 | } 74 | 75 | })(this); 76 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | var FILE_REGEXP = /.*?\/(\w+\.html)(.*)/; 2 | 3 | function getNameFromFile() { 4 | return (global.location || '').toString().replace(FILE_REGEXP, '$1'); 5 | } 6 | 7 | function chain(subclass, superclass) { 8 | function Subclass() {} 9 | Subclass.prototype = superclass.prototype; 10 | subclass.prototype = new Subclass(); 11 | subclass.prototype.constructor = subclass; 12 | return subclass; 13 | } 14 | 15 | function defer(block, context) { 16 | if ('setTimeout' in global) { 17 | global.setTimeout(function() { 18 | block.call(context); 19 | }, 0); 20 | } else { 21 | block.call(context); 22 | } 23 | } -------------------------------------------------------------------------------- /src/test_case.js: -------------------------------------------------------------------------------- 1 | function TestCase(methodName) { 2 | this._methodName = methodName; 3 | this.name = methodName; 4 | } 5 | 6 | (function() { 7 | function extend(name, methods) { 8 | function TestCaseSubclass(methodName) { 9 | TestCase.call(this, methodName); 10 | } 11 | 12 | if (!methods) { 13 | methods = name; 14 | name = getNameFromFile(); 15 | } 16 | 17 | chain(TestCaseSubclass, this); 18 | TestCaseSubclass.displayName = name; 19 | TestCaseSubclass.extend = extend; 20 | 21 | for(var prop in methods) { 22 | TestCaseSubclass.prototype[prop] = methods[prop]; 23 | } 24 | TestCase.subclasses.push(TestCaseSubclass); 25 | return TestCaseSubclass; 26 | } 27 | 28 | // Mixin Assertions 29 | function AssertionsMixin() {} 30 | AssertionsMixin.prototype = Assertions; 31 | TestCase.prototype = new AssertionsMixin(); 32 | TestCase.constructor = TestCase; 33 | 34 | TestCase.displayName = 'TestCase'; 35 | TestCase.extend = extend; 36 | TestCase.subclasses = []; 37 | TestCase.defaultTimeout = 10000; 38 | })(); 39 | 40 | (function(p) { 41 | 42 | function run(result, callback) { 43 | this._result = result; 44 | this._callback = callback || function() {}; 45 | defer(function() { this.next(result); }, this); 46 | } 47 | 48 | function next(result) { 49 | try { 50 | if (this._nextAssertions) { 51 | result.resumeTest(this); 52 | this._nextAssertions(this); 53 | } else { 54 | result.startTest(this); 55 | this.setUp(this); 56 | this[this._methodName](this); 57 | } 58 | } catch(e) { 59 | this._filterException(e); 60 | } finally { 61 | if (this._paused) { 62 | result.pauseTest(this); 63 | } else { 64 | try { 65 | this.tearDown(this); 66 | } catch(e) { 67 | this._filterException(e); 68 | } finally { 69 | this._nextAssertions = null; 70 | result.stopTest(this); 71 | this._callback(result); 72 | } 73 | } 74 | } 75 | } 76 | 77 | function _filterException(e) { 78 | // Check e.name for cross-frame support. 79 | var name = e.name, 80 | result = this._result; 81 | switch(name) { 82 | case 'AssertionFailedError': 83 | result.addFailure(this, e); 84 | break; 85 | case 'AssertionSkippedError': 86 | result.addSkip(this, e); 87 | break; 88 | default: 89 | result.addError(this, e); 90 | } 91 | } 92 | 93 | function pause(assertions) { 94 | var self = this; 95 | this._paused = true; 96 | if (assertions) { self._nextAssertions = assertions; } 97 | self._timeoutId = global.setTimeout(function() { 98 | self.resume(function() { 99 | self.fail('Test timed out. Testing was not resumed after being paused.'); 100 | }); 101 | }, TestCase.defaultTimeout); 102 | } 103 | 104 | function resume(assertions) { 105 | if (this._paused) { // avoid race conditions 106 | this._paused = false; 107 | global.clearTimeout(this._timeoutId); 108 | if (assertions) { this._nextAssertions = assertions; } 109 | this.next(this._result); 110 | } 111 | } 112 | 113 | function size() { 114 | return 1; 115 | } 116 | 117 | function toString() { 118 | return this.constructor.displayName + '#' + this.name; 119 | } 120 | 121 | function addAssertion() { 122 | this._result.addAssertion(); 123 | } 124 | 125 | p.run = run; 126 | p.next = next; 127 | p.addAssertion = addAssertion; 128 | p._filterException = _filterException; 129 | p.pause = pause; 130 | p.resume = resume; 131 | p.size = size; 132 | p.toString = toString; 133 | p.setUp = function() {}; 134 | p.tearDown = function() {}; 135 | })(TestCase.prototype); 136 | -------------------------------------------------------------------------------- /src/test_loader.js: -------------------------------------------------------------------------------- 1 | function TestLoader() { 2 | } 3 | 4 | TestLoader.displayName = 'TestLoader'; 5 | 6 | (function(p) { 7 | function loadTestsFromTestCase(testcaseClass) { 8 | var suite = new TestSuite(testcaseClass.displayName), 9 | props = this.getTestCaseNames(testcaseClass); 10 | for (var i=0; i < props.length; i++) { 11 | suite.push(new testcaseClass(props[i])); 12 | } 13 | return suite; 14 | } 15 | 16 | function loadTestsFromTestCases(testcases) { 17 | var suite = new TestSuite(getNameFromFile()); 18 | for (var i = 0; i < testcases.length; i++) { 19 | var testcase = testcases[i]; 20 | var subSuite = defaultLoader.loadTestsFromTestCase(testcase); 21 | if (!subSuite.isEmpty()) { suite.push(subSuite); } 22 | } 23 | return suite; 24 | } 25 | 26 | function getTestCaseNames(testcaseClass) { 27 | var results = [], 28 | proto = testcaseClass.prototype, 29 | prefix = this.testMethodPrefix; 30 | 31 | for (var property in proto) { 32 | if (property.indexOf(prefix) === 0) { 33 | results.push(property); 34 | } 35 | } 36 | return results.sort(); 37 | } 38 | 39 | function loadRegisteredTestCases() { 40 | return loadTestsFromTestCases(TestCase.subclasses); 41 | } 42 | 43 | p.loadTestsFromTestCase = loadTestsFromTestCase; 44 | p.loadRegisteredTestCases = loadRegisteredTestCases; 45 | p.loadTestsFromTestCases = loadTestsFromTestCases; 46 | p.testMethodPrefix = 'test'; 47 | p.getTestCaseNames = getTestCaseNames; 48 | 49 | })(TestLoader.prototype); -------------------------------------------------------------------------------- /src/test_result.js: -------------------------------------------------------------------------------- 1 | function TestResult() { 2 | this.testCount = 0; 3 | this.assertionCount = 0; 4 | this.skipCount = 0; 5 | this.skips = []; 6 | this.failureCount = 0; 7 | this.failures = []; 8 | this.errors = []; 9 | this.errorCount = 0; 10 | this.testCount = 0; 11 | } 12 | 13 | TestResult.displayName = 'TestResult'; 14 | 15 | (function(p) { 16 | function addAssertion() { 17 | this.assertionCount++; 18 | } 19 | 20 | function addSkip(testcase, reason) { 21 | this.skipCount++; 22 | this.skips.push(reason); 23 | } 24 | 25 | function addFailure(testcase, reason) { 26 | this.failureCount++; 27 | this.failures.push(reason); 28 | } 29 | 30 | function addError(testcase, error) { 31 | this.errorCount++; 32 | this.errors.push(error); 33 | } 34 | 35 | function startTest(testcase) { 36 | this.testCount++; 37 | } 38 | 39 | function stopTest(testcase) {} 40 | 41 | function pauseTest(testcase) {} 42 | 43 | function resumeTest(testcase) {} 44 | 45 | function startSuite(suite) {} 46 | 47 | function stopSuite(suite) {} 48 | 49 | function loadPage(win) {} 50 | 51 | function startPage(win, suite) {} 52 | 53 | function stopPage(win) {} 54 | 55 | function start(t0) { 56 | this.t0 = t0; 57 | } 58 | 59 | function stop(t1) { 60 | this.t1 = t1; 61 | } 62 | 63 | function toString() { 64 | return this.testCount + ' tests, ' + 65 | this.assertionCount + ' assertions, ' + 66 | this.failureCount + ' failures, ' + 67 | this.errorCount + ' errors, ' + 68 | this.skipCount + ' skips'; 69 | } 70 | 71 | p.addAssertion = addAssertion; 72 | p.addSkip = addSkip; 73 | p.addFailure = addFailure; 74 | p.addError = addError; 75 | p.startTest = startTest; 76 | p.stopTest = stopTest; 77 | p.pauseTest = pauseTest; 78 | p.resumeTest = resumeTest; 79 | p.startSuite = startSuite; 80 | p.stopSuite = stopSuite; 81 | p.loadPage = loadPage; 82 | p.startPage = startPage; 83 | p.stopPage = stopPage; 84 | p.start = start; 85 | p.stop = stop; 86 | p.toString = toString; 87 | })(TestResult.prototype); -------------------------------------------------------------------------------- /src/test_result_tree.js: -------------------------------------------------------------------------------- 1 | function TestResultTree(name) { 2 | this.testCount = 0; 3 | this.assertionCount = 0; 4 | this.skipCount = 0; 5 | this.skips = []; 6 | this.failureCount = 0; 7 | this.failures = []; 8 | this.errors = []; 9 | this.errorCount = 0; 10 | this.testCount = 0; 11 | this.name = name; 12 | } 13 | 14 | chain(TestResultTree, TestResult); 15 | TestResultTree.displayName = 'TestResultTree'; 16 | 17 | (function(p) { 18 | function addAssertion() { 19 | var node = this.currentNode; 20 | do { 21 | node.assertionCount++; 22 | } while (node = node.parent); 23 | } 24 | 25 | function addSkip(testcase, reason) { 26 | var node = this.currentNode; 27 | do { 28 | node.skipCount++; 29 | node.skips.push(reason); 30 | } while (node = node.parent); 31 | } 32 | 33 | function addFailure(testcase, reason) { 34 | var node = this.currentNode; 35 | do { 36 | node.failureCount++; 37 | node.failures.push(reason); 38 | } while (node = node.parent); 39 | } 40 | 41 | function addError(testcase, error) { 42 | var node = this.currentNode; 43 | do { 44 | node.errorCount++; 45 | node.errors.push(error); 46 | } while (node = node.parent); 47 | } 48 | 49 | function startTest(testcase) { 50 | var node = this.createChildNode(testcase.name); 51 | do { 52 | node.testCount++; 53 | } while (node = node.parent); 54 | } 55 | 56 | function stopTest(testcase) { 57 | this.currentNode = this.currentNode.parent || this; 58 | } 59 | 60 | function startSuite(suite) { 61 | this.createChildNode(suite.name); 62 | } 63 | 64 | function stopSuite(suite) { 65 | this.currentNode = this.currentNode.parent || this; 66 | } 67 | 68 | function start() { 69 | this.t0 = new Date(); 70 | this.currentNode = this; 71 | } 72 | 73 | function stop() { 74 | this.currentNode = null; 75 | this.t1 = new Date(); 76 | } 77 | 78 | function toString() { 79 | var results = ''; 80 | if (this.children) { 81 | results += this.testCount; 82 | results += ' tests, '; 83 | } 84 | return results + 85 | this.assertionCount + ' assertions, ' + 86 | this.failureCount + ' failures, ' + 87 | this.errorCount + ' errors, ' + 88 | this.skipCount + ' skips'; 89 | } 90 | 91 | function createChildNode(name) { 92 | var node = new this.constructor(name); 93 | this.currentNode.appendChild(node); 94 | this.currentNode = node; 95 | return node; 96 | } 97 | 98 | function appendChild(child) { 99 | this.children = this.children || []; 100 | this.children.push(child); 101 | child.parent = this; 102 | } 103 | 104 | p.createChildNode = createChildNode; 105 | p.appendChild = appendChild; 106 | p.addAssertion = addAssertion; 107 | p.addSkip = addSkip; 108 | p.addFailure = addFailure; 109 | p.addError = addError; 110 | p.startTest = startTest; 111 | p.stopTest = stopTest; 112 | p.startSuite = startSuite; 113 | p.stopSuite = stopSuite; 114 | p.start = start; 115 | p.stop = stop; 116 | p.toString = toString; 117 | })(TestResultTree.prototype); -------------------------------------------------------------------------------- /src/test_runner.js: -------------------------------------------------------------------------------- 1 | function TestRunner() { 2 | } 3 | 4 | TestRunner.displayName = 'TestRunner'; 5 | 6 | (function(p) { 7 | function run(suite) { 8 | var self = this, 9 | result = self._makeResult(); 10 | Evidence.currentResult = result; 11 | this._suite = suite; 12 | self.start(result); 13 | suite.run(result, function(result) { 14 | self.stop(result); 15 | }); 16 | return result; 17 | } 18 | 19 | function _makeResult() { 20 | return new TestResult(); 21 | } 22 | 23 | function start(result) { 24 | result.start(); 25 | } 26 | 27 | function stop(result) { 28 | result.stop(); 29 | } 30 | 31 | p.start = start; 32 | p.stop = stop; 33 | p.run = run; 34 | p._makeResult = _makeResult; 35 | })(TestRunner.prototype); 36 | -------------------------------------------------------------------------------- /src/test_suite.js: -------------------------------------------------------------------------------- 1 | function TestSuite(name, tests) { 2 | this.name = name; 3 | this._tests = []; 4 | if (tests) { 5 | this.push.apply(this, tests); 6 | } 7 | } 8 | 9 | TestSuite.displayName = 'TestSuite'; 10 | 11 | (function(p) { 12 | function run(result, callback) { 13 | this._index = 0; 14 | this._callback = callback || function() {}; 15 | result.startSuite(this); 16 | this.next(result); 17 | return result; 18 | } 19 | 20 | function next(result) { 21 | var self = this, 22 | next = self._tests[self._index]; 23 | if (next) { 24 | self._index++; 25 | next.run(result, function(result) { 26 | self.next(result); 27 | }); 28 | } else { 29 | result.stopSuite(self); 30 | self._callback(result); 31 | } 32 | } 33 | 34 | function push() { 35 | for (var i = 0, length = arguments.length; i < length; i++) { 36 | this._tests.push(arguments[i]); 37 | } 38 | } 39 | 40 | function addTest(test) { 41 | this._tests.push(test); 42 | } 43 | 44 | function addTests(tests) { 45 | for (var i = 0, length = tests.length; i < length; i++) { 46 | this._tests.push(tests[i]); 47 | } 48 | } 49 | 50 | function size() { 51 | var tests = this._tests, 52 | length = tests.length, 53 | sum = 0; 54 | 55 | for (var i = 0; i < length; i++) { 56 | sum += tests[i].size(); 57 | } 58 | return sum; 59 | } 60 | 61 | function isEmpty() { 62 | return this.size() === 0; 63 | } 64 | 65 | function toString() { 66 | return this.name; 67 | } 68 | 69 | p.run = run; 70 | p.next = next; 71 | p.push = push; 72 | p.size = size; 73 | p.isEmpty = isEmpty; 74 | p.toString = toString; 75 | })(TestSuite.prototype); -------------------------------------------------------------------------------- /src/ui.js: -------------------------------------------------------------------------------- 1 | //= require "ui/console" 2 | //= require "ui/web" 3 | //= require "ui/ascii_view_builder" 4 | 5 | var UI = (function() { 6 | function printf(template, args, inspector) { 7 | var parts = [], 8 | regexp = /(^%|.%)([a-zA-Z])/, 9 | args = args.splice(0); // clone args 10 | 11 | inspector = inspector || String; 12 | 13 | if (template.length <= 0) { 14 | return ''; 15 | } 16 | while (m = regexp.exec(template)) { 17 | var match = m[0], index = m.index, type, arg; 18 | 19 | if (match.indexOf('%%') === 0) { 20 | parts.push(template.substr(0, index)); 21 | parts.push(match.substr(1)); 22 | } else { 23 | parts.push(template.substr(0, match.indexOf('%' === 0) ? index + 1 : index)); 24 | type = m[2]; 25 | arg = args.shift(); 26 | arg = inspector(arg, type); 27 | parts.push(arg); 28 | } 29 | template = template.substr(index + match.length); 30 | } 31 | parts.push(template); 32 | return parts.join(''); 33 | } 34 | 35 | return { 36 | printf: printf, 37 | Console: Console, 38 | Web: Web 39 | }; 40 | })(); -------------------------------------------------------------------------------- /src/ui/ascii_view_builder.js: -------------------------------------------------------------------------------- 1 | function AsciiViewBuilder(result) { 2 | this.prefix = ''; 3 | this._result = result; 4 | } 5 | 6 | AsciiViewBuilder.name = AsciiViewBuilder.displayName = 'AsciiViewBuilder'; 7 | 8 | (function(p) { 9 | 10 | function draw() { 11 | return this._build(this._result); 12 | } 13 | 14 | function _build(r) { 15 | var rString = r.toString(), 16 | max = 100 - rString.length - this.prefix.length, 17 | str = r.name || 'Anonymous TestSuite'; 18 | 19 | str = this._truncate(str, max); 20 | str += ' ' + this._times('.', max - str.length) + ' '; 21 | str += rString; 22 | str += this._displayStatus(r) 23 | str += '\n'; 24 | 25 | var length = r.children ? r.children.length : 0, 26 | i; 27 | for (i = 0; i < length; i++) { 28 | if (i === length - 1) { // last 29 | str += this._buildChild(' ', '\'-- ', r.children[i], this.prefix + '\n'); 30 | } else { 31 | str += this._buildChild('| ', '|-- ', r.children[i]); 32 | } 33 | } 34 | return str; 35 | } 36 | 37 | function _buildChild(modifier, prefix, child, suffix) { 38 | var str, original = this.prefix; 39 | suffix = suffix || ''; 40 | this.prefix += modifier; 41 | str = original + prefix + this._build(child) + suffix; 42 | this.prefix = original; 43 | return str; 44 | } 45 | 46 | function _truncate(str, size) { 47 | size = Math.max(size, 0); 48 | if (str.length > size) { 49 | return '...' + str.substr(str.length - size + 3); 50 | } 51 | return str; 52 | } 53 | 54 | function _times(c, times) { 55 | var str = ''; 56 | for (var i = 0; i < times; i++) { str += c; } 57 | return str; 58 | } 59 | 60 | function _displayStatus(r) { 61 | if (r.children) { return ''; } 62 | if (r.errorCount > 0) { return ' E'; } 63 | if (r.failureCount > 0) { return ' F'; } 64 | if (r.skipCount > 0) { return ' S'; } 65 | return ''; 66 | } 67 | 68 | p.draw = draw; 69 | p._build = _build; 70 | p._buildChild = _buildChild; 71 | p._displayStatus = _displayStatus; 72 | p._times = _times; 73 | p._truncate = _truncate; 74 | })(AsciiViewBuilder.prototype); -------------------------------------------------------------------------------- /src/ui/console.js: -------------------------------------------------------------------------------- 1 | var Console = {}; 2 | 3 | //= require "console/logger" 4 | Console.Logger = Logger; 5 | //= require "console/popup_logger" 6 | Console.PopupLogger = PopupLogger; 7 | //= require "console/command_line_logger" 8 | Console.CommandLineLogger = CommandLineLogger; 9 | //= require "console/test_runner" 10 | Console.TestRunner = ConsoleTestRunner; 11 | //= require "console/test_result" 12 | Console.TestResult = ConsoleTestResult; -------------------------------------------------------------------------------- /src/ui/console/command_line_logger.js: -------------------------------------------------------------------------------- 1 | function CommandLineLogger(level) { 2 | Logger.call(this, level); 3 | } 4 | 5 | chain(CommandLineLogger, Logger); 6 | CommandLineLogger.displayName = 'CommandLineLogger'; 7 | 8 | (function(p) { 9 | 10 | function log(level, msg, params) { 11 | level = level || Logger.NOTSET; 12 | if (level >= this.level) { 13 | var prefix = this.prefix; 14 | if (level > Logger.INFO) { 15 | prefix += Logger.LEVELS[level]+ ': '; 16 | } 17 | if (params) { 18 | msg = UI.printf(msg, params); 19 | } 20 | global.print(prefix + msg); 21 | } 22 | } 23 | 24 | p.log = log; 25 | })(CommandLineLogger.prototype); -------------------------------------------------------------------------------- /src/ui/console/logger.js: -------------------------------------------------------------------------------- 1 | function Logger(level) { 2 | if (typeof level !== 'undefined') { 3 | this.level = level; 4 | } 5 | } 6 | 7 | Logger.displayName = 'Logger'; 8 | Logger.LEVELS = ['NOTSET', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']; 9 | Logger.CRITICAL = 5; 10 | Logger.ERROR = 4; 11 | Logger.WARN = 3; 12 | Logger.INFO = 2; 13 | Logger.DEBUG = 1; 14 | Logger.NOTSET = 0; 15 | 16 | (function(p) { 17 | function critical(template, params) { 18 | this.log(Logger.CRITICAL, template, params); 19 | } 20 | 21 | function error(template, params) { 22 | this.log(Logger.ERROR, template, params); 23 | } 24 | 25 | function warn(template, params) { 26 | this.log(Logger.WARN, template, params); 27 | } 28 | 29 | function info(template, params) { 30 | this.log(Logger.INFO, template, params); 31 | } 32 | 33 | function debug(template, params) { 34 | this.log(Logger.DEBUG, template, params); 35 | } 36 | 37 | function group(title) { 38 | this.prefix += ' '; 39 | //global.console.group(title); 40 | } 41 | 42 | function groupEnd() { 43 | //global.console.groupEnd(); 44 | this.prefix = this.prefix.substr(0, this.prefix.length - 4); 45 | } 46 | 47 | function log(level, template, params) { 48 | level = level || Logger.NOTSET; 49 | var c = global.console; 50 | 51 | var method = Logger.LEVELS[level].toLowerCase(); 52 | if (method === 'critical') { method = 'error'; } 53 | method = (method in c) ? method : 'log'; 54 | template = this.prefix + template; 55 | if (level >= this.level) { 56 | if (params) { 57 | params = params.slice(0); 58 | params.unshift(template); 59 | c[method].apply(c, params); 60 | } else { 61 | c[method](template); 62 | } 63 | } 64 | } 65 | 66 | p.prefix = ''; 67 | p.group = group; 68 | p.groupEnd = groupEnd; 69 | p.log = log; 70 | p.critical = critical; 71 | p.error = error; 72 | p.warn = warn; 73 | p.info = info; 74 | p.debug = debug; 75 | p.level = 0; 76 | })(Logger.prototype); -------------------------------------------------------------------------------- /src/ui/console/popup_logger.js: -------------------------------------------------------------------------------- 1 | function PopupLogger(level) { 2 | Logger.call(this, level); 3 | } 4 | 5 | chain(PopupLogger, Logger); 6 | PopupLogger.displayName = 'PopupLogger'; 7 | 8 | (function(p) { 9 | var BASIC_STYLES = 'color: #333; background-color: #fff; font-family: monospace; border-bottom: 1px solid #ccc;'; 10 | var STYLES = { 11 | WARN: 'color: #000; background-color: #fc6;', 12 | ERROR: 'color: #f00; background-color: #fcc;', 13 | CRITICAL: 'color: #fff; background-color: #000;' 14 | }; 15 | 16 | function _cleanup(html) { 17 | return html.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&').replace(/[\n\r]+/, '
'); 18 | } 19 | 20 | function _makePopup() { 21 | var popup = global.open('','popup','height=400,width=400'); 22 | var doc = popup.document; 23 | doc.write('\ 24 | \ 25 | \ 26 | \ 27 | Console\ 28 | \ 29 |
\ 30 | '); 31 | doc.close(); 32 | popup.focus(); 33 | return popup; 34 | } 35 | 36 | function _appendLine(level, msg) { 37 | this.popup = this.popup || this._makePopup(); 38 | var levelName = Logger.LEVELS[level]; 39 | 40 | var html = '
'; 44 | if (level > Logger.INFO) { 45 | html += ''; 46 | html += levelName; 47 | html += ': '; 48 | } 49 | html += _cleanup(msg); 50 | html += '
'; 51 | var doc = this.popup.document, 52 | div = doc.createElement('div'); 53 | div.innerHTML = html; 54 | html = div.firstChild; 55 | div = null; 56 | doc.getElementById('evidence_console').appendChild(html); 57 | } 58 | 59 | function log(level, msg, params) { 60 | level = level || Logger.NOTSET; 61 | if (level >= this.level) { 62 | if (params) { 63 | msg = UI.printf(msg, params); 64 | } 65 | this._appendLine(level, msg); 66 | } 67 | } 68 | 69 | p.log = log; 70 | p._makePopup = _makePopup; 71 | p._appendLine = _appendLine; 72 | })(PopupLogger.prototype); -------------------------------------------------------------------------------- /src/ui/console/test_result.js: -------------------------------------------------------------------------------- 1 | function ConsoleTestResult(logger) { 2 | TestResult.call(this); 3 | this.logger = logger; 4 | } 5 | 6 | chain(ConsoleTestResult, TestResult); 7 | ConsoleTestResult.displayName = 'ConsoleTestResult'; 8 | 9 | (function(p) { 10 | var _super = TestResult.prototype; 11 | 12 | function addAssertion() { 13 | this.assertionCount++; 14 | } 15 | 16 | function addSkip(testcase, msg) { 17 | _super.addSkip.call(this, testcase, msg); 18 | this.logger.warn('Skipping testcase ' + testcase + ': ' + msg.message); 19 | } 20 | 21 | function addFailure(testcase, msg) { 22 | _super.addFailure.call(this, testcase, msg); 23 | this.logger.error(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); 24 | } 25 | 26 | function addError(testcase, error) { 27 | _super.addError.call(this, testcase, error); 28 | this.logger.error(testcase + ' threw an error. ' + error); 29 | } 30 | 31 | function startTest(testcase) { 32 | _super.startTest.call(this, testcase); 33 | this.logger.debug('Started testcase ' + testcase + '.'); 34 | } 35 | 36 | function stopTest(testcase) { 37 | this.logger.debug('Completed testcase ' + testcase + '.'); 38 | } 39 | 40 | function pauseTest(testcase) { 41 | this.logger.info('Paused testcase ' + testcase + '.'); 42 | } 43 | 44 | function resumeTest(testcase) { 45 | this.logger.info('Restarted testcase ' + testcase + '.'); 46 | } 47 | 48 | function startSuite(suite) { 49 | this.logger.info('Started suite ' + suite + '.'); 50 | this.logger.group('Suite ' + suite); 51 | } 52 | 53 | function stopSuite(suite) { 54 | this.logger.groupEnd(); 55 | this.logger.info('Completed suite ' + suite + '.'); 56 | } 57 | 58 | function start(t0) { 59 | _super.start.call(this, t0); 60 | this.logger.info('Started tests.'); 61 | this.logger.group('Tests'); 62 | } 63 | 64 | function stop(t1) { 65 | _super.stop.call(this, t1); 66 | this.logger.groupEnd(); 67 | this.logger.info('Completed tests in ' + ((t1 - this.t0)/1000) + 's.'); 68 | this.logger.info(this.toString() + '.'); 69 | } 70 | 71 | p.addAssertion = addAssertion; 72 | p.addSkip = addSkip; 73 | p.addFailure = addFailure; 74 | p.addError = addError; 75 | p.startTest = startTest; 76 | p.stopTest = stopTest; 77 | p.pauseTest = pauseTest; 78 | p.resumeTest = resumeTest; 79 | p.startSuite = startSuite; 80 | p.stopSuite = stopSuite; 81 | p.start = start; 82 | p.stop = stop; 83 | })(ConsoleTestResult.prototype); 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/ui/console/test_runner.js: -------------------------------------------------------------------------------- 1 | function ConsoleTestRunner(logger) { 2 | TestRunner.call(this); 3 | this.logger = logger; 4 | } 5 | 6 | chain(ConsoleTestRunner, TestRunner); 7 | ConsoleTestRunner.displayName = 'ConsoleTestRunner'; 8 | 9 | (function(p) { 10 | function _makeResult() { 11 | return new ConsoleTestResult(this.logger); 12 | } 13 | 14 | p._makeResult = _makeResult; 15 | })(ConsoleTestRunner.prototype); 16 | -------------------------------------------------------------------------------- /src/ui/web.js: -------------------------------------------------------------------------------- 1 | var Web = {}; 2 | //= require "web/abstract_widget" 3 | Web.AbstractWidget = AbstractWidget; 4 | //= require "web/labelled_text" 5 | Web.LabelledText = LabelledText; 6 | //= require "web/progress_bar" 7 | Web.ProgressBar = ProgressBar; 8 | //= require "web/gui" 9 | Web.GUI = WebGUI; 10 | //= require "web/test_runner" 11 | Web.TestRunner = WebTestRunner; 12 | //= require "web/test_result" 13 | Web.TestResult = WebTestResult; 14 | -------------------------------------------------------------------------------- /src/ui/web/abstract_widget.js: -------------------------------------------------------------------------------- 1 | function AbstractWidget(doc) { 2 | this.doc = doc || document; 3 | } 4 | 5 | AbstractWidget.displayName = 'Widget'; 6 | 7 | (function(p) { 8 | function escapeHTML(html) { 9 | return (html + '').replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); 10 | } 11 | 12 | function toElement() { 13 | return this.element; 14 | } 15 | 16 | function appendChild(child) { 17 | var element = child && child.toElement ? child.toElement() : child; 18 | this.element.appendChild(element); 19 | return child; 20 | } 21 | p.draw = function() { return this; }; 22 | p.redraw = function() { return this.draw() }; 23 | p.appendChild = appendChild; 24 | p.escapeHTML = escapeHTML; 25 | p.toElement = toElement; 26 | })(AbstractWidget.prototype); -------------------------------------------------------------------------------- /src/ui/web/gui.js: -------------------------------------------------------------------------------- 1 | function WebGUI(doc) { 2 | AbstractWidget.call(this, doc); 3 | this._build(); 4 | } 5 | 6 | chain(WebGUI, AbstractWidget); 7 | WebGUI.displayName = 'WebGUI'; 8 | 9 | (function(p) { 10 | function _build() { 11 | this.element = this.doc.createElement('div'); 12 | this.element.id = 'evidence'; 13 | this.appendChild(new LabelledText('User agent string').setContent(global.navigator.userAgent).draw()) 14 | this.status = this.appendChild(new LabelledText('Status')); 15 | this.progressBar = this.appendChild(new ProgressBar(300)); 16 | this.results = this.appendChild(new LabelledText('Results')); 17 | return this; 18 | } 19 | 20 | function draw() { 21 | defer(function() { 22 | this.status.draw(); 23 | this.progressBar.draw(); 24 | this.results.draw(); 25 | }, this); 26 | } 27 | 28 | function setResults(txt) { 29 | txt = this._appendFullStop(txt); 30 | this.results.setContent(txt); 31 | return this; 32 | } 33 | 34 | function setStatus(txt) { 35 | txt = this._appendFullStop(txt); 36 | this.status.setContent(txt); 37 | this.draw(); 38 | return this; 39 | } 40 | 41 | function setProgress(ratio) { 42 | this.progressBar.setValue(ratio); 43 | return this; 44 | } 45 | 46 | function setLevel(level) { 47 | this.progressBar.setLevel(level); 48 | return this; 49 | } 50 | 51 | function _appendFullStop(txt) { 52 | return (txt + '').replace(/\.?\s*$/, '.'); 53 | } 54 | 55 | p._build = _build; 56 | p.setResults = setResults; 57 | p.setStatus = setStatus; 58 | p.setProgress = setProgress; 59 | p.setLevel = setLevel; 60 | p._appendFullStop = _appendFullStop; 61 | p.draw = draw; 62 | })(WebGUI.prototype); -------------------------------------------------------------------------------- /src/ui/web/labelled_text.js: -------------------------------------------------------------------------------- 1 | function LabelledText(label, doc) { 2 | AbstractWidget.call(this, doc) 3 | this.label = label; 4 | this.element = this.doc.createElement('p'); 5 | } 6 | 7 | chain(LabelledText, AbstractWidget); 8 | LabelledText.displayName = 'LabelledText'; 9 | 10 | (function(p) { 11 | function setContent(content) { 12 | this._content = this.escapeHTML(content); 13 | this._content = TEMPLATE.replace('{{ label }}', this.label).replace('{{ content }}', content); 14 | return this; 15 | } 16 | 17 | function draw() { 18 | this.element.innerHTML = this._content; 19 | return this; 20 | } 21 | 22 | var TEMPLATE = '{{ label }}: {{ content }}'; 23 | 24 | p.setContent = setContent; 25 | p.draw = draw; 26 | })(LabelledText.prototype); -------------------------------------------------------------------------------- /src/ui/web/list.js: -------------------------------------------------------------------------------- 1 | function List(name, doc) { 2 | this.name = name; 3 | this.doc = doc || document; 4 | this.level = 0; 5 | } 6 | 7 | List.displayName = 'List'; 8 | 9 | (function(p) { 10 | 11 | function build() { 12 | this.element = this.doc.createElement('div'); 13 | this.element.className = 'suite'; 14 | var name = this.name.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); 15 | this.element.innerHTML = TEMPLATE.replace('{{ name }}', name); 16 | return this; 17 | } 18 | 19 | function appendTo(container) { 20 | container.appendChild(this.element); 21 | return this; 22 | } 23 | 24 | function appendChild(child) { 25 | this.children = this.children || []; 26 | this.children.push(child); 27 | child.parent = this; 28 | this.element.lastChild.appendChild(child.element); 29 | return this; 30 | } 31 | 32 | function updateResults(results) { 33 | this.element.childNodes[1].innerHTML = results; 34 | return this; 35 | } 36 | 37 | function setLevel(level) { 38 | if (level > this.level) { 39 | this.element.className = 'suite ' + (Logger.LEVELS[level] || '').toLowerCase();; 40 | this.element.lastChild.style.display = ''; 41 | this.level = level; 42 | if (level > Logger.WARN) { 43 | this.element.firstChild.firstChild.firstChild.checked = true; 44 | } 45 | if (this.parent) { 46 | this.parent.setLevel(level); 47 | } 48 | } 49 | } 50 | 51 | var TEMPLATE = '

'; 52 | 53 | p.build = build; 54 | p.updateResults = updateResults; 55 | p.setLevel = setLevel; 56 | p.appendTo = appendTo; 57 | p.appendChild = appendChild; 58 | })(List.prototype); -------------------------------------------------------------------------------- /src/ui/web/list_element.js: -------------------------------------------------------------------------------- 1 | function ListElement(name, doc) { 2 | this.name = name; 3 | this.doc = doc || document; 4 | this.level = 0; 5 | } 6 | 7 | ListElement.displayName = 'ListElement'; 8 | 9 | (function(p) { 10 | 11 | function build() { 12 | this.element = this.doc.createElement('li'); 13 | var name = this.name.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); 14 | this.element.innerHTML = TEMPLATE.replace('{{ name }}', name); 15 | return this; 16 | } 17 | 18 | function appendTo(container) { 19 | container.appendChild(this.element); 20 | return this; 21 | } 22 | 23 | function updateResults(results) { 24 | this.element.firstChild.lastChild.innerHTML = results; 25 | return this; 26 | } 27 | 28 | function setLevel(level) { 29 | if (level > this.level) { 30 | this.element.className = (Logger.LEVELS[level] || '').toLowerCase();; 31 | this.level = level; 32 | if (level > Logger.WARN) { 33 | this.element.firstChild.firstChild.checked = true; 34 | } 35 | } 36 | this.parent.setLevel(level); 37 | } 38 | 39 | var TEMPLATE = ''; 40 | 41 | p.build = build; 42 | p.updateResults = updateResults; 43 | p.setLevel = setLevel; 44 | p.appendTo = appendTo; 45 | })(ListElement.prototype); -------------------------------------------------------------------------------- /src/ui/web/progress_bar.js: -------------------------------------------------------------------------------- 1 | function ProgressBar(width, doc) { 2 | this._width = width; 3 | this._level = 0; 4 | AbstractWidget.call(this, doc); 5 | this._build(); 6 | } 7 | 8 | chain(ProgressBar, AbstractWidget); 9 | ProgressBar.displayName = 'ProgressBar'; 10 | 11 | (function(p) { 12 | function _build() { 13 | this.element = this._createDiv(this.width); 14 | this.element.id = 'evidence_progress_bar_container'; 15 | this.progressBar = this._createDiv(0); 16 | this.progressBar.id = 'evidence_progress_bar'; 17 | this.element.appendChild(this.progressBar); 18 | return this; 19 | } 20 | 21 | function _createDiv(width) { 22 | var element = this.doc.createElement('div'); 23 | element.style.width = width + 'px'; 24 | return element; 25 | } 26 | 27 | function draw() { 28 | this.progressBar.style.width = this._value + 'px'; 29 | var className = (Logger.LEVELS[this._level] || '').toLowerCase(); 30 | this.progressBar.className = className; 31 | return this; 32 | } 33 | 34 | function setValue(ratio) { 35 | this._value = Math.floor(ratio * this._width); 36 | return this; 37 | } 38 | 39 | function setLevel(level) { 40 | if (level > this._level) { 41 | this._level = level; 42 | } 43 | return this; 44 | } 45 | 46 | p._build = _build; 47 | p._createDiv = _createDiv; 48 | p.draw = draw; 49 | p.setValue = setValue; 50 | p.setLevel = setLevel; 51 | })(ProgressBar.prototype); -------------------------------------------------------------------------------- /src/ui/web/test_result.js: -------------------------------------------------------------------------------- 1 | function WebTestResult(name) { 2 | TestResultTree.call(this, name); 3 | this.pageCount = 0; 4 | } 5 | 6 | chain(WebTestResult, TestResultTree); 7 | WebTestResult.displayName = 'WebTestResult'; 8 | 9 | (function(p) { 10 | var _super = TestResultTree.prototype; 11 | 12 | function addAssertion() { 13 | _super.addAssertion.call(this); 14 | this.gui.setResults(this); 15 | } 16 | 17 | function addSkip(testcase, msg) { 18 | _super.addSkip.call(this, testcase, msg); 19 | var gui = this.gui; 20 | gui.setResults(this); 21 | gui.setLevel(Logger.WARN); 22 | gui.setStatus('Skipping testcase ' + testcase + ': ' + msg.message); 23 | } 24 | 25 | function addFailure(testcase, msg) { 26 | _super.addFailure.call(this, testcase, msg); 27 | var gui = this.gui; 28 | gui.setResults(this); 29 | gui.setLevel(Logger.ERROR); 30 | gui.setStatus(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); 31 | } 32 | 33 | function addError(testcase, error) { 34 | _super.addError.call(this, testcase, error); 35 | var gui = this.gui; 36 | gui.setResults(this); 37 | gui.setLevel(Logger.ERROR); 38 | gui.setStatus(testcase + ' threw an error. ' + error); 39 | } 40 | 41 | function startTest(testcase) { 42 | _super.startTest.call(this, testcase); 43 | this.gui.setStatus('Started testcase ' + testcase); 44 | } 45 | 46 | function stopTest(testcase) { 47 | _super.stopTest.call(this, testcase); 48 | var gui = this.gui; 49 | gui.setProgress(this.getRatio()); 50 | gui.setStatus('Completed testcase ' + testcase); 51 | } 52 | 53 | function pauseTest(testcase) { 54 | this.gui.setStatus('Paused testcase ' + testcase + '...'); 55 | } 56 | 57 | function resumeTest(testcase) { 58 | this.gui.setStatus('Resumed testcase ' + testcase); 59 | } 60 | 61 | function startSuite(suite) { 62 | _super.startSuite.call(this, suite); 63 | if (!this.size) { this.size = suite.size(); } 64 | this.gui.setStatus('Started suite ' + suite); 65 | } 66 | 67 | function stopSuite(suite) { 68 | _super.stopSuite.call(this, suite); 69 | this.gui.setStatus('Completed suite ' + suite); 70 | } 71 | 72 | function loadPage(page) { 73 | this.gui.setStatus('Loading page ' + page.location.pathname + '...'); 74 | } 75 | 76 | function startPage(page, suite) { 77 | this.pageSize = suite.size(); 78 | this.previousTestCount = this.testCount; 79 | this.gui.setStatus('Loaded page ' + page.location.pathname); 80 | } 81 | 82 | function stopPage(page) { 83 | this.pageCount++; 84 | this.gui.setStatus('Finished page ' + page.location.pathname); 85 | } 86 | 87 | function getRatio() { 88 | if (!this.pageSize) { 89 | return this.testCount / this.size; 90 | } 91 | var pageRatio = (this.testCount - this.previousTestCount) / this.pageSize; 92 | return (pageRatio + this.pageCount) / this.size; 93 | } 94 | 95 | function start() { 96 | _super.start.call(this); 97 | var gui = new WebGUI(document); 98 | this.gui = gui; 99 | document.body.appendChild(gui.toElement()); 100 | gui.setResults(this); 101 | } 102 | 103 | function stop() { 104 | _super.stop.call(this); 105 | this.gui.setStatus('Completed tests in ' + ((this.t1 - this.t0)/1000) + 's'); 106 | 107 | console.log(new AsciiViewBuilder(this).draw()) 108 | } 109 | 110 | p.getRatio = getRatio; 111 | p.addAssertion = addAssertion; 112 | p.addSkip = addSkip; 113 | p.addFailure = addFailure; 114 | p.addError = addError; 115 | p.startTest = startTest; 116 | p.stopTest = stopTest; 117 | p.pauseTest = pauseTest; 118 | p.resumeTest = resumeTest; 119 | p.startSuite = startSuite; 120 | p.stopSuite = stopSuite; 121 | p.loadPage = loadPage; 122 | p.startPage = startPage; 123 | p.stopPage = stopPage; 124 | p.start = start; 125 | p.stop = stop; 126 | })(WebTestResult.prototype); 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/ui/web/test_runner.js: -------------------------------------------------------------------------------- 1 | function WebTestRunner(logger) { 2 | TestRunner.call(this); 3 | this.logger = logger; 4 | } 5 | 6 | chain(WebTestRunner, TestRunner); 7 | WebTestRunner.displayName = 'WebTestRunner'; 8 | 9 | (function(p) { 10 | function _makeResult() { 11 | return new WebTestResult(); 12 | } 13 | 14 | p._makeResult = _makeResult; 15 | })(WebTestRunner.prototype); 16 | --------------------------------------------------------------------------------