pax_global_header00006660000000000000000000000064126631006040014510gustar00rootroot0000000000000052 comment=78f4185a5c849c252ace086eaa80d3ca3bdb9f8b qunit-1.22.0/000077500000000000000000000000001266310060400127325ustar00rootroot00000000000000qunit-1.22.0/.editorconfig000066400000000000000000000003411266310060400154050ustar00rootroot00000000000000# This file is for unifying the coding style for different editors and IDEs # editorconfig.org root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true qunit-1.22.0/.gitattributes000066400000000000000000000001771266310060400156320ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # JS files must always use LF for tools to work *.js eol=lf qunit-1.22.0/.gitignore000066400000000000000000000000721266310060400147210ustar00rootroot00000000000000dist node_modules build/report browserstack-run.pid temp/ qunit-1.22.0/.jscsrc000066400000000000000000000000721266310060400142210ustar00rootroot00000000000000{ "preset": "jquery", "requireMultipleVarDecl": null } qunit-1.22.0/.jshintrc000066400000000000000000000005211266310060400145550ustar00rootroot00000000000000{ "boss": true, "curly": true, "eqeqeq": true, "eqnull": true, "expr": true, "immed": true, "noarg": true, "quotmark": "double", "smarttabs": true, "trailing": true, "undef": true, "unused": true, "browser": true, "es3": true, "globals": { "QUnit": false, "define": false, "exports": false, "module": false } } qunit-1.22.0/.mailmap000066400000000000000000000073701266310060400143620ustar00rootroot00000000000000Jörn Zaefferer Jörn Zaefferer Ariel Flesler Scott González Richard Worth Philippe Rathé John Resig Will Moffat Jan Kassens Ziling Zhao Ryan Szulczewski Chris Lloyd Louis-Rémi Babé Louis-Rémi Babé Louis-Rémi Babé Jake Archibald Jake Archibald Frances Berriman Rune Halvorsen Rune Halvorsen Chris Thatcher Chris Thatcher Fábio Rehm Leon Sorokin Douglas Neiner Paul Elliott Nikita Vasilyev Benjamin Lee Paul Irish Oleg Slobodskoi Anton Matzneller Aurélien Bombo Mathias Bynens Erik Vold Wesley Walser Wesley Walser Rob Kinninmont Marc Portier Michael Righi Timo Tijhof Jan Alonzo Daniel Trebbien Bob Fanger Markus Messner-Chaney Trevor Parscal Ashar Voultoiz Jimmy Mabey Domenic Denicola Mike Sherov Seong-A Kong Graham Conzett Niall Smart Johan Sörlin Gijs Kruitbosch Erkan Yilmaz Jonathan Sanchez Keith Cirkel Rick Waldron Herbert Vojčík Richard Gibson Alex J Burke Sergii Kliuchnyk Sergii Kliuchnyk Corey Frang John Reeves John Reeves Vivin Paliath Joshua Niehus Glen Huang Glen Huang Jonas Ulrich Jamie Hoover Jamie Hoover James M. Greene Rodney Rehm Peter Wagenet Clog Clog Antranig Basman Antranig Basman Matthew Mirande Jared Wyles Dmitry Gusev Ian Wallis Ian Wallis Dan Andreescu Dan Andreescu Matthew DuVall Matthew DuVall Dave K. Smith David Vollbracht Jochen Ulrich qunit-1.22.0/.travis.yml000066400000000000000000000010071266310060400150410ustar00rootroot00000000000000sudo: false language: node_js node_js: - "4" before_install: - true && `base64 --decode <<< ZXhwb3J0IEJST1dTRVJTVEFDS19VU0VSTkFNRT1icm93c2Vyc3RhY2txdW5pMQo=` - true && `base64 --decode <<< ZXhwb3J0IEJST1dTRVJTVEFDS19LRVk9SllzeHJrVWk5aGJGVndkdW44ZUsK=` script: - npm run-script ci cache: directories: - node_modules notifications: irc: channels: - "chat.freenode.net#jquery-dev" template: - "%{repository}#%{build_number} (%{branch} - %{commit} %{author}): %{message} - %{build_url}" qunit-1.22.0/AUTHORS.txt000066400000000000000000000107651266310060400146310ustar00rootroot00000000000000Authors ordered by first contribution Jörn Zaefferer Ariel Flesler Scott González Richard Worth Philippe Rathé John Resig Will Moffat Jan Kassens Ziling Zhao Ryan Szulczewski Chris Lloyd Louis-Rémi Babé Jake Archibald Frances Berriman Rune Halvorsen Chris Thatcher Fábio Rehm Leon Sorokin Douglas Neiner Paul Elliott Nikita Vasilyev Benjamin Lee Paul Irish Oleg Slobodskoi Anton Matzneller Aurélien Bombo Mathias Bynens Erik Vold Wesley Walser Rob Kinninmont Marc Portier Michael Righi Timo Tijhof Jan Alonzo Daniel Trebbien Bob Fanger Markus Messner-Chaney Trevor Parscal Ashar Voultoiz Jimmy Mabey Domenic Denicola Mike Sherov Seong-A Kong Graham Conzett Niall Smart Johan Sörlin Gijs Kruitbosch Erkan Yilmaz Jonathan Sanchez Keith Cirkel Rick Waldron Herbert Vojčík Richard Gibson Alex J Burke Sergii Kliuchnyk Corey Frang John Reeves Antranig Basman Vivin Paliath Joshua Niehus Glen Huang Jochen Ulrich Jamie Hoover James M. Greene Rodney Rehm Peter Wagenet Clog Matthew Mirande Jared Wyles Dmitry Gusev Ian Wallis Dan Andreescu Matthew DuVall Dave K. Smith David Vollbracht Katie Gengler Joshua Peek Leonardo Balter Jeff Cooper Corey Frang Nathan Dauber Michał Gołębiowski XhmikosR Patrick Stapleton DarkPark Oleg Gaidarenko Mike Sidorov Don Kirkby don Sean Xu Matthew Beale Mislav Marohnić Anne-Gaelle Colom Leonardo Braga Jon Bretman Jonny Buchanan Adrian Phinney Lam Chau Henning Beyer Jesús Leganés Combarro Shivam Dixit Gaurav Mittal Kevin Partington Braulio Valdivielso Martínez Shinnosuke Watanabe Aurelio De Rosa Toh Chee Chuan Stefan Penner Taehee Kim YongWoo Jeon Stephen Jones Ralph Dugue bianca c [arghgr] Erik Benoist Sergio Cinos Josh Soref Jon Dufresne Maksim Gudow Trent Willis Frank Weigel Jürg Lehni qunit-1.22.0/CONTRIBUTING.md000066400000000000000000000020351266310060400151630ustar00rootroot00000000000000Welcome! Thanks for your interest in contributing to QUnit. You're **almost** in the right place. More information on how to contribute to this and all other jQuery Foundation projects is over at [contribute.jquery.org](https://contribute.jquery.org). You'll definitely want to take a look at the articles on contributing [code](https://contribute.jquery.org/code). You may also want to take a look at our [commit & pull request guide](https://contribute.jquery.org/commits-and-pull-requests/) and [style guides](https://contribute.jquery.org/style-guide/) for instructions on how to maintain your fork and submit your code. Before we can merge any pull request, we'll also need you to sign our [contributor license agreement](https://contribute.jquery.org/cla). You can find us on [IRC](https://irc.jquery.org), specifically in #jquery-dev should you have any questions. If you've never contributed to open source before, we've put together [a short guide with tips, tricks, and ideas on getting started](https://contribute.jquery.org/open-source/). qunit-1.22.0/Gruntfile.js000066400000000000000000000070721266310060400152350ustar00rootroot00000000000000/*jshint node:true */ module.exports = function( grunt ) { require( "load-grunt-tasks" )( grunt ); function process( code, filepath ) { // Make coverage ignore external files if ( filepath.match( /^external\// ) ) { code = "/*istanbul ignore next */\n" + code; } return code // Embed version .replace( /@VERSION/g, grunt.config( "pkg" ).version ) // Embed date (yyyy-mm-ddThh:mmZ) .replace( /@DATE/g, ( new Date() ).toISOString().replace( /:\d+\.\d+Z$/, "Z" ) ); } grunt.initConfig({ pkg: grunt.file.readJSON( "package.json" ), concat: { "src-js": { options: { process: process }, src: [ "src/intro.js", "src/core/initialize.js", "src/core/utilities.js", "src/core/stacktrace.js", "src/core/config.js", "src/core/logging.js", "src/core/onerror.js", "src/core.js", "src/test.js", "src/assert.js", "src/equiv.js", "src/dump.js", "src/export.js", "src/diff.js", "src/outro.js", "reporter/html.js" ], dest: "dist/qunit.js" }, "src-css": { options: { process: process }, src: "src/qunit.css", dest: "dist/qunit.css" } }, jshint: { options: { jshintrc: true }, all: [ "*.js", "{test,dist}/**/*.js", "build/*.js", "build/tasks/**/*.js" ] }, jscs: { options: { config: ".jscsrc" }, all: [ "<%= jshint.all %>", "!test/main/deepEqual.js" ] }, search: { options: { // Ensure that the only HTML entities used are those with a special status in XHTML // and that any common singleton/empty HTML elements end with the XHTML-compliant // "/>"rather than ">" searchString: /(&(?!gt|lt|amp|quot)[A-Za-z0-9]+;|<(?:hr|HR|br|BR|input|INPUT)(?![^>]*\/>)(?:\s+[^>]*)?>)/g, logFormat: "console", failOnMatch: true }, xhtml: [ "src/**/*.js", "reporter/**/*.js" ] }, qunit: { options: { timeout: 30000, "--web-security": "no", coverage: { src: "dist/qunit.js", instrumentedFiles: "temp/", htmlReport: "build/report/coverage", lcovReport: "build/report/lcov", linesThresholdPct: 70 } }, qunit: [ "test/index.html", "test/autostart.html", "test/startError.html", "test/reorderError1.html", "test/reorderError2.html", "test/logs.html", "test/setTimeout.html", "test/amd.html", "test/reporter-html/index.html", "test/reporter-html/legacy-markup.html", "test/reporter-html/no-qunit-element.html", "test/reporter-html/single-testid.html", "test/only.html", "test/regex-filter.html", "test/regex-exclude-filter.html", "test/string-filter.html" ] }, coveralls: { options: { force: true }, all: { // LCOV coverage file relevant to every target src: "build/report/lcov/lcov.info" } }, "test-on-node": { files: [ "test/logs", "test/main/test", "test/main/assert", "test/main/async", "test/main/promise", "test/main/modules", "test/main/deepEqual", "test/main/stack", "test/globals-node", "test/only", "test/setTimeout", "test/main/dump", "test/reporter-html/diff" ] }, concurrent: { build: [ "concat:src-js", "concat:src-css" ], test: [ "jshint", "jscs", "search", "qunit", "test-on-node" ] }, watch: { options: { atBegin: true, spawn: false, interrupt: true }, files: [ ".jshintrc", "*.js", "build/*.js", "{src,test,reporter}/**/*.js", "src/qunit.css", "test/**/*.html" ], tasks: "default" } }); grunt.loadTasks( "build/tasks" ); grunt.registerTask( "build", [ "concat" ] ); grunt.registerTask( "default", [ "concurrent:build", "concurrent:test" ] ); }; qunit-1.22.0/History.md000066400000000000000000001355771266310060400147370ustar00rootroot00000000000000 1.22.0 / 2016-02-23 ================== * Assert: Implement Assert#pushResult * Assert: Extend Assert methods to QUnit for backwards compatibility * HTML Reporter: Escape setUrl output 1.21.0 / 2016-02-01 ================== * Assert: Improve size and speed of QUnit.equiv * Assert: Fully support Object-wrapped primitives in deepEqual * Assert: Register notOk as a non-negative assertion * CSS: Fix hidden test results under static parents * Core: Improve regular expression comparisons * Core: Support filtering by regular expression * Test: Prevents skiping tests after rerun reordering * Tests: Differentiate QUnit.equiv assertions 1.20.0 / 2015-10-27 ================== * Assert: Exposes assert.raises() to the global scope * Assert: Add a calls count parameter on assert.async * Build: Improve grunt speed using grunt-concurrent * Core: Implement QUnit.only * Core: Support Symbol types on QUnit.equiv * Core: QUnit.start fails if called with a non-numeric argument * Core: Implement Nested modules * Core: Equivalency for descendants of null constructors * HTML Reporter: Adds indicator for filtered test * HTML Reporter: Collapse details for successive failed tests * Test: Fix regression when a failing test canceled the module hooks * Tests: Isolate and improve tests for Object equivalency * Tests: Split browserstack runs on CI to avoid timeout errors 1.19.0 / 2015-09-01 ================== * Assert: Add support to ES6' Map and Set equiv objects * Build: Enable IRC notifications for Travis CI * Build: Add 'Readme' to commitplease components * Build: Remove unintended QUnit global export on Node * Build: Remove testSwarm job * Core: Implement QUnit.stack * Dump: Escape backslash when quoting strings * HTML Reporter: Avoid readyState issue with PhantomJS * HTML Reporter: HTML reporter enhancements for negative asserts * HTML Reporter: Show diff only when it helps * Tests: Avoid loosen errors on autostart test * Tests: HTML Reporter tests are now isolated with reordering disabled * Tests: Rename stack error tests * Test: Release module hooks to avoid memory leaks * Test: Don't pass Promise fulfillment value to QUnit.start * Test: Source Displayed Even for Passed Test 1.18.0 / 2015-04-03 ================== * Assert: throws uses push method only * Assert: Fix missing test on exported throws * Assert: Implements notOk to assert falsy values * Core: More graceful handling of AMD * Core: Simplify stack trace methods * Core: Expose Dump maxDepth property * Core: Expose QUnit version as QUnit.version property * Core: Handle multiple testId parameters * Dump: Fix .name/.property doublettes * HTML Reporter: New diff using Google's Diff-Patch-Match Library * HTML Reporter: Make it more obvious why diff is suppressed. * HTML Reporter: Change display text for bad tests * HTML Reporter: Fix checkbox and select handling in IE <9 * HTML Reporter: Fix test filter without any module * HTML Reporter: Retain failed tests numbers * Test: lowercase the valid test filter before using it 1.17.1 / 2015-01-20 ================== * HTML Reporter: Fix missing toolbar bug 1.17.0 / 2015-01-19 ================== * Build: Remove bower.json from ignored files * Build: Support Node.js export parity with CommonJS * HTML Reporter: Add the filter field * HTML Reporter: Don't hide skipped tests * HTML Reporter: Fix regression for old markup * HTML Reporter: Prevent XSS attacks * HTML Reporter: QUnit.url is now a private function in the HTML Reporter * HTML Reporter: url params can be set by code 1.16.0 / 2014-12-03 ================== * Assert: Add alias for throws called 'raises' * Async: Fail assertions after existing assert.async flows are resolved * Async: Implement assert.async * Async: Tests are now Promise-aware * Callbacks: Restore and warn if some logging callback gets modified * Callbacks: Throws an error on non-function params for logging methods * Core: change url() helper to output `?foo` instead of `?foo=true` * Core: Detail modules and tests names in the logging callbacks * Core: Implements QUnit.skip * Core: Remove constructor * Core: Rename config.module to config.moduleFilter * Core: Use `Error#stack` without throwing when available * Dump: Configurable limit for object depth * HTML Reporter: Enable activating config.hidepassed from URL params * HTML Reporter: Move QUnit.reset back to core to run it before testDone * HTML Reporter: Output runtime of each assertion in results * Logging: Add runtime to moduleDone * Logging: Defer begin event till tests actually starts * Test: Introduce order independent testId to replace testNumber * Test: Rename module's setup/teardown to beforeEach/afterEach 1.15.0 / 2014-08-08 ================== * Assert: Implement Assert constructor with test context. This heavily improves debugging of async tests, since assertions can't leak into other tests anymore. Use the assert argument in your test callbacks to run assertions to get the full benefit of this. * Assert: Improved the default message from assert.ok. Now assert.ok() outputs the exact value it received, instead of only saying it wasn't thruthy. * Assert: Removal of raises, same and equals. These were deprecated a long time ago and finally removed. Use throws, deepEqual and equal instead. * Core: Pass total amount of tests to QUnit.begin callback as totalTests. Will be used by Karma and other reporters. * Dump: Move QUnit.jsDump to QUnit.dump. QUnit.jsDump still exists, but will be removed later. Use QUnit.dump. * Dump: Output non-enumerable properties of TypeError. Makes it easier to compare properties of error objects. * Reporter: Output only assertion count for green tests. Less visual clutter for passing tests. * Reporter: Move HTML reporter to a new JS file. The HTML reporter is still bundled, but the code has been refactored to move it to a separate file. * Test: Remove deprecated QUnit.current_testEnvironment * Throws: support for oldIE native Error types. Error objects in IE are buggy, this works around those issues. 1.14.0 / 2014-01-31 ================== * Grunt: Run tests on ios browserSet as well * Package: Set main property to qunit/qunit.js * Grunt: Inline browserSet config for TestSwarm runs * CSS: Removing redundancy * Core: Add config property for disabling default scroll-to-top * Grunt: Remove addons leftovers * Addons: Remove last remnants * Core: Extend QUnit.config.urlConfig to support select-one dropdowns * Assert: Extend throws to accept Error instances * Assert: Extend throws to handle errors as strings * CSS: Updating qunit.css for consistency * Core: Cache window.clearTimeout in case it gets mocked * Core: Run multiple tests by test number * jshint: add es3 option to ensure oldie support 1.13.0 / 2014-01-04 ================== * Tests: Stop using the expected argument in test() calls * Logging: Add runtime property to testDone, deprecate duration * Assert: Remove raises (deprecated 2012), replace with failed assertion * Grunt: Add non-browser test as grunt task. Runs existing tests in node. * Export: Only export to the variable that we check for. * Core: Properly check for existence of document * Core: Remove triggerEvent, which isn't used or documented anywhere. * Core: Silence addEvent in non-browser env * The Grand QUnit Split of 2013 * Use `id` function for selection elements in two places that were not using it. Closes gh-463 * Add bower.json. Fixes #461 1.12.0 / 2013-06-21 =================== * Add a deprecation comment to QUnit.reset. Partial fix for #354 * Fix mis-match between moduleStart and moduleDone events * Removed jQuery.trim optimization. Fixes #424. * Use a local setTimeout reference, add separate unit test suite for that. Fixes #432 - Using a setTimeout stub can stop test suite from continuing. Closes gh-433 * Added CONTRIBUTING.md. * Moved 'addons/themes/nv' to 'Krinkle/qunit-theme-nv.git' * Moved 'addons/themes/ninja' to 'Krinkle/qunit-theme-ninja.git' * Moved 'addons/themes/gabe' to 'Krinkle/qunit-theme-gabe.git' * Moved 'addons/canvas' to 'JamesMGreene/qunit-assert-canvas.git'. Tree: https://github.com/JamesMGreene/qunit-assert-canvas/tree/v1.0.0 * Moved 'addons/close-enough' to 'JamesMGreene/qunit-assert-close.git'. Tree: https://github.com/JamesMGreene/qunit-assert-close/tree/v1.0.0 * Moved 'addons/step' to 'JamesMGreene/qunit-assert-step.git'. Tree: https://github.com/JamesMGreene/qunit-assert-step/tree/v1.0.0 * Canvas plugin: Show how to test with images. Closes gh-438. * Clear filter and testNumber when choosing a module. Fixes #442. * Deprecate QUnit.current_testEnvironment in favour of config.current.testEnvironment. * assert.ok: Message shouldn't be undefined in 'log' event. * Emit moduleStart before testStart even if test isn't in a module. * PhantomJS plugin: Added optional timeout. Closes #415. * PhantomJS plugin: Include stack trace for all failed tests. Closes #416. * Moved 'addons/composite' to 'jquery/qunit-composite.git'. Tree: https://github.com/jquery/qunit-composite/tree/v1.0.0 Fixes #419. * Moved 'addons/junitlogger' to 'jquery/qunit-reporter-junit.git'. * Sort the module names so we no longer rely on implicit ordering. Fixes #391. Closes gh-392 * JUnitLogger: Add a `name` property to the test run. Closes gh-389 * Improve circular reference logic in equiv - Fixes #397 1.11.0 / 2013-01-20 ================== * Diff: Fix exception on property "constructor". Fixes #394. * Composite Add-on: Test suites can be named by including an obj with name & path props within array param for .testSuites() * Fix URL generator to take protocol and host into account to fix usage with file protocol in IE7/8 * Fix issue with Error.prototype.toString in IE 7 * Refactor jsDump for "node". Fixes #381. * Show contents of text nodes in jsDump.node. Fixes #380. * Escape text. Fixes #379. * Rewrote most of the JUnitLogger addon as it was in bad shape: unused variables, duplicate effort that QUnit handles internally (e.g. tallying number of total assertions, failed assertions, etc.), sub-optimal XmlWriter implementation, etc. * Phantomjs: Include source in assertion details * Phantomjs: Removed the polling mechanism in favor of PhantomJS 1.6+'s `WebPage#onCallback` * Delay start() until init() happened. Fixes #358. Closes #373. * urlConfig: Fix checkbox event for oldIE. Fixes #369. Closes #370. * Issue #365: Fix module picker for oldIE. Closes #366. * Fixes #344 - Capture and show test duration. * Rename tests to assertions in summary. Fixes #336 - Summary counts assertions but mentions 'tests'. * Assert: Implement propEqual and notPropEqual. Fixes #317. * Canvas addon: Use 0.6 as alpha value to avoid inconsistencies between browsers. Fixes #342 * Remove global variable "assert". Fixes #341. * Add a test for loading tests asynchronously * Improve start()-called-too-often fix, initialize semaphore at 1, fixes autostart=false case. Also provide stack for the offending start() call * There's type-free objects in Firefox, extend objectType() to allow null match. Fixes #315 * Push a failing assertion when calling start() while already running. Resets anyway to keep other tests going. Fixes #314 * Adds Ninja Theme * Extend jsdump to output Error objects as such, including the message property. Extend throws to provide 'expected' value when possible. Fixes #307 * Use classes to collapse assertion groups. Fixes #269 * Readme for junitlogger addon * Better readme for composite addon * Make `throws` ES3 compatible * Composite: Adds test whether iframe contains content. Fixes #318 - Composite: Raises "global failure" in Opera * Apply the same exception handling for test and teardown try/catch as for setup 1.10.0 / 2012-08-30 ================== * Simplify licensing: Only MIT, no more MIT/GPL dual licensing. * Scroll the window back to top after tests finished running. Fixes #304 * Simplify phantomjs runner to use module property in testDone callback * Adds module and test name to the information that is returned in the callback provided to QUnit.log(Function). Fixes #296 * Make QUnit.expect() (without arguments) a getter. Fixes #226 * Compare the ES6 sticky (y) property for RegExp. Can't add to tests yet. Fixes #284 - deepEqual for RegExp should compare * onerror: force display of global errors despite URL parameters. Fixes #288 - Global failures can be filtered out by test-limiting URL parameters * Remove conditional codepath based on jQuery presence from reset(). * Add module filter to UI * Keep a local reference to Date. Fixes #283. * Update copyright to jQuery Foundation. 1.9.0 / 2012-07-11 ================== * added jsdoc for QUnit.assert functions * Styling: radius to 5px and small pass/error border, remove inner shadow * Move checkboxes into toolbar and give them labels and descriptions (as tooltip). Fixes #274 - Improve urlFilter API and UI * Where we receive no exception in throws() use a relevant message. * Also make module filter case-insensitive. Follow-up to #252 * Banner: Link should ignore "testNumber" and "module". Fixes #270 * Rename assert.raises to assert.throws. Fixes #267 * Change package.json name property to 'qunitjs' to avoid conflict with node-qunit; will publish next release to npm 1.8.0 / 2012-06-14 ================== * Improve window.onerror handling * (issue #260) config.current should be reset at the right time. * Filter: Implement 'module' url parameter. Fixes #252 * raises: ignore global exceptions stemming from test. Fixes #257 - Globally-executed errors sneak past raises in IE 1.7.0 / 2012-06-07 ================== * Add config.requireExpects. Fixes #207 - Add option to require all tests to call expect(). * Improve extractStacktrace() implementation. Fixes #254 - Include all relevant stack lines * Make filters case-insensitive. Partial fix for #252 * is() expects lowercase types. Fixes #250 - Expected Date value is not displayed properly * Fix phantomjs addon header and add readme. Fixes #239 * Add some hints to composite addon readme. Fixes #251 * Track tests by the order in which they were run and create rerun links based on that number. Fixes #241 - Make Rerun link run only a single test. * Use QUnit.push for raises implementation. Fixes #243 * CLI runner for phantomjs * Fix jshint validation until they deal with /** */ comments properly * Update validTest() : Simplify logic, clarify vars and add comments * Refactor assertion helpers into QUnit.assert (backwards compatible) * Add Rerun link to placeholders. Fixes #240 1.6.0 / 2012-05-04 ================== * Save stack for each test, use that for failed expect() results, points at the line where test() was called. Fixes #209 * Prefix test-output id and ignore that in noglobals check. Fixes #212 * Only check for an exports object to detect a CommonJS environment. Fixes #237 - Incompatibility with require.js * Add testswarm integration as grunt task * Added padding on URL config checkboxes. * Cleanup composite addon: Use callback registration instead of overwriting them. Set the correct src on rerun link (and dblclick). Remove the composite test itself, as that was a crazy hack not worth maintaining * Cleanup reset() test and usage - run testDone callback first, to allow listeners ignoring reset assertions * Double clicking on composite test rows opens individual test page * test-message for all message-bearing API reporting details 1.5.0 / 2012-04-04 ================== * Modify "Running..." to display test name. Fixes #220 * Fixed clearing of sessionStorage in Firefox 3.6. * Fixes #217 by calling "block" with config.current.testEnvironment * Add stats results to data. QUnit.jUnitReport function take one argument { xml:' QUnit.start() * Remove the 'let teardown clean up globals test' - IE<9 doesn't support (==buggy) deleting window properties, and that's not worth the trouble, as everything else passes just fine. Fixes #155 * Fix globals in test.js, part 2 * Fix globals in test.js. ?tell wwalser to use ?noglobals every once in a while * Extend readme regarding release process 1.1.0 / 2011-10-11 ================== * Fixes #134 - Add a window.onerror handler. Makes uncaught errors actually fail the testsuite, instead of going by unnoticed. * Whitespace cleanup * Merge remote branch 'trevorparscal/master' * Fixed IE compatibility issues with using toString on NodeList objects, which in some browsers results in [object Object] rather than [object NodeList]. Now using duck typing for NodeList objects based on the presence of length, length being a number, presence of item method (which will be typeof string in IE and function in others, so we just check that it's not undefined) and that item(0) returns the same value as [0], unless it's empty, in which case item(0) will return 0, while [0] would return undefined. Tested in IE6, IE8, Firefox 6, Safari 5 and Chrome 16. * Update readme with basic notes on releases * More whitespace/parens cleanup * Check if setTimeout is available before trying to delay running the next task. Fixes #160 * Whitespace/formatting fix, remove unnecessary parens * Use alias for Object.prototype.toString * Merge remote branch 'trevorparscal/master' * Merge remote branch 'wwalser/recursionBug' * Default 'expected' to null in asyncTest(), same as in test() itself. * Whitespace cleanup * Merge remote branch 'mmchaney/master' * Merge remote branch 'Krinkle/master' * Using === instead of == * Added more strict array type detection for dump output, and allowed NodeList objects to be output as arrays * Fixes a bug where after an async test, assertions could move between test cases because of internal state (config.current) being incorrectly set * Simplified check for assertion count and adjusted whitespace * Redo of fixing issue #156 (Support Object.prototype extending environment). * QUnit.diff: Throws exception without this if Object.prototype is set (Property 'length' of undefined. Since Object.prototype.foo doesn't have a property 'rows') * QUnit.url: Without this fix, if Object.prototype.foo is set, the url will be set to ?foo=...&the=rest. * saveGlobals: Without this fix, whenever a member is added to Object.prototype, saveGlobals will think it was a global variable in this loop. --- This time using the call method instead of obj.hasOwnProperty(key), which may fail if the object has that as it's own property (touché!). * Handle expect(0) as expected, i.e. expect(0); ok(true, foo); will cause a test to fail 1.0.0 / 2011-10-06 ================== * Make QUnit work with TestSwarm * Run other addons tests as composite addon demo. Need to move that to /test folder once this setup actually works * Add-on: New assertion-type: step() * added parameter to start and stop allowing a user to increment/decrement the semaphore more than once per call * Update readmes with .md extension for GitHub to render them as markdown * Update close-enough addon to include readme and match (new) naming conventions * Merge remote branch 'righi/close-enough-addon' * Canvas addon: Update file references * Update canvas addon: Rename files and add README * Merge remote branch 'wwalser/composite' * Fix #142 - Backslash characters in messages should not be escaped * Add module name to testStart and testDone callbacks * Removed extra columns in object literals. Closes #153 * Remove dead links in comments. * Merge remote branch 'wwalser/multipleCallbacks' * Fixed syntax error and CommonJS incompatibilities in package.json * Allow multiple callbacks to be registered. * Add placeholder for when Safari may end up providing useful error handling * changed file names to match addon naming convention * Whitespace * Created the composite addon. * Using array and object literals. * Issue #140: Make toggle system configurable. * Merge remote branch 'tweetdeck/master' * Adds the 'close enough' addon to determine if numbers are acceptably close enough in value. * Fix recursion support in jsDump, along with tests. Fixes #63 and #100 * Adding a QUnit.config.altertitle flag which will allow users to opt-out of the functionality introduced in 60147ca0164e3d810b8a9bf46981c3d9cc569efc * Refactor window.load handler into QUnit.load, makes it possible to call it manually. * More whitespace cleanup * Merge remote branch 'erikvold/one-chk-in-title' * Whitespace * Merge remote branch 'wwalser/syncStopCalls' * Introducing the first QUnit addon, based on https://github.com/jquery/qunit/pull/84 - adds QUnit.pixelEqual assertion method, along with example tests. * Remove config.hidepassed setting in test.js, wasn't intended to land in master. * Expose QUnit.config.hidepassed setting. Overrides sessionStorage and enables enabling the feature programmatically. Fixes #133 * Fix formatting (css whitespace) for tracebacks. * Expose extend, id, and addEvent methods. * minor comment typo correction * Ignore Eclipse WTP .settings * Set 'The jQuery Project' as author in package.json * Fixes a bug where synchronous calls to stop would cause tests to end before start was called again * Point to planning testing wiki in readme * only add one checkmark to the document.title * Escape the stacktrace output before setting it as innerHTML, since it tends to contain `<` and `>` characters. * Cleanup whitespace * Run module.teardown before checking for pollution. Fixes #109 - noglobals should run after module teardown * Fix accidental global variable "not" * Update document.title status to use more robust unicode escape sequences, works even when served with non-utf-8-charset. * Modify document.title when suite is done to show success/failure in tab, allows you to see the overall result without seeing the tab content. * Merge pull request #107 from sexyprout/master * Set a generic font * Add/update headers * Drop support for deprecated #main in favor of #qunit-fixture. If this breaks your testsuite, replace id="main" with id="qunit-fixture". Fixes #103 * Remove the same key as the one being set. Partial fix for #101 * Don't modify expected-count when checking pollution. The failing assertion isn't expected, so shouldn't be counted. And if expect wasn't used, the count is misleading. * Fix order of noglobals check to produce correct introduced/delete error messages * Prepend module name to sessionStorage keys to avoid conflicts * Store filter-tests only when checked * Write to sessionStorage only bad tests * Moved QUnit.url() definition after QUnit properties are merged into the global scope. Fixes #93 - QUnit url/extend function breaking urls in jQuery ajax test component * Add a "Rerun" link to each test to replace the dblclick (still supported, for now). * Fixed the regex for parsing the name of a test when double clicking to filter. * Merge remote branch 'scottgonzalez/url' * Added checkboxes to show which flags are currently on and allow toggling them. * Retain all querystring parameters when filtering a test via double click. * Added better querystring parsing. Now storing all querystring params in QUnit.urlParams so that we can carry the params forward when filtering to a specific test. This removes the ability to specify multiple filters. * Make reordering optional (QUnit.config.reorder = false) and optimize "Hide passed tests" mode by also hiding "Running [testname]" entries. * Added missing semicolons and wrapped undefined key in quotes. * Optimize test hiding, add class on page load if stored in sessionStorage * Optimize the hiding of passed tests. * Position test results above test list, making it visible without ever having to scroll. Create a placeholder to avoid pushing down results later. * Don't check for existing qunit-testresult element, it gets killed on init anyway. * Added URL flag ?notrycatch (ala ?noglobals) for debugging exceptions. Won't try/catch test code, giving better debugging changes on the original exceptions. Fixes #72 * Always show qunit-toolbar (if at all specified), persist checkbox via sessionStorage. Fixes #47 * Use non-html testname for calls to fail(). Fixes #77 * Overhaul of QUnit.callbacks. Consistent single argument with related properties, with additional runtime property for QUnit.done * Extended test/logs.html to capture more of the callbacks. * Fixed moduleStart/Done callbacks. Added test/logs.html to test these callbacks. To be extended. * Update copyright and license header. Fixes #61 * Formatting fix. * Use a semaphore to synchronize stop() and start() calls. Fixes #76 * Merge branch 'master' of https://github.com/paulirish/qunit into paulirish-master * Added two tests for previous QUnit.raises behaviour. For #69 * add optional 2. arg to QUnit.raises #69. * fix references inside Complex Instances Nesting to what was originally intended. * Qualify calls to ok() in raises() for compatibility with CLI environments. * Fix done() handling, check for blocking, not block property * Fix moduleStart/Done and done callbacks. * Replacing sessionStorage test with the one from Modernizr/master (instead of current release). Here's hoping it'll work for some time. * Updated test for availability of sessionStorage, based on test from Modernizr. Fixes #64 * Defer test execution when previous run passed, persisted via sessionStorage. Fixes #49 * Refactored module handling and queuing to enable selective defer of test runs. * Move assertions property from config to Test * Move expected-tests property from config to Test * Refactored test() method to delegate to a Test object to encapsulate all properties and methods of a single test, allowing further modifications. * Adding output of sourcefile and linenumber of failed assertions (except ok()). Only limited cross-browser support for now. Fixes #60 * Drop 'hide missing tests' feature. Fixes #48 * Adding readme. Fixes #58 * Merge branch 'prettydiff' * Improve jsDump output with formatted diffs. * Cleanup whitespace * Cleanup whitespace * Added additional guards around browser specific code and cleaned up jsDump code * Added guards around tests which are only for browsers * cleaned up setTimeout undefined checking and double done on test finish * fixing .gitignore * making window setTimeout query more consistent * Moved expect-code back to beginning of function, where it belongs. Fixes #52 * Bread crumb in header: Link to suite without filters, add link to current page based on the filter, if present. Fixes #50 * Make the toolbar element optional when checking for show/hide of test results. Fixes #46 * Adding headless.html to manually test logging and verify that QUnit works without output elements. Keeping #qunit-fixture as a few tests actually use that. * Fix for QUnit.moduleDone, get rid of initial bogus log. Fixes #33 * Pass raw data (result, message, actual, expected) as third argument to QUnit.log. Fixes #32 * Dump full exception. Not pretty, but functional (see issue Pretty diff for pretty output). Fixes #31 * Don't let QUnit.reset() cause assertions to run. Manually applied from Scott Gonzalez branch. Fixes #34 * Added missing semicolons. Fixes #37 * Show okay/failed instead of undefined. Fixes #38 * Expose push as QUnit.push to build custom assertions. Fixes #39 * Respect filter pass selection when writing new results. Fixes #43 * Cleanup tests, removing asyncTest-undefined check and formatting * Reset: Fall back to innerHTML when jQuery isn't available. Fixes #44 * Merge branch 'master' of github.com:jquery/qunit * reset doesn't exist here - fixes #28. * - less css cruft, better readability - replaced inline style for test counts with "counts" class - test counts now use a "failed"/"passed" vs "pass"/"fail", shorter/more distinct selectors - pulled all test counts styling together and up (they're always the same regardless of section pass/fail state) * Adding .gitignore file * Removing diff test - diffing works fine, as the browser collapses whitespace in its output, but the test can't do that and isn't worth fixing. * Always synchronize the done-step (it'll set the timeout when necessary), fixes timing race conditions. * Insert location.href as an anchor around the header. Fixes issue #29 * - kill double ;; in escapeHtml. oops * Removed html escaping from QUnit.diff, as input is already escaped, only leads to double escaping. Replaced newlines with single whitespace. * Optimized and cleaned up CSS file * Making the reset-method non-global (only module, test and assertions should be global), and fixing the fixture reset by using jQuery's html() method again, doesn't work with innerHTML, yet * Introducing #qunit-fixture element, deprecating the (never documented) #main element. Doesn't require inline styles and is now independent of jQuery. * Amending previous commit: Remove jQuery-core specific resets (will be replaced within jQuery testsuite). Fixes issue #19 - QUnit.reset() removes global jQuery ajax event handlers * Remove jQuery-core specific resets (will be replaced within jQuery testsuite). Fixes issue #19 - QUnit.reset() removes global jQuery ajax event handlers * Cleaning up rubble from the previous commit. * Added raises assertion, reusing some of kensnyder's code. * Merged kensnyder's object detection code. Original message: Streamlined object detection and exposed QUnit.objectType as a function. * Fixed some bad formatting. * Move various QUnit properties below the globals-export to avoid init becoming a global method. Fixes issue #11 - Remove 'init' function from a global namespace * Improved output when expected != actual: Output both only then, and add a diff. Fixes issue #10 - Show diff if equal() or deepEqual() failed * Expand failed tests on load. Fixes issue #8 - Failed tests expanded on load * Set location.search for url-filtering instead of location.href. Fixes issue #7 - Modify location.search instead of location.href on test double-click * Add QUnit.begin() callback. Fixes issue #6 - Add 'start' callback. * add css style for result (".test-actual") in passed tests * Fixed output escaping by using leeoniya's custom escaping along with innerHTML. Also paves the way for outputting diffs. * Cleanup * Revert "Revert part of bad merge, back to using createTextNode" * Revert part of bad merge, back to using createTextNode * Fixed doubleclick-handler and filtering to rerun only a single test. * Add ability to css style a test's messages, expected and actual results. Merged from Leon Sorokin (leeoniya). * Remove space between module name and colon * - removed "module" wording from reports (unneeded and cluttery) - added and modified css to make module & test names styleable * Logging support for Each test can extend the module testEnvironment * Fixing whitespace * Update tests to use equal() and deepEqual() rather than the deprecated equals() and same() * Consistent argument names for deepEqual * Skip DOM part of jsDump test if using a SSJS environment without a DOM * Improve async testing by creating the result element before running the test, updating it later. If the test fails, its clear which test is the culprit. * Add autostart option to config. Set via QUnit.config.autostart = false; start later via QUnit.start() * Expose QUnit.config, but don't make config a global * Expose QUnit.config as global to make external workarounds easier * Merge branch 'asyncsetup' * Allowing async setup and teardown. Fixes https://github.com/jquery/qunit/issues#issue/20 * Always output expected and actual result (no reason not to). Fixes https://github.com/jquery/qunit/issues#issue/21 * More changes to the detection of types in jsDump's typeOf. * Change the typeOf checks in QUnit to be more accurate. * Added test for jsDump and modified its options to properly output results when document.createTextNode is used; currently tests for DOM elements cause a stackoverflow error in IEs, works fine, with the correct output, elsewhere * Always use jsDump to output result objects into messages, making the output for passing assertions more useful * Make it so that the display is updated, at least, once a second - also prevents scripts from executing for too long and causing problems. * added tests and patch for qunit.equiv to avoid circular references in objects and arrays * No reason to continue looping, we can stop at this point. Thanks to Chris Thatcher for the suggestion. * Use createTextNode instead of innerHTML for showing test result since expected and actual might be something that looks like a tag. * 'Test card' design added * switched green to blue for top-level pass + reduced padding * Bringing the QUnit API in line with the CommonJS API. * Explicitly set list-style-position: inside on result LIs. * Madness with border-radius. * Corrected banner styles for new class names * Added rounded corners and removed body rules for embedded tests * Resolving merge conflicts. * added colouring for value summary * adding some extra text colours * added styles for toolbar * added new styles * IE 6 and 7 weren't respecting the CSS rules for the banner, used a different technique instead. * Went a bit further and made extra-sure that the target was specified correctly. * Fixed problem where double-clicking an entry in IE caused an error to occur. * Path for https://dev.jquery.com/ticket/5426 - fix the microformat test result * Fixed test() to use 'expected' 2nd param * Remove the named function expressions, to stop Safari 2 from freaking out. Fixes #5. * Each test can extend the module testEnvironment * Extra test for current test environment * Make the current testEnvironment available to utility functions * typeOf in QUnit.jsDump now uses QUnit.is * hoozit in QUnit.equiv now uses QUnit.is * Properly set label attributes. * Some minor tweaks to RyanS' GETParams change. * left a console.log in :( * Took into account a fringe case when using qunit with testswarm. Trying to run all the tests with the extra url params from testswarm would make qunit look for a testsuite that did not exist * need to set config.currentModule to have correct names and working filters * Support logging of testEnvironment * async tests aren't possible on rhino * Fixed a missing QUnit.reset(). * The QUnit. prefix was missing from the uses of the start() method. * Merged lifecycle object into testEnvironment * "replacing totally wrong diff algorithm with a working one" Patch from kassens (manually applied). * fixing jslint errors in test.js * Fixed: testDone() was always called with 0 failures in CommonJS mode * Fixed: moduleDone() was invoked on first call to module() * Added a new asyncTest method - removes the need for having to call start() at the beginning of an asynchronous test. * Added support for expected numbers in the test method. * Fixed broken dynamic loading of tests (can now dynamically load tests and done still works properly). * Simplified the logic for calling 'done' and pushing off new tests - was causing too many inconsistencies otherwise. * Simplified the markup for the QUnit test test suite. * Realized that it's really easy to handle the case where stop() has been called and then an exception is thrown. * Added in better logging support. Now handle moduleStart/moduleDone and testStart/testDone. Also make sure that done only fires once at the end. * Made it so that you can reset the suite to an initial state (at which point tests can be dynamically loaded and run, for example). * Re-worked QUnit to handle dynamic loading of additional code (the 'done' code will be re-run after additional code is loaded). * Removed the old SVN version stuff. * Moved the QUnit source into a separate directory and updated the test suite/packages files. * Added in CommonJS support for exporting the QUnit functionality. * Missing quote from package.json. * Fixed trailing comma in package.json. * Added a CommonJS/Narwhal package.json file. * Accidentally reverted the jsDump/equiv changes that had been made. * Hide the filter toolbar if it's not needed. Also exposed the jsDump and equiv objects on QUnit. * Retooled the QUnit CSS to be more generic. * Renamed the QUnit files from testrunner/testsuite to QUnit. * Expose QUnit.equiv and QUnit.jsDump in QUnit. * Moved the QUnit test directory into the QUnit directory. * Reworked the QUnit CSS (moved jQuery-specific stuff out, made all the other selectors more specific). * Removed the #main reset for non-jQuery code (QUnit.reset can be overwritten with your own reset code). * Moved the QUnit toolbar inline. * Switched to using a qunit- prefix for special elements (banner, userAgent, and tests). * Missed a case in QUnit where an element was assumed to exist. * QUnit's isSet and isObj are no longer needed - you should use same instead. * Make sure that QUnit's equiv entity escaping is enabled by default (otherwise the output gets kind of crazy). * Refactored QUnit, completely reorganized the structure of the file. Additionally made it so that QUnit can run outside of a browser (inside Rhino, for example). * Removed some legacy and jQuery-specific test methods. * Added callbacks for tests and modules. It's now possible to reproduce the full display of the testrunner without using the regular rendering. * QUnit no longer depends upon rendering the results (it can work simply by using the logging callbacks). * Made QUnit no longer require jQuery (it is now a standalone, framework independent, test runner). * Reverted the noglobals changed from QUnit - causing chaos in the jQuery test suite. * qunit: removed noglobals flag, instead always check for globals after teardown; if a test has to introduce a global "myVar", use delete window.myVar in teardown or at the end of a test * qunit: don't child selectors when IE should behave nicely, too * qunit: improvement for the test-scope: create a new object and call setup, the test, and teardown in the scope of that object - allows you to provide test fixtures to each test without messing with global data; kudos to Martin Häcker for the contribution * qunit: added missing semicolons * qunit: fixed a semicolon, that should have been a comma * QUnit: implemented error handling for Opera as proposed by #3628 * qunit: fix for https://dev.jquery.com/ticket/3215 changing wording of testresults, to something more positive (x of y passed, z failed) * QUnit: testrunner.js: Ensures equality of types (String, Boolean, Number) declared with the 'new' prefix. See comments #3, #4 and #5 on http://philrathe.com/articles/equiv * qunit: wrap name of test in span when a module is used for better styling * qunit: auto-prepend default mark (#header, #banner, #userAgent, #tests) when not present * Landing some changes to add logging to QUnit (so that it's easier to hook in to when a test finishes). * Added checkbox for hiding missing tests (tests that fail with the text 'missing test - untested code is broken code') * qunit: eol-style:native and mime-type * HTML being injected for the test result wasn't valid HTML. * qunit: setting mimetype for testsuite.css * qunit: update to Ariel's noglobals patch to support async tests as well * Landing Ariel's change - checks for global variable leakage. * qunit: run module-teardown in its own synchronize block to synchronize with async tests (ugh) * qunit: same: equiv - completely refactored in the testrunner. * testrunner.js: - Update equiv to support Date and RegExp. - Change behavior when comparing function: - abort when in an instance of Object (when references comparison failed) - skip otherwise (like before) * qunit: code refactoring and cleanup * QUnit: update equiv to latest version, handling multiple arguments and NaN, see http://philrathe.com/articles/equiv * QUnit: cleanup, deprecating compare, compare2 and serialArray: usage now throws an error with a helpful message * QUnit: optional timeout argument for stop, while making tests undetermined, useful for debugging * QUnit: added toolbar with "hide passed tests" checkbox to help focus on failed tests * QUnit: minor output formatting * QUnit: adding same-assertion for a recursive comparison of primitive values, arrays and objects, thanks to Philippe Rathé for the contribution, including tests * QUnit: adding same-assertion for a recursive comparison of primitive values, arrays and objects, thanks to Philippe Rathé for the contribution, including tests * QUnit: adding same-assertion for a recursive comparison of primitive values, arrays and objects, thanks to Philippe Rathé for the contribution, including tests * qunit: use window.load to initialize tests, allowing other code to run on document-ready before starting to run tests * qunit: allow either setup or teardown, instead of both or nothing * qunit: make everything private by default, expose only public API; removed old timeout-option (non-deterministic, disabled for a long time anyway); use local $ reference instead of global jQuery reference; minor code cleanup (var config instead of _config; queue.shift instead of slice) * qunit: added support for module level setup/teardown callbacks * qunit: modified example for equals to avoid confusion with parameter ordering * qunit: added id/classes to result element to enable integration with browser automation tools, see http://docs.jquery.com/QUnit#Integration_into_Browser_Automation_Tools * qunit: replaced $ alias with jQuery (merged from jquery/test/data/testrunner.js) * qunit: fixed inline documentation for equals * qunit testrunner - catch and log possible error during reset() * QUnit: Switched out Date and Rev for Id. * qunit: when errors are thrown in a test, the message is successfully show on all browsers. * qunit: added license header * qunit: moved jquery testrunner to top-level project, see http://docs.jquery.com/QUnit * Share project 'qunit' into 'https://jqueryjs.googlecode.com/svn' qunit-1.22.0/LICENSE.txt000066400000000000000000000030661266310060400145620ustar00rootroot00000000000000Copyright jQuery Foundation and other contributors, https://jquery.org/ This software consists of voluntary contributions made by many individuals. For exact contribution history, see the revision history available at https://github.com/jquery/qunit The following license applies to all parts of this software except as documented below: ==== Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== All files located in the node_modules directory are externally maintained libraries used by this software which have their own licenses; we recommend you read them, as their terms may differ from the terms above. qunit-1.22.0/README.md000066400000000000000000000056261266310060400142220ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/jquery/qunit.svg?branch=master)](https://travis-ci.org/jquery/qunit) [![Coverage Status](https://coveralls.io/repos/jquery/qunit/badge.svg)](https://coveralls.io/github/jquery/qunit) # [QUnit](https://qunitjs.com) - A JavaScript Unit Testing Framework. QUnit is a powerful, easy-to-use, JavaScript unit testing framework. It's used by the jQuery project to test its code and plugins but is capable of testing any generic JavaScript code (and even capable of testing JavaScript code on the server-side). QUnit is especially useful for regression testing: Whenever a bug is reported, write a test that asserts the existence of that particular bug. Then fix it and commit both. Every time you work on the code again, run the tests. If the bug comes up again - a regression - you'll spot it immediately and know how to fix it, because you know what code you just changed. Having good unit test coverage makes safe refactoring easy and cheap. You can run the tests after each small refactoring step and always know what change broke something. QUnit is similar to other unit testing frameworks like JUnit, but makes use of the features JavaScript provides and helps with testing code in the browser, e.g. with its stop/start facilities for testing asynchronous code. If you are interested in helping developing QUnit, you are in the right place. For related discussions, visit the [QUnit and Testing forum](https://forum.jquery.com/qunit-and-testing). ## Development To submit patches, fork the repository, create a branch for the change. Then implement the change, run `npm test` to lint and test it, then commit, push and create a pull request. Include some background for the change in the commit message and `Fixes #nnn`, referring to the issue number you're addressing. To run `npm test`, you need [Node.js](https://nodejs.org/download/), which includes `npm`. ## Releases Use [jquery-release](https://github.com/jquery/jquery-release). The following aren't handled there, do that first: * Install [git-extras](https://github.com/visionmedia/git-extras) and run `git changelog` to update `History.md`. Clean up the changelog, removing merge commits, whitespace cleanups or other irrelevant commits. * Run `grunt authors` and add any new authors to AUTHORS.txt * Update the version property in `package.json` to have the right -pre version. Not necessary for patch releases. Commit these: Build: Prepare @VERSION release, including authors and history update Then run the script: node release.js --remote=jquery/qunit Update `jquery/qunitjs.com`, replacing previous versions with new ones: * pages/index.html * resources/*.html Update [GitHub releases](https://github.com/jquery/qunit/releases), use the changelog from `History.md`. Finally announce on Twitter @qunitjs (add highlights if possible, otherwise a 2nd tweet might do): Released @VERSION: https://github.com/jquery/qunit/releases/tag/1.17.0 qunit-1.22.0/bower.json000066400000000000000000000004551266310060400147470ustar00rootroot00000000000000{ "name": "qunit", "main": [ "qunit/qunit.js", "qunit/qunit.css" ], "license": "https://github.com/jquery/qunit/blob/master/LICENSE.txt", "ignore": [ "**/.*", "!LICENSE.txt", "package.json", "Gruntfile.js", "node_modules", "test" ], "version": "1.22.0" } qunit-1.22.0/build/000077500000000000000000000000001266310060400140315ustar00rootroot00000000000000qunit-1.22.0/build/browserstack-current-1.json000066400000000000000000000005441266310060400212560ustar00rootroot00000000000000{ "username": "BROWSERSTACK_USERNAME", "key": "BROWSERSTACK_KEY", "test_framework": "qunit", "test_path": [ "test/index.html", "test/logs.html" ], "browsers": [ "chrome_current", "firefox_current", "opera_current", "safari_current", "ie_11", "edge_latest", { "os": "iOS", "os_version": "8.3", "real_mobile": false } ] } qunit-1.22.0/build/browserstack-current-2.json000066400000000000000000000006011266310060400212510ustar00rootroot00000000000000{ "username": "BROWSERSTACK_USERNAME", "key": "BROWSERSTACK_KEY", "test_framework": "qunit", "test_path": [ "test/startError.html", "test/amd.html", "test/autostart.html" ], "browsers": [ "chrome_current", "firefox_current", "opera_current", "safari_current", "ie_11", "edge_latest", { "os": "iOS", "os_version": "8.3", "real_mobile": false } ] } qunit-1.22.0/build/browserstack-legacy-1.json000066400000000000000000000007221266310060400210360ustar00rootroot00000000000000{ "username": "BROWSERSTACK_USERNAME", "key": "BROWSERSTACK_KEY", "test_framework": "qunit", "test_path": [ "test/index.html", "test/logs.html" ], "browsers": [ "chrome_previous", "firefox_previous", "opera_previous", { "browser": "safari", "browser_version": "8.0" }, "ie_9", "ie_10", { "os": "iOS", "os_version": "6.0", "real_mobile": false }, { "os": "iOS", "os_version": "7.0", "real_mobile": false } ] } qunit-1.22.0/build/browserstack-legacy-2.json000066400000000000000000000007571266310060400210470ustar00rootroot00000000000000{ "username": "BROWSERSTACK_USERNAME", "key": "BROWSERSTACK_KEY", "test_framework": "qunit", "test_path": [ "test/startError.html", "test/amd.html", "test/autostart.html" ], "browsers": [ "chrome_previous", "firefox_previous", "opera_previous", { "browser": "safari", "browser_version": "8.0" }, "ie_9", "ie_10", { "os": "iOS", "os_version": "6.0", "real_mobile": false }, { "os": "iOS", "os_version": "7.0", "real_mobile": false } ] } qunit-1.22.0/build/release.js000066400000000000000000000013271266310060400160120ustar00rootroot00000000000000/*jshint node:true */ module.exports = function( Release ) { var shell = require( "shelljs" ); Release.define({ npmPublish: true, issueTracker: "github", changelogShell: function() { return "# Changelog for QUnit v" + Release.newVersion + "\n"; }, generateArtifacts: function( done ) { Release.exec( "grunt", "Grunt command failed" ); shell.mkdir( "-p", "qunit" ); shell.cp( "-r", "dist/*", "qunit/" ); shell.mkdir( "-p", "dist/cdn" ); shell.cp( "dist/qunit.js", "dist/cdn/qunit-" + Release.newVersion + ".js" ); shell.cp( "dist/qunit.css", "dist/cdn/qunit-" + Release.newVersion + ".css" ); done([ "qunit/qunit.js", "qunit/qunit.css" ]); } }); }; module.exports.dependencies = [ "shelljs@0.2.6" ]; qunit-1.22.0/build/run-browserstack.sh000066400000000000000000000004511266310060400177000ustar00rootroot00000000000000#!/bin/bash for run in \ "./build/browserstack-current-1.json" \ "./build/browserstack-current-2.json" \ "./build/browserstack-legacy-1.json" \ "./build/browserstack-legacy-2.json" do export BROWSERSTACK_JSON=$run if ! node_modules/.bin/browserstack-runner ; then exit 1 fi done exit 0 qunit-1.22.0/build/tasks/000077500000000000000000000000001266310060400151565ustar00rootroot00000000000000qunit-1.22.0/build/tasks/test-on-node.js000066400000000000000000000044701266310060400200350ustar00rootroot00000000000000/*jshint node:true */ "use strict"; var async = require( "async" ); var path = require( "path" ); module.exports = function( grunt ) { grunt.registerMultiTask( "test-on-node", function() { var runs = this.data.map( function( file ) { return function( runEnd ) { runQUnit( file, runEnd ); }; }); var done = this.async(); async.series( runs, function( error, result ) { var total = result.reduce(function( previous, details ) { return previous + details.total; }, 0 ); var failed = result.reduce(function( previous, details ) { return previous + details.failed; }, 0 ); var runtime = result.reduce(function( previous, details ) { return previous + details.runtime; }, 0 ); grunt.log.writeln( "-----" ); grunt.log.ok( total + " total assertions (in " + runtime + "ms) , " + "with " + failed + " failed assertions" ); done( !error ); }); }); function runQUnit( file, runEnd ) { // Resolve current QUnit path and remove it from the require cache // to avoid stacking the QUnit logs. var QUnitFile = path.resolve( __dirname, "../../dist/qunit.js" ); delete require.cache[ QUnitFile ]; var QUnit = require( QUnitFile ); // Expose QUnit to the global scope to be seen on the other tests. global.QUnit = QUnit; registerEvents( QUnit, file, runEnd ); QUnit.config.autorun = false; require( "../../" + file ); QUnit.load(); } function registerEvents( QUnit, file, runEnd ) { var runDone = false; var testActive = false; QUnit.begin( function() { grunt.log.ok( "Testing " + file + " ..." ); }); QUnit.testStart( function() { testActive = true; }); QUnit.log( function( details ) { if ( !testActive || details.result ) { return; } var message = "name: " + details.name + " module: " + details.module + " message: " + details.message; grunt.log.error( message ); }); QUnit.testDone( function() { testActive = false; }); QUnit.done( function( details ) { if ( runDone ) { return; } var message = details.total + " assertions (in " + details.runtime + "ms), passed: " + details.passed + ", failed: " + details.failed; if ( details.failed ) { grunt.log.error( message ); } else { grunt.log.ok( message ); } runDone = true; runEnd( details.failed, details ); }); } }; qunit-1.22.0/package.json000066400000000000000000000031041266310060400152160ustar00rootroot00000000000000{ "name": "qunitjs", "title": "QUnit", "description": "An easy-to-use JavaScript Unit Testing framework.", "version": "1.22.0", "homepage": "https://qunitjs.com", "author": { "name": "jQuery Foundation and other contributors", "url": "https://github.com/jquery/qunit/blob/1.22.0/AUTHORS.txt" }, "repository": { "type": "git", "url": "git://github.com/jquery/qunit.git" }, "keywords": [ "testing", "unit", "jquery" ], "bugs": { "url": "https://github.com/jquery/qunit/issues" }, "license": "MIT", "files": [ "qunit/qunit.js", "qunit/qunit.css", "LICENSE.txt" ], "dependencies": {}, "devDependencies": { "async": "1.4.2", "browserstack-runner": "0.3.7", "commitplease": "2.0.0", "grunt": "0.4.5", "grunt-cli": "0.1.13", "grunt-concurrent": "2.0.3", "grunt-contrib-concat": "0.3.0", "grunt-contrib-jshint": "0.11.2", "grunt-contrib-watch": "0.5.3", "grunt-coveralls": "1.0.0", "grunt-git-authors": "3.0.0", "grunt-jscs": "0.8.1", "grunt-qunit-istanbul": "0.5.0", "grunt-search": "0.1.6", "load-grunt-tasks": "0.3.0", "requirejs": "2.1.16" }, "scripts": { "browserstack": "sh build/run-browserstack.sh", "ci": "grunt && grunt coveralls && npm run browserstack", "test": "grunt", "prepublish": "grunt build" }, "commitplease": { "components": [ "All", "Assert", "Build", "CSS", "Core", "Dump", "HTML Reporter", "Readme", "Test", "Tests" ] }, "main": "qunit/qunit.js" } qunit-1.22.0/qunit/000077500000000000000000000000001266310060400140725ustar00rootroot00000000000000qunit-1.22.0/qunit/qunit.css000066400000000000000000000124271266310060400157520ustar00rootroot00000000000000/*! * QUnit 1.22.0 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2016-02-23T15:57Z */ /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } /** Header */ #qunit-header { padding: 0.5em 0 0.5em 1em; color: #8699A4; background-color: #0D3349; font-size: 1.5em; line-height: 1em; font-weight: 400; border-radius: 5px 5px 0 0; } #qunit-header a { text-decoration: none; color: #C2CCD1; } #qunit-header a:hover, #qunit-header a:focus { color: #FFF; } #qunit-testrunner-toolbar label { display: inline-block; padding: 0 0.5em 0 0.1em; } #qunit-banner { height: 5px; } #qunit-testrunner-toolbar { padding: 0.5em 1em 0.5em 1em; color: #5E740B; background-color: #EEE; overflow: hidden; } #qunit-filteredTest { padding: 0.5em 1em 0.5em 1em; background-color: #F4FF77; color: #366097; } #qunit-userAgent { padding: 0.5em 1em 0.5em 1em; background-color: #2B81AF; color: #FFF; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } #qunit-modulefilter-container { float: right; padding: 0.2em; } .qunit-url-config { display: inline-block; padding: 0.1em; } .qunit-filter { display: block; float: right; margin-left: 1em; } /** Tests: Pass/Fail */ #qunit-tests { list-style-position: inside; } #qunit-tests li { padding: 0.4em 1em 0.4em 1em; border-bottom: 1px solid #FFF; list-style-position: inside; } #qunit-tests > li { display: none; } #qunit-tests li.running, #qunit-tests li.pass, #qunit-tests li.fail, #qunit-tests li.skipped { display: list-item; } #qunit-tests.hidepass { position: relative; } #qunit-tests.hidepass li.running, #qunit-tests.hidepass li.pass { visibility: hidden; position: absolute; width: 0; height: 0; padding: 0; border: 0; margin: 0; } #qunit-tests li strong { cursor: pointer; } #qunit-tests li.skipped strong { cursor: default; } #qunit-tests li a { padding: 0.5em; color: #C2CCD1; text-decoration: none; } #qunit-tests li p a { padding: 0.25em; color: #6B6464; } #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } #qunit-tests li .runtime { float: right; font-size: smaller; } .qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #FFF; border-radius: 5px; } .qunit-source { margin: 0.6em 0 0.3em; } .qunit-collapsed { display: none; } #qunit-tests table { border-collapse: collapse; margin-top: 0.2em; } #qunit-tests th { text-align: right; vertical-align: top; padding: 0 0.5em 0 0; } #qunit-tests td { vertical-align: top; } #qunit-tests pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } #qunit-tests del { background-color: #E0F2BE; color: #374E0C; text-decoration: none; } #qunit-tests ins { background-color: #FFCACA; color: #500; text-decoration: none; } /*** Test Counts */ #qunit-tests b.counts { color: #000; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; background-color: #FFF; border-bottom: none; list-style-position: inside; } /*** Passing Styles */ #qunit-tests li li.pass { color: #3C510C; background-color: #FFF; border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999; } #qunit-banner.qunit-pass { background-color: #C6E746; } /*** Failing Styles */ #qunit-tests li li.fail { color: #710909; background-color: #FFF; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; } #qunit-tests .fail { color: #000; background-color: #EE5757; } #qunit-tests .fail .test-name, #qunit-tests .fail .module-name { color: #000; } #qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-expected { color: #008000; } #qunit-banner.qunit-fail { background-color: #EE5757; } /*** Skipped tests */ #qunit-tests .skipped { background-color: #EBECE9; } #qunit-tests .qunit-skipped-label { background-color: #F4FF77; display: inline-block; font-style: normal; color: #366097; line-height: 1.8em; padding: 0 0.5em; margin: -0.4em 0.4em -0.4em 0; } /** Result */ #qunit-testresult { padding: 0.5em 1em 0.5em 1em; color: #2B81AF; background-color: #D2E0E6; border-bottom: 1px solid #FFF; } #qunit-testresult .module-name { font-weight: 700; } /** Fixture */ #qunit-fixture { position: absolute; top: -10000px; left: -10000px; width: 1000px; height: 1000px; } qunit-1.22.0/qunit/qunit.js000066400000000000000000003345151266310060400156030ustar00rootroot00000000000000/*! * QUnit 1.22.0 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2016-02-23T15:57Z */ (function( global ) { var QUnit = {}; var Date = global.Date; var now = Date.now || function() { return new Date().getTime(); }; var setTimeout = global.setTimeout; var clearTimeout = global.clearTimeout; // Store a local window from the global to allow direct references. var window = global.window; var defined = { document: window && window.document !== undefined, setTimeout: setTimeout !== undefined, sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch ( e ) { return false; } }() ) }; var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); var globalStartCalled = false; var runStarted = false; var toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty; // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var i, j, result = a.slice(); for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { if ( result[ i ] === b[ j ] ) { result.splice( i, 1 ); i--; break; } } } return result; } // from jquery.js function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ function objectValues ( obj ) { var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { val = obj[ key ]; vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } } return vals; } function extend( a, b, undefOnly ) { for ( var prop in b ) { if ( hasOwn.call( b, prop ) ) { // Avoid "Member not found" error in IE8 caused by messing with window.constructor // This block runs on every environment, so `global` is being used instead of `window` // to avoid errors on node. if ( prop !== "constructor" || a !== global ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { a[ prop ] = b[ prop ]; } } } } return a; } function objectType( obj ) { if ( typeof obj === "undefined" ) { return "undefined"; } // Consider: typeof null === object if ( obj === null ) { return "null"; } var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), type = match && match[ 1 ]; switch ( type ) { case "Number": if ( isNaN( obj ) ) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Set": case "Map": case "Date": case "RegExp": case "Function": case "Symbol": return type.toLowerCase(); } if ( typeof obj === "object" ) { return "object"; } } // Safe object type checking function is( type, obj ) { return QUnit.objectType( obj ) === type; } var getUrlParams = function() { var i, param, name, value; var urlParams = {}; var location = window.location; var params = location.search.slice( 1 ).split( "&" ); var length = params.length; for ( i = 0; i < length; i++ ) { if ( params[ i ] ) { param = params[ i ].split( "=" ); name = decodeURIComponent( param[ 0 ] ); // allow just a key to turn on a flag, e.g., test.html?noglobals value = param.length === 1 || decodeURIComponent( param.slice( 1 ).join( "=" ) ) ; if ( urlParams[ name ] ) { urlParams[ name ] = [].concat( urlParams[ name ], value ); } else { urlParams[ name ] = value; } } } return urlParams; }; // Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 4 : offset; var stack, include, i; if ( e.stack ) { stack = e.stack.split( "\n" ); if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); } if ( fileName ) { include = []; for ( i = offset; i < stack.length; i++ ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) { break; } include.push( stack[ i ] ); } if ( include.length ) { return include.join( "\n" ); } } return stack[ offset ]; // Support: Safari <=6 only } else if ( e.sourceURL ) { // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } function sourceFromStacktrace( offset ) { var error = new Error(); // Support: Safari <=7 only, IE <=10 - 11 only // Not all browsers generate the `stack` property for `new Error()`, see also #636 if ( !error.stack ) { try { throw error; } catch ( err ) { error = err; } } return extractStacktrace( error, offset ); } /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ var config = { // The queue of tests to run queue: [], // block until document ready blocking: true, // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // by default, modify document.title when suite is done altertitle: true, // HTML Reporter: collapse every test except the first failing test // If false, all failing tests will be expanded collapse: true, // by default, scroll to top of the page when suite is done scrolltop: true, // depth up-to which object will be dumped maxDepth: 5, // when enabled, all tests must call expect() requireExpects: false, // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ { id: "hidepassed", label: "Hide passed tests", tooltip: "Only show tests and assertions that fail. Stored as query-strings." }, { id: "noglobals", label: "Check for Globals", tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." } ], // Set of all modules. modules: [], // Stack of nested modules moduleStack: [], // The first unnamed module currentModule: { name: "", tests: [] }, callbacks: {} }; var urlParams = defined.document ? getUrlParams() : {}; // Push a loose unnamed module to the modules collection config.modules.push( config.currentModule ); if ( urlParams.filter === true ) { delete urlParams.filter; } // String search anywhere in moduleName+testName config.filter = urlParams.filter; config.testId = []; if ( urlParams.testId ) { // Ensure that urlParams.testId is an array urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); for (var i = 0; i < urlParams.testId.length; i++ ) { config.testId.push( urlParams.testId[ i ] ); } } var loggingCallbacks = {}; // Register logging callbacks function registerLoggingCallbacks( obj ) { var i, l, key, callbackNames = [ "begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone" ]; function registerLoggingCallback( key ) { var loggingCallback = function( callback ) { if ( objectType( callback ) !== "function" ) { throw new Error( "QUnit logging methods require a callback function as their first parameters." ); } config.callbacks[ key ].push( callback ); }; // DEPRECATED: This will be removed on QUnit 2.0.0+ // Stores the registered functions allowing restoring // at verifyLoggingCallbacks() if modified loggingCallbacks[ key ] = loggingCallback; return loggingCallback; } for ( i = 0, l = callbackNames.length; i < l; i++ ) { key = callbackNames[ i ]; // Initialize key collection of logging callback if ( objectType( config.callbacks[ key ] ) === "undefined" ) { config.callbacks[ key ] = []; } obj[ key ] = registerLoggingCallback( key ); } } function runLoggingCallbacks( key, args ) { var i, l, callbacks; callbacks = config.callbacks[ key ]; for ( i = 0, l = callbacks.length; i < l; i++ ) { callbacks[ i ]( args ); } } // DEPRECATED: This will be removed on 2.0.0+ // This function verifies if the loggingCallbacks were modified by the user // If so, it will restore it, assign the given callback and print a console warning function verifyLoggingCallbacks() { var loggingCallback, userCallback; for ( loggingCallback in loggingCallbacks ) { if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { userCallback = QUnit[ loggingCallback ]; // Restore the callback function QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; // Assign the deprecated given callback QUnit[ loggingCallback ]( userCallback ); if ( global.console && global.console.warn ) { global.console.warn( "QUnit." + loggingCallback + " was replaced with a new value.\n" + "Please, check out the documentation on how to apply logging callbacks.\n" + "Reference: https://api.qunitjs.com/category/callbacks/" ); } } } } ( function() { if ( !defined.document ) { return; } // `onErrorFnPrev` initialized at top of scope // Preserve other handlers var onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will suppress the default browser handler, // returning false will let it run. window.onerror = function( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); } // Treat return value as window.onerror itself does, // Only do our handling if not suppressed. if ( ret !== true ) { if ( QUnit.config.current ) { if ( QUnit.config.current.ignoreGlobalErrors ) { return true; } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { QUnit.test( "global failure", extend(function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); }, { validTest: true } ) ); } return false; } return ret; }; } )(); QUnit.urlParams = urlParams; // Figure out if we're running the tests from a server or not QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); // Expose the current QUnit version QUnit.version = "1.22.0"; extend( QUnit, { // call on start of module test to prepend name to all tests module: function( name, testEnvironment, executeNow ) { var module, moduleFns; var currentModule = config.currentModule; if ( arguments.length === 2 ) { if ( testEnvironment instanceof Function ) { executeNow = testEnvironment; testEnvironment = undefined; } } // DEPRECATED: handles setup/teardown functions, // beforeEach and afterEach should be used instead if ( testEnvironment && testEnvironment.setup ) { testEnvironment.beforeEach = testEnvironment.setup; delete testEnvironment.setup; } if ( testEnvironment && testEnvironment.teardown ) { testEnvironment.afterEach = testEnvironment.teardown; delete testEnvironment.teardown; } module = createModule(); moduleFns = { beforeEach: setHook( module, "beforeEach" ), afterEach: setHook( module, "afterEach" ) }; if ( executeNow instanceof Function ) { config.moduleStack.push( module ); setCurrentModule( module ); executeNow.call( module.testEnvironment, moduleFns ); config.moduleStack.pop(); module = module.parentModule || currentModule; } setCurrentModule( module ); function createModule() { var parentModule = config.moduleStack.length ? config.moduleStack.slice( -1 )[ 0 ] : null; var moduleName = parentModule !== null ? [ parentModule.name, name ].join( " > " ) : name; var module = { name: moduleName, parentModule: parentModule, tests: [] }; var env = {}; if ( parentModule ) { extend( env, parentModule.testEnvironment ); delete env.beforeEach; delete env.afterEach; } extend( env, testEnvironment ); module.testEnvironment = env; config.modules.push( module ); return module; } function setCurrentModule( module ) { config.currentModule = module; } }, // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. asyncTest: asyncTest, test: test, skip: skip, only: only, // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. start: function( count ) { var globalStartAlreadyCalled = globalStartCalled; if ( !config.current ) { globalStartCalled = true; if ( runStarted ) { throw new Error( "Called start() outside of a test context while already started" ); } else if ( globalStartAlreadyCalled || count > 1 ) { throw new Error( "Called start() outside of a test context too many times" ); } else if ( config.autostart ) { throw new Error( "Called start() outside of a test context when " + "QUnit.config.autostart was true" ); } else if ( !config.pageLoaded ) { // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it config.autostart = true; return; } } else { // If a test is running, adjust its semaphore config.current.semaphore -= count || 1; // If semaphore is non-numeric, throw error if ( isNaN( config.current.semaphore ) ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() with a non-numeric decrement.", sourceFromStacktrace( 2 ) ); return; } // Don't start until equal number of stop-calls if ( config.current.semaphore > 0 ) { return; } // throw an Error if start is called more often than stop if ( config.current.semaphore < 0 ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() while already started (test's semaphore was 0 already)", sourceFromStacktrace( 2 ) ); return; } } resumeProcessing(); }, // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. stop: function( count ) { // If there isn't a test running, don't allow QUnit.stop() to be called if ( !config.current ) { throw new Error( "Called stop() outside of a test context" ); } // If a test is running, adjust its semaphore config.current.semaphore += count || 1; pauseProcessing(); }, config: config, is: is, objectType: objectType, extend: extend, load: function() { config.pageLoaded = true; // Initialize the configuration options extend( config, { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: 0, updateRate: 1000, autostart: true, filter: "" }, true ); config.blocking = false; if ( config.autostart ) { resumeProcessing(); } }, stack: function( offset ) { offset = ( offset || 0 ) + 2; return sourceFromStacktrace( offset ); } }); registerLoggingCallbacks( QUnit ); function begin() { var i, l, modulesLog = []; // If the test run hasn't officially begun yet if ( !config.started ) { // Record the time of the test run's beginning config.started = now(); verifyLoggingCallbacks(); // Delete the loose unnamed module if unused. if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { config.modules.shift(); } // Avoid unnecessary information by not logging modules' test environments for ( i = 0, l = config.modules.length; i < l; i++ ) { modulesLog.push({ name: config.modules[ i ].name, tests: config.modules[ i ].tests }); } // The test run is officially beginning now runLoggingCallbacks( "begin", { totalTests: Test.count, modules: modulesLog }); } config.blocking = false; process( true ); } function process( last ) { function next() { process( last ); } var start = now(); config.depth = ( config.depth || 0 ) + 1; while ( config.queue.length && !config.blocking ) { if ( !defined.setTimeout || config.updateRate <= 0 || ( ( now() - start ) < config.updateRate ) ) { if ( config.current ) { // Reset async tracking for each phase of the Test lifecycle config.current.usedAsync = false; } config.queue.shift()(); } else { setTimeout( next, 13 ); break; } } config.depth--; if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } function pauseProcessing() { config.blocking = true; if ( config.testTimeout && defined.setTimeout ) { clearTimeout( config.timeout ); config.timeout = setTimeout(function() { if ( config.current ) { config.current.semaphore = 0; QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); } else { throw new Error( "Test timed out" ); } resumeProcessing(); }, config.testTimeout ); } } function resumeProcessing() { runStarted = true; // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) if ( defined.setTimeout ) { setTimeout(function() { if ( config.current && config.current.semaphore > 0 ) { return; } if ( config.timeout ) { clearTimeout( config.timeout ); } begin(); }, 13 ); } else { begin(); } } function done() { var runtime, passed; config.autorun = true; // Log the last module results if ( config.previousModule ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started }); } delete config.previousModule; runtime = now() - config.started; passed = config.stats.all - config.stats.bad; runLoggingCallbacks( "done", { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime }); } function setHook( module, hookName ) { if ( module.testEnvironment === undefined ) { module.testEnvironment = {}; } return function( callback ) { module.testEnvironment[ hookName ] = callback; }; } var focused = false; var priorityCount = 0; function Test( settings ) { var i, l; ++Test.count; extend( this, settings ); this.assertions = []; this.semaphore = 0; this.usedAsync = false; this.module = config.currentModule; this.stack = sourceFromStacktrace( 3 ); // Register unique strings for ( i = 0, l = this.module.tests; i < l.length; i++ ) { if ( this.module.tests[ i ].name === this.testName ) { this.testName += " "; } } this.testId = generateHash( this.module.name, this.testName ); this.module.tests.push({ name: this.testName, testId: this.testId }); if ( settings.skip ) { // Skipped tests will fully ignore any sent callback this.callback = function() {}; this.async = false; this.expected = 0; } else { this.assert = new Assert( this ); } } Test.count = 0; Test.prototype = { before: function() { if ( // Emit moduleStart when we're switching from one module to another this.module !== config.previousModule || // They could be equal (both undefined) but if the previousModule property doesn't // yet exist it means this is the first test in a suite that isn't wrapped in a // module, in which case we'll just emit a moduleStart event for 'undefined'. // Without this, reporters can get testStart before moduleStart which is a problem. !hasOwn.call( config, "previousModule" ) ) { if ( hasOwn.call( config, "previousModule" ) ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started }); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0, started: now() }; runLoggingCallbacks( "moduleStart", { name: this.module.name, tests: this.module.tests }); } config.current = this; if ( this.module.testEnvironment ) { delete this.module.testEnvironment.beforeEach; delete this.module.testEnvironment.afterEach; } this.testEnvironment = extend( {}, this.module.testEnvironment ); this.started = now(); runLoggingCallbacks( "testStart", { name: this.testName, module: this.module.name, testId: this.testId }); if ( !config.pollution ) { saveGlobal(); } }, run: function() { var promise; config.current = this; if ( this.async ) { QUnit.stop(); } this.callbackStarted = now(); if ( config.notrycatch ) { runTest( this ); return; } try { runTest( this ); } catch ( e ) { this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { QUnit.start(); } } function runTest( test ) { promise = test.callback.call( test.testEnvironment, test.assert ); test.resolvePromise( promise ); } }, after: function() { checkPollution(); }, queueHook: function( hook, hookName ) { var promise, test = this; return function runHook() { config.current = test; if ( config.notrycatch ) { callHook(); return; } try { callHook(); } catch ( error ) { test.pushFailure( hookName + " failed on " + test.testName + ": " + ( error.message || error ), extractStacktrace( error, 0 ) ); } function callHook() { promise = hook.call( test.testEnvironment, test.assert ); test.resolvePromise( promise, hookName ); } }; }, // Currently only used for module level hooks, can be used to add global level ones hooks: function( handler ) { var hooks = []; function processHooks( test, module ) { if ( module.parentModule ) { processHooks( test, module.parentModule ); } if ( module.testEnvironment && QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); } } // Hooks are ignored on skipped tests if ( !this.skip ) { processHooks( this, this.module ); } return hooks; }, finish: function() { config.current = this; if ( config.requireExpects && this.expected === null ) { this.pushFailure( "Expected number of assertions to be defined, but expect() was " + "not called.", this.stack ); } else if ( this.expected !== null && this.expected !== this.assertions.length ) { this.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); } else if ( this.expected === null && !this.assertions.length ) { this.pushFailure( "Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack ); } var i, bad = 0; this.runtime = now() - this.started; config.stats.all += this.assertions.length; config.moduleStats.all += this.assertions.length; for ( i = 0; i < this.assertions.length; i++ ) { if ( !this.assertions[ i ].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } runLoggingCallbacks( "testDone", { name: this.testName, module: this.module.name, skipped: !!this.skip, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, runtime: this.runtime, // HTML Reporter use assertions: this.assertions, testId: this.testId, // Source of Test source: this.stack, // DEPRECATED: this property will be removed in 2.0.0, use runtime instead duration: this.runtime }); // QUnit.reset() is deprecated and will be replaced for a new // fixture reset function on QUnit 2.0/2.1. // It's still called here for backwards compatibility handling QUnit.reset(); config.current = undefined; }, queue: function() { var priority, test = this; if ( !this.valid() ) { return; } function run() { // each of these can by async synchronize([ function() { test.before(); }, test.hooks( "beforeEach" ), function() { test.run(); }, test.hooks( "afterEach" ).reverse(), function() { test.after(); }, function() { test.finish(); } ]); } // Prioritize previously failed tests, detected from sessionStorage priority = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); return synchronize( run, priority ); }, pushResult: function( resultInfo ) { // resultInfo = { result, actual, expected, message, negative } var source, details = { module: this.module.name, name: this.testName, result: resultInfo.result, message: resultInfo.message, actual: resultInfo.actual, expected: resultInfo.expected, testId: this.testId, negative: resultInfo.negative || false, runtime: now() - this.started }; if ( !resultInfo.result ) { source = sourceFromStacktrace(); if ( source ) { details.source = source; } } runLoggingCallbacks( "log", details ); this.assertions.push({ result: !!resultInfo.result, message: resultInfo.message }); }, pushFailure: function( message, source, actual ) { if ( !( this instanceof Test ) ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) ); } var details = { module: this.module.name, name: this.testName, result: false, message: message || "error", actual: actual || null, testId: this.testId, runtime: now() - this.started }; if ( source ) { details.source = source; } runLoggingCallbacks( "log", details ); this.assertions.push({ result: false, message: message }); }, resolvePromise: function( promise, phase ) { var then, message, test = this; if ( promise != null ) { then = promise.then; if ( QUnit.objectType( then ) === "function" ) { QUnit.stop(); then.call( promise, function() { QUnit.start(); }, function( error ) { message = "Promise rejected " + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + " " + test.testName + ": " + ( error.message || error ); test.pushFailure( message, extractStacktrace( error, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Unblock QUnit.start(); } ); } } }, valid: function() { var filter = config.filter, regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), fullName = ( this.module.name + ": " + this.testName ); function testInModuleChain( testModule ) { var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; if ( testModuleName === module ) { return true; } else if ( testModule.parentModule ) { return testInModuleChain( testModule.parentModule ); } else { return false; } } // Internally-generated tests are always valid if ( this.callback && this.callback.validTest ) { return true; } if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { return false; } if ( module && !testInModuleChain( this.module ) ) { return false; } if ( !filter ) { return true; } return regexFilter ? this.regexFilter( !!regexFilter[1], regexFilter[2], regexFilter[3], fullName ) : this.stringFilter( filter, fullName ); }, regexFilter: function( exclude, pattern, flags, fullName ) { var regex = new RegExp( pattern, flags ); var match = regex.test( fullName ); return match !== exclude; }, stringFilter: function( filter, fullName ) { filter = filter.toLowerCase(); fullName = fullName.toLowerCase(); var include = filter.charAt( 0 ) !== "!"; if ( !include ) { filter = filter.slice( 1 ); } // If the filter matches, we need to honour include if ( fullName.indexOf( filter ) !== -1 ) { return include; } // Otherwise, do the opposite return !include; } }; // Resets the test setup. Useful for tests that modify the DOM. /* DEPRECATED: Use multiple tests instead of resetting inside a test. Use testStart or testDone for custom cleanup. This method will throw an error in 2.0, and will be removed in 2.1 */ QUnit.reset = function() { // Return on non-browser environments // This is necessary to not break on node tests if ( !defined.document ) { return; } var fixture = defined.document && document.getElementById && document.getElementById( "qunit-fixture" ); if ( fixture ) { fixture.innerHTML = config.fixture; } }; QUnit.pushFailure = function() { if ( !QUnit.config.current ) { throw new Error( "pushFailure() assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } // Gets current test obj var currentTest = QUnit.config.current; return currentTest.pushFailure.apply( currentTest, arguments ); }; // Based on Java's String.hashCode, a simple but not // rigorously collision resistant hashing function function generateHash( module, testName ) { var hex, i = 0, hash = 0, str = module + "\x1C" + testName, len = str.length; for ( ; i < len; i++ ) { hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); hash |= 0; } // Convert the possibly negative integer hash code into an 8 character hex string, which isn't // strictly necessary but increases user understanding that the id is a SHA-like hash hex = ( 0x100000000 + hash ).toString( 16 ); if ( hex.length < 8 ) { hex = "0000000" + hex; } return hex.slice( -8 ); } function synchronize( callback, priority ) { var last = !priority; if ( QUnit.objectType( callback ) === "array" ) { while ( callback.length ) { synchronize( callback.shift() ); } return; } if ( priority ) { config.queue.splice( priorityCount++, 0, callback ); } else { config.queue.push( callback ); } if ( config.autorun && !config.blocking ) { process( last ); } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in global ) { if ( hasOwn.call( global, key ) ) { // in Opera sometimes DOM element ids show up here, ignore them if ( /^qunit-test-output/.test( key ) ) { continue; } config.pollution.push( key ); } } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); } } // Will be exposed as QUnit.asyncTest function asyncTest( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } QUnit.test( testName, expected, callback, true ); } // Will be exposed as QUnit.test function test( testName, expected, callback, async ) { if ( focused ) { return; } var newTest; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test({ testName: testName, expected: expected, async: async, callback: callback }); newTest.queue(); } // Will be exposed as QUnit.skip function skip( testName ) { if ( focused ) { return; } var test = new Test({ testName: testName, skip: true }); test.queue(); } // Will be exposed as QUnit.only function only( testName, expected, callback, async ) { var newTest; if ( focused ) { return; } QUnit.config.queue.length = 0; focused = true; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test({ testName: testName, expected: expected, async: async, callback: callback }); newTest.queue(); } function Assert( testContext ) { this.test = testContext; } // Assert helpers QUnit.assert = Assert.prototype = { // Specify the number of expected assertions to guarantee that failed test // (no assertions are run at all) don't slip through. expect: function( asserts ) { if ( arguments.length === 1 ) { this.test.expected = asserts; } else { return this.test.expected; } }, // Increment this Test's semaphore counter, then return a function that // decrements that counter a maximum of once. async: function( count ) { var test = this.test, popped = false, acceptCallCount = count; if ( typeof acceptCallCount === "undefined" ) { acceptCallCount = 1; } test.semaphore += 1; test.usedAsync = true; pauseProcessing(); return function done() { if ( popped ) { test.pushFailure( "Too many calls to the `assert.async` callback", sourceFromStacktrace( 2 ) ); return; } acceptCallCount -= 1; if ( acceptCallCount > 0 ) { return; } test.semaphore -= 1; popped = true; resumeProcessing(); }; }, // Exports test.push() to the user API // Alias of pushResult. push: function( result, actual, expected, message, negative ) { var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; return currentAssert.pushResult( { result: result, actual: actual, expected: expected, message: message, negative: negative } ); }, pushResult: function( resultInfo ) { // resultInfo = { result, actual, expected, message, negative } var assert = this, currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; // Backwards compatibility fix. // Allows the direct use of global exported assertions and QUnit.assert.* // Although, it's use is not recommended as it can leak assertions // to other tests from async tests, because we only get a reference to the current test, // not exactly the test where assertion were intended to be called. if ( !currentTest ) { throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", sourceFromStacktrace( 2 ) ); // Allow this assertion to continue running anyway... } if ( !( assert instanceof Assert ) ) { assert = currentTest.assert; } return assert.test.pushResult( resultInfo ); }, ok: function( result, message ) { message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !!result, actual: result, expected: true, message: message } ); }, notOk: function( result, message ) { message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !result, actual: result, expected: false, message: message } ); }, equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected == actual, actual: actual, expected: expected, message: message } ); }, notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected != actual, actual: actual, expected: expected, message: message, negative: true } ); }, propEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notPropEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, deepEqual: function( actual, expected, message ) { this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notDeepEqual: function( actual, expected, message ) { this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, strictEqual: function( actual, expected, message ) { this.pushResult( { result: expected === actual, actual: actual, expected: expected, message: message } ); }, notStrictEqual: function( actual, expected, message ) { this.pushResult( { result: expected !== actual, actual: actual, expected: expected, message: message, negative: true } ); }, "throws": function( block, expected, message ) { var actual, expectedType, expectedOutput = expected, ok = false, currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; // 'expected' is optional unless doing string comparison if ( message == null && typeof expected === "string" ) { message = expected; expected = null; } currentTest.ignoreGlobalErrors = true; try { block.call( currentTest.testEnvironment ); } catch (e) { actual = e; } currentTest.ignoreGlobalErrors = false; if ( actual ) { expectedType = QUnit.objectType( expected ); // we don't want to validate thrown error if ( !expected ) { ok = true; expectedOutput = null; // expected is a regexp } else if ( expectedType === "regexp" ) { ok = expected.test( errorString( actual ) ); // expected is a string } else if ( expectedType === "string" ) { ok = expected === errorString( actual ); // expected is a constructor, maybe an Error constructor } else if ( expectedType === "function" && actual instanceof expected ) { ok = true; // expected is an Error object } else if ( expectedType === "object" ) { ok = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; // expected is a validation function which returns true if validation passed } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { expectedOutput = null; ok = true; } } currentTest.assert.pushResult( { result: ok, actual: actual, expected: expectedOutput, message: message } ); } }; // Provide an alternative to assert.throws(), for environments that consider throws a reserved word // Known to us are: Closure Compiler, Narwhal (function() { /*jshint sub:true */ Assert.prototype.raises = Assert.prototype[ "throws" ]; }()); function errorString( error ) { var name, message, resultErrorString = error.toString(); if ( resultErrorString.substring( 0, 7 ) === "[object" ) { name = error.name ? error.name.toString() : "Error"; message = error.message ? error.message.toString() : ""; if ( name && message ) { return name + ": " + message; } else if ( name ) { return name; } else if ( message ) { return message; } else { return "Error"; } } else { return resultErrorString; } } // Test for equality any JavaScript type. // Author: Philippe Rathé QUnit.equiv = (function() { // Stack to decide between skip/abort functions var callers = []; // Stack to avoiding loops from circular referencing var parents = []; var parentsB = []; var getProto = Object.getPrototypeOf || function( obj ) { /*jshint proto: true */ return obj.__proto__; }; function useStrictEquality( b, a ) { // To catch short annotation VS 'new' annotation of a declaration. e.g.: // `var i = 1;` // `var j = new Number(1);` if ( typeof a === "object" ) { a = a.valueOf(); } if ( typeof b === "object" ) { b = b.valueOf(); } return a === b; } function compareConstructors( a, b ) { var protoA = getProto( a ); var protoB = getProto( b ); // Comparing constructors is more strict than using `instanceof` if ( a.constructor === b.constructor ) { return true; } // Ref #851 // If the obj prototype descends from a null constructor, treat it // as a null prototype. if ( protoA && protoA.constructor === null ) { protoA = null; } if ( protoB && protoB.constructor === null ) { protoB = null; } // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. if ( ( protoA === null && protoB === Object.prototype ) || ( protoB === null && protoA === Object.prototype ) ) { return true; } return false; } function getRegExpFlags( regexp ) { return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ]; } var callbacks = { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "symbol": useStrictEquality, "date": useStrictEquality, "nan": function() { return true; }, "regexp": function( b, a ) { return a.source === b.source && // Include flags in the comparison getRegExpFlags( a ) === getRegExpFlags( b ); }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function() { var caller = callers[ callers.length - 1 ]; return caller !== Object && typeof caller !== "undefined"; }, "array": function( b, a ) { var i, j, len, loop, aCircular, bCircular; len = a.length; if ( len !== b.length ) { // safe and faster return false; } // Track reference to avoid circular references parents.push( a ); parentsB.push( b ); for ( i = 0; i < len; i++ ) { loop = false; for ( j = 0; j < parents.length; j++ ) { aCircular = parents[ j ] === a[ i ]; bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { parents.pop(); parentsB.pop(); return false; } } } if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { parents.pop(); parentsB.pop(); return false; } } parents.pop(); parentsB.pop(); return true; }, "set": function( b, a ) { var aArray, bArray; aArray = []; a.forEach( function( v ) { aArray.push( v ); }); bArray = []; b.forEach( function( v ) { bArray.push( v ); }); return innerEquiv( bArray, aArray ); }, "map": function( b, a ) { var aArray, bArray; aArray = []; a.forEach( function( v, k ) { aArray.push( [ k, v ] ); }); bArray = []; b.forEach( function( v, k ) { bArray.push( [ k, v ] ); }); return innerEquiv( bArray, aArray ); }, "object": function( b, a ) { var i, j, loop, aCircular, bCircular; // Default to true var eq = true; var aProperties = []; var bProperties = []; if ( compareConstructors( a, b ) === false ) { return false; } // Stack constructor before traversing properties callers.push( a.constructor ); // Track reference to avoid circular references parents.push( a ); parentsB.push( b ); // Be strict: don't ensure hasOwnProperty and go deep for ( i in a ) { loop = false; for ( j = 0; j < parents.length; j++ ) { aCircular = parents[ j ] === a[ i ]; bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { eq = false; break; } } } aProperties.push( i ); if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { eq = false; break; } } parents.pop(); parentsB.pop(); // Unstack, we are done callers.pop(); for ( i in b ) { // Collect b's properties bProperties.push( i ); } // Ensures identical properties name return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); } }; function typeEquiv( a, b ) { var type = QUnit.objectType( a ); return QUnit.objectType( b ) === type && callbacks[ type ]( b, a ); } // The real equiv function function innerEquiv( a, b ) { // We're done when there's nothing more to compare if ( arguments.length < 2 ) { return true; } // Require type-specific equality return ( a === b || typeEquiv( a, b ) ) && // ...across all consecutive argument pairs ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) ); } return innerEquiv; }()); // Based on jsDump by Ariel Flesler // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html QUnit.dump = (function() { function quote( str ) { return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; } function literal( o ) { return o + ""; } function join( pre, arr, post ) { var s = dump.separator(), base = dump.indent(), inner = dump.indent( 1 ); if ( arr.join ) { arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } return [ pre, inner + arr, base + post ].join( s ); } function array( arr, stack ) { var i = arr.length, ret = new Array( i ); if ( dump.maxDepth && dump.depth > dump.maxDepth ) { return "[object Array]"; } this.up(); while ( i-- ) { ret[ i ] = this.parse( arr[ i ], undefined, stack ); } this.down(); return join( "[", ret, "]" ); } var reName = /^function (\w+)/, dump = { // objType is used mostly internally, you can fix a (custom) type in advance parse: function( obj, objType, stack ) { stack = stack || []; var res, parser, parserType, inStack = inArray( obj, stack ); if ( inStack !== -1 ) { return "recursion(" + ( inStack - stack.length ) + ")"; } objType = objType || this.typeOf( obj ); parser = this.parsers[ objType ]; parserType = typeof parser; if ( parserType === "function" ) { stack.push( obj ); res = parser.call( this, obj, stack ); stack.pop(); return res; } return ( parserType === "string" ) ? parser : this.parsers.error; }, typeOf: function( obj ) { var type; if ( obj === null ) { type = "null"; } else if ( typeof obj === "undefined" ) { type = "undefined"; } else if ( QUnit.is( "regexp", obj ) ) { type = "regexp"; } else if ( QUnit.is( "date", obj ) ) { type = "date"; } else if ( QUnit.is( "function", obj ) ) { type = "function"; } else if ( obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined ) { type = "window"; } else if ( obj.nodeType === 9 ) { type = "document"; } else if ( obj.nodeType ) { type = "node"; } else if ( // native arrays toString.call( obj ) === "[object Array]" || // NodeList objects ( typeof obj.length === "number" && obj.item !== undefined && ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && obj[ 0 ] === undefined ) ) ) ) { type = "array"; } else if ( obj.constructor === Error.prototype.constructor ) { type = "error"; } else { type = typeof obj; } return type; }, separator: function() { return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " "; }, // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { if ( !this.multiline ) { return ""; } var chr = this.indentChar; if ( this.HTML ) { chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } return new Array( this.depth + ( extra || 0 ) ).join( chr ); }, up: function( a ) { this.depth += a || 1; }, down: function( a ) { this.depth -= a || 1; }, setParser: function( name, parser ) { this.parsers[ name ] = parser; }, // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, // depth: 1, maxDepth: QUnit.config.maxDepth, // This is the list of parsers, to modify them, use dump.setParser parsers: { window: "[Window]", document: "[Document]", error: function( error ) { return "Error(\"" + error.message + "\")"; }, unknown: "[Unknown]", "null": "null", "undefined": "undefined", "function": function( fn ) { var ret = "function", // functions never have name in IE name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; if ( name ) { ret += " " + name; } ret += "( "; ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); return join( ret, dump.parse( fn, "functionCode" ), "}" ); }, array: array, nodelist: array, "arguments": array, object: function( map, stack ) { var keys, key, val, i, nonEnumerableProperties, ret = []; if ( dump.maxDepth && dump.depth > dump.maxDepth ) { return "[object Object]"; } dump.up(); keys = []; for ( key in map ) { keys.push( key ); } // Some properties are not always enumerable on Error objects. nonEnumerableProperties = [ "message", "name" ]; for ( i in nonEnumerableProperties ) { key = nonEnumerableProperties[ i ]; if ( key in map && inArray( key, keys ) < 0 ) { keys.push( key ); } } keys.sort(); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; val = map[ key ]; ret.push( dump.parse( key, "key" ) + ": " + dump.parse( val, undefined, stack ) ); } dump.down(); return join( "{", ret, "}" ); }, node: function( node ) { var len, i, val, open = dump.HTML ? "<" : "<", close = dump.HTML ? ">" : ">", tag = node.nodeName.toLowerCase(), ret = open + tag, attrs = node.attributes; if ( attrs ) { for ( i = 0, len = attrs.length; i < len; i++ ) { val = attrs[ i ].nodeValue; // IE6 includes all attributes in .attributes, even ones not explicitly // set. Those have values like undefined, null, 0, false, "" or // "inherit". if ( val && val !== "inherit" ) { ret += " " + attrs[ i ].nodeName + "=" + dump.parse( val, "attribute" ); } } } ret += close; // Show content of TextNode or CDATASection if ( node.nodeType === 3 || node.nodeType === 4 ) { ret += node.nodeValue; } return ret + open + "/" + tag + close; }, // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, l = fn.length; if ( !l ) { return ""; } args = new Array( l ); while ( l-- ) { // 97 is 'a' args[ l ] = String.fromCharCode( 97 + l ); } return " " + args.join( ", " ) + " "; }, // object calls it internally, the key part of an item in a map key: quote, // function calls it internally, it's the content of the function functionCode: "[code]", // node calls it internally, it's a html attribute value attribute: quote, string: quote, date: quote, regexp: literal, number: literal, "boolean": literal }, // if true, entities are escaped ( <, >, \t, space and \n ) HTML: false, // indentation unit indentChar: " ", // if true, items in a collection, are separated by a \n, else just a space. multiline: true }; return dump; }()); // back compat QUnit.jsDump = QUnit.dump; // Deprecated // Extend assert methods to QUnit for Backwards compatibility (function() { var i, assertions = Assert.prototype; function applyCurrent( current ) { return function() { var assert = new Assert( QUnit.config.current ); current.apply( assert, arguments ); }; } for ( i in assertions ) { QUnit[ i ] = applyCurrent( assertions[ i ] ); } })(); // For browser, export only select globals if ( defined.document ) { (function() { var i, l, keys = [ "test", "module", "expect", "asyncTest", "start", "stop", "ok", "notOk", "equal", "notEqual", "propEqual", "notPropEqual", "deepEqual", "notDeepEqual", "strictEqual", "notStrictEqual", "throws", "raises" ]; for ( i = 0, l = keys.length; i < l; i++ ) { window[ keys[ i ] ] = QUnit[ keys[ i ] ]; } })(); window.QUnit = QUnit; } // For nodejs if ( typeof module !== "undefined" && module && module.exports ) { module.exports = QUnit; // For consistency with CommonJS environments' exports module.exports.QUnit = QUnit; } // For CommonJS with exports, but without module.exports, like Rhino if ( typeof exports !== "undefined" && exports ) { exports.QUnit = QUnit; } if ( typeof define === "function" && define.amd ) { define( function() { return QUnit; } ); QUnit.config.autostart = false; } /* * This file is a modified version of google-diff-match-patch's JavaScript implementation * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), * modifications are licensed as more fully set forth in LICENSE.txt. * * The original source of google-diff-match-patch is attributable and licensed as follows: * * Copyright 2006 Google Inc. * https://code.google.com/p/google-diff-match-patch/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * More Info: * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * */ QUnit.diff = ( function() { function DiffMatchPatch() { } // DIFF FUNCTIONS /** * The data structure representing a diff is an array of tuples: * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] * which means: delete 'Hello', add 'Goodbye' and keep ' world.' */ var DIFF_DELETE = -1, DIFF_INSERT = 1, DIFF_EQUAL = 0; /** * Find the differences between two texts. Simplifies the problem by stripping * any common prefix or suffix off the texts before diffing. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean=} optChecklines Optional speedup flag. If present and false, * then don't run a line-level diff first to identify the changed areas. * Defaults to true, which does a faster, slightly less optimal diff. * @return {!Array.} Array of diff tuples. */ DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) { var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; // The diff must be complete in up to 1 second. deadline = ( new Date() ).getTime() + 1000; // Check for null inputs. if ( text1 === null || text2 === null ) { throw new Error( "Null input. (DiffMain)" ); } // Check for equality (speedup). if ( text1 === text2 ) { if ( text1 ) { return [ [ DIFF_EQUAL, text1 ] ]; } return []; } if ( typeof optChecklines === "undefined" ) { optChecklines = true; } checklines = optChecklines; // Trim off common prefix (speedup). commonlength = this.diffCommonPrefix( text1, text2 ); commonprefix = text1.substring( 0, commonlength ); text1 = text1.substring( commonlength ); text2 = text2.substring( commonlength ); // Trim off common suffix (speedup). commonlength = this.diffCommonSuffix( text1, text2 ); commonsuffix = text1.substring( text1.length - commonlength ); text1 = text1.substring( 0, text1.length - commonlength ); text2 = text2.substring( 0, text2.length - commonlength ); // Compute the diff on the middle block. diffs = this.diffCompute( text1, text2, checklines, deadline ); // Restore the prefix and suffix. if ( commonprefix ) { diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); } if ( commonsuffix ) { diffs.push( [ DIFF_EQUAL, commonsuffix ] ); } this.diffCleanupMerge( diffs ); return diffs; }; /** * Reduce the number of edits by eliminating operationally trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Is there an insertion operation before the last equality. preIns = false; // Is there a deletion operation before the last equality. preDel = false; // Is there an insertion operation after the last equality. postIns = false; // Is there a deletion operation after the last equality. postDel = false; while ( pointer < diffs.length ) { // Equality found. if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) { // Candidate found. equalities[ equalitiesLength++ ] = pointer; preIns = postIns; preDel = postDel; lastequality = diffs[ pointer ][ 1 ]; } else { // Not a candidate, and can never become one. equalitiesLength = 0; lastequality = null; } postIns = postDel = false; // An insertion or deletion. } else { if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { postDel = true; } else { postIns = true; } /* * Five types to be split: * ABXYCD * AXCD * ABXC * AXCD * ABXC */ if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || ( ( lastequality.length < 2 ) && ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { // Duplicate record. diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); // Change second copy to insert. diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; equalitiesLength--; // Throw away the equality we just deleted; lastequality = null; if ( preIns && preDel ) { // No changes made which could affect previous entry, keep going. postIns = postDel = true; equalitiesLength = 0; } else { equalitiesLength--; // Throw away the previous equality. pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; postIns = postDel = false; } changes = true; } } pointer++; } if ( changes ) { this.diffCleanupMerge( diffs ); } }; /** * Convert a diff array into a pretty HTML report. * @param {!Array.} diffs Array of diff tuples. * @param {integer} string to be beautified. * @return {string} HTML representation. */ DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { var op, data, x, html = []; for ( x = 0; x < diffs.length; x++ ) { op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal) data = diffs[ x ][ 1 ]; // Text of change. switch ( op ) { case DIFF_INSERT: html[ x ] = "" + data + ""; break; case DIFF_DELETE: html[ x ] = "" + data + ""; break; case DIFF_EQUAL: html[ x ] = "" + data + ""; break; } } return html.join( "" ); }; /** * Determine the common prefix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the start of each * string. */ DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { var pointermid, pointermax, pointermin, pointerstart; // Quick check for common null cases. if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min( text1.length, text2.length ); pointermid = pointermax; pointerstart = 0; while ( pointermin < pointermid ) { if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { pointermin = pointermid; pointerstart = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); } return pointermid; }; /** * Determine the common suffix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of each string. */ DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { var pointermid, pointermax, pointermin, pointerend; // Quick check for common null cases. if ( !text1 || !text2 || text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min( text1.length, text2.length ); pointermid = pointermax; pointerend = 0; while ( pointermin < pointermid ) { if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) === text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { pointermin = pointermid; pointerend = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); } return pointermid; }; /** * Find the differences between two texts. Assumes that the texts do not * have any common prefix or suffix. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean} checklines Speedup flag. If false, then don't run a * line-level diff first to identify the changed areas. * If true, then run a faster, slightly less optimal diff. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; if ( !text1 ) { // Just add some text (speedup). return [ [ DIFF_INSERT, text2 ] ]; } if ( !text2 ) { // Just delete some text (speedup). return [ [ DIFF_DELETE, text1 ] ]; } longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; i = longtext.indexOf( shorttext ); if ( i !== -1 ) { // Shorter text is inside the longer text (speedup). diffs = [ [ DIFF_INSERT, longtext.substring( 0, i ) ], [ DIFF_EQUAL, shorttext ], [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] ]; // Swap insertions for deletions if diff is reversed. if ( text1.length > text2.length ) { diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE; } return diffs; } if ( shorttext.length === 1 ) { // Single character string. // After the previous speedup, the character can't be an equality. return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ]; } // Check to see if the problem can be split in two. hm = this.diffHalfMatch( text1, text2 ); if ( hm ) { // A half-match was found, sort out the return data. text1A = hm[ 0 ]; text1B = hm[ 1 ]; text2A = hm[ 2 ]; text2B = hm[ 3 ]; midCommon = hm[ 4 ]; // Send both pairs off for separate processing. diffsA = this.DiffMain( text1A, text2A, checklines, deadline ); diffsB = this.DiffMain( text1B, text2B, checklines, deadline ); // Merge the results. return diffsA.concat( [ [ DIFF_EQUAL, midCommon ] ], diffsB ); } if ( checklines && text1.length > 100 && text2.length > 100 ) { return this.diffLineMode( text1, text2, deadline ); } return this.diffBisect( text1, text2, deadline ); }; /** * Do the two texts share a substring which is at least half the length of the * longer text? * This speedup can produce non-minimal diffs. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {Array.} Five element Array, containing the prefix of * text1, the suffix of text1, the prefix of text2, the suffix of * text2 and the common middle. Or null if there was no match. * @private */ DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) { var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) { return null; // Pointless. } dmp = this; // 'this' becomes 'window' in a closure. /** * Does a substring of shorttext exist within longtext such that the substring * is at least half the length of longtext? * Closure, but does not reference any external variables. * @param {string} longtext Longer string. * @param {string} shorttext Shorter string. * @param {number} i Start index of quarter length substring within longtext. * @return {Array.} Five element Array, containing the prefix of * longtext, the suffix of longtext, the prefix of shorttext, the suffix * of shorttext and the common middle. Or null if there was no match. * @private */ function diffHalfMatchI( longtext, shorttext, i ) { var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; // Start with a 1/4 length substring at position i as a seed. seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) ); j = -1; bestCommon = ""; while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) { prefixLength = dmp.diffCommonPrefix( longtext.substring( i ), shorttext.substring( j ) ); suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ), shorttext.substring( 0, j ) ); if ( bestCommon.length < suffixLength + prefixLength ) { bestCommon = shorttext.substring( j - suffixLength, j ) + shorttext.substring( j, j + prefixLength ); bestLongtextA = longtext.substring( 0, i - suffixLength ); bestLongtextB = longtext.substring( i + prefixLength ); bestShorttextA = shorttext.substring( 0, j - suffixLength ); bestShorttextB = shorttext.substring( j + prefixLength ); } } if ( bestCommon.length * 2 >= longtext.length ) { return [ bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon ]; } else { return null; } } // First check if the second quarter is the seed for a half-match. hm1 = diffHalfMatchI( longtext, shorttext, Math.ceil( longtext.length / 4 ) ); // Check again based on the third quarter. hm2 = diffHalfMatchI( longtext, shorttext, Math.ceil( longtext.length / 2 ) ); if ( !hm1 && !hm2 ) { return null; } else if ( !hm2 ) { hm = hm1; } else if ( !hm1 ) { hm = hm2; } else { // Both matched. Select the longest. hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2; } // A half-match was found, sort out the return data. text1A, text1B, text2A, text2B; if ( text1.length > text2.length ) { text1A = hm[ 0 ]; text1B = hm[ 1 ]; text2A = hm[ 2 ]; text2B = hm[ 3 ]; } else { text2A = hm[ 0 ]; text2B = hm[ 1 ]; text1A = hm[ 2 ]; text1B = hm[ 3 ]; } midCommon = hm[ 4 ]; return [ text1A, text1B, text2A, text2B, midCommon ]; }; /** * Do a quick line-level diff on both strings, then rediff the parts for * greater accuracy. * This speedup can produce non-minimal diffs. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) { var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; // Scan the text on a line-by-line basis first. a = this.diffLinesToChars( text1, text2 ); text1 = a.chars1; text2 = a.chars2; linearray = a.lineArray; diffs = this.DiffMain( text1, text2, false, deadline ); // Convert the diff back to original text. this.diffCharsToLines( diffs, linearray ); // Eliminate freak matches (e.g. blank lines) this.diffCleanupSemantic( diffs ); // Rediff any replacement blocks, this time character-by-character. // Add a dummy entry at the end. diffs.push( [ DIFF_EQUAL, "" ] ); pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; while ( pointer < diffs.length ) { switch ( diffs[ pointer ][ 0 ] ) { case DIFF_INSERT: countInsert++; textInsert += diffs[ pointer ][ 1 ]; break; case DIFF_DELETE: countDelete++; textDelete += diffs[ pointer ][ 1 ]; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if ( countDelete >= 1 && countInsert >= 1 ) { // Delete the offending records and add the merged ones. diffs.splice( pointer - countDelete - countInsert, countDelete + countInsert ); pointer = pointer - countDelete - countInsert; a = this.DiffMain( textDelete, textInsert, false, deadline ); for ( j = a.length - 1; j >= 0; j-- ) { diffs.splice( pointer, 0, a[ j ] ); } pointer = pointer + a.length; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } pointer++; } diffs.pop(); // Remove the dummy entry at the end. return diffs; }; /** * Find the 'middle snake' of a diff, split the problem in two * and return the recursively constructed diff. * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) { var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; maxD = Math.ceil( ( text1Length + text2Length ) / 2 ); vOffset = maxD; vLength = 2 * maxD; v1 = new Array( vLength ); v2 = new Array( vLength ); // Setting all elements to -1 is faster in Chrome & Firefox than mixing // integers and undefined. for ( x = 0; x < vLength; x++ ) { v1[ x ] = -1; v2[ x ] = -1; } v1[ vOffset + 1 ] = 0; v2[ vOffset + 1 ] = 0; delta = text1Length - text2Length; // If the total number of characters is odd, then the front path will collide // with the reverse path. front = ( delta % 2 !== 0 ); // Offsets for start and end of k loop. // Prevents mapping of space beyond the grid. k1start = 0; k1end = 0; k2start = 0; k2end = 0; for ( d = 0; d < maxD; d++ ) { // Bail out if deadline is reached. if ( ( new Date() ).getTime() > deadline ) { break; } // Walk the front path one step. for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) { k1Offset = vOffset + k1; if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { x1 = v1[ k1Offset + 1 ]; } else { x1 = v1[ k1Offset - 1 ] + 1; } y1 = x1 - k1; while ( x1 < text1Length && y1 < text2Length && text1.charAt( x1 ) === text2.charAt( y1 ) ) { x1++; y1++; } v1[ k1Offset ] = x1; if ( x1 > text1Length ) { // Ran off the right of the graph. k1end += 2; } else if ( y1 > text2Length ) { // Ran off the bottom of the graph. k1start += 2; } else if ( front ) { k2Offset = vOffset + delta - k1; if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) { // Mirror x2 onto top-left coordinate system. x2 = text1Length - v2[ k2Offset ]; if ( x1 >= x2 ) { // Overlap detected. return this.diffBisectSplit( text1, text2, x1, y1, deadline ); } } } } // Walk the reverse path one step. for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) { k2Offset = vOffset + k2; if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { x2 = v2[ k2Offset + 1 ]; } else { x2 = v2[ k2Offset - 1 ] + 1; } y2 = x2 - k2; while ( x2 < text1Length && y2 < text2Length && text1.charAt( text1Length - x2 - 1 ) === text2.charAt( text2Length - y2 - 1 ) ) { x2++; y2++; } v2[ k2Offset ] = x2; if ( x2 > text1Length ) { // Ran off the left of the graph. k2end += 2; } else if ( y2 > text2Length ) { // Ran off the top of the graph. k2start += 2; } else if ( !front ) { k1Offset = vOffset + delta - k2; if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) { x1 = v1[ k1Offset ]; y1 = vOffset + x1 - k1Offset; // Mirror x2 onto top-left coordinate system. x2 = text1Length - x2; if ( x1 >= x2 ) { // Overlap detected. return this.diffBisectSplit( text1, text2, x1, y1, deadline ); } } } } } // Diff took too long and hit the deadline or // number of diffs equals number of characters, no commonality at all. return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ]; }; /** * Given the location of the 'middle snake', split the diff in two parts * and recurse. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} x Index of split point in text1. * @param {number} y Index of split point in text2. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { var text1a, text1b, text2a, text2b, diffs, diffsb; text1a = text1.substring( 0, x ); text2a = text2.substring( 0, y ); text1b = text1.substring( x ); text2b = text2.substring( y ); // Compute both diffs serially. diffs = this.DiffMain( text1a, text2a, false, deadline ); diffsb = this.DiffMain( text1b, text2b, false, deadline ); return diffs.concat( diffsb ); }; /** * Reduce the number of edits by eliminating semantically trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) { var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Number of characters that changed prior to the equality. lengthInsertions1 = 0; lengthDeletions1 = 0; // Number of characters that changed after the equality. lengthInsertions2 = 0; lengthDeletions2 = 0; while ( pointer < diffs.length ) { if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. equalities[ equalitiesLength++ ] = pointer; lengthInsertions1 = lengthInsertions2; lengthDeletions1 = lengthDeletions2; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = diffs[ pointer ][ 1 ]; } else { // An insertion or deletion. if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) { lengthInsertions2 += diffs[ pointer ][ 1 ].length; } else { lengthDeletions2 += diffs[ pointer ][ 1 ].length; } // Eliminate an equality that is smaller or equal to the edits on both // sides of it. if ( lastequality && ( lastequality.length <= Math.max( lengthInsertions1, lengthDeletions1 ) ) && ( lastequality.length <= Math.max( lengthInsertions2, lengthDeletions2 ) ) ) { // Duplicate record. diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); // Change second copy to insert. diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; // Throw away the equality we just deleted. equalitiesLength--; // Throw away the previous equality (it needs to be reevaluated). equalitiesLength--; pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; // Reset the counters. lengthInsertions1 = 0; lengthDeletions1 = 0; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = null; changes = true; } } pointer++; } // Normalize the diff. if ( changes ) { this.diffCleanupMerge( diffs ); } // Find any overlaps between deletions and insertions. // e.g: abcxxxxxxdef // -> abcxxxdef // e.g: xxxabcdefxxx // -> defxxxabc // Only extract an overlap if it is as big as the edit ahead or behind it. pointer = 1; while ( pointer < diffs.length ) { if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE && diffs[ pointer ][ 0 ] === DIFF_INSERT ) { deletion = diffs[ pointer - 1 ][ 1 ]; insertion = diffs[ pointer ][ 1 ]; overlapLength1 = this.diffCommonOverlap( deletion, insertion ); overlapLength2 = this.diffCommonOverlap( insertion, deletion ); if ( overlapLength1 >= overlapLength2 ) { if ( overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2 ) { // Overlap found. Insert an equality and trim the surrounding edits. diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); diffs[ pointer - 1 ][ 1 ] = deletion.substring( 0, deletion.length - overlapLength1 ); diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 ); pointer++; } } else { if ( overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2 ) { // Reverse overlap found. // Insert an equality and swap and trim the surrounding edits. diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT; diffs[ pointer - 1 ][ 1 ] = insertion.substring( 0, insertion.length - overlapLength2 ); diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE; diffs[ pointer + 1 ][ 1 ] = deletion.substring( overlapLength2 ); pointer++; } } pointer++; } pointer++; } }; /** * Determine if the suffix of one string is the prefix of another. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of the first * string and the start of the second string. * @private */ DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) { var text1Length, text2Length, textLength, best, length, pattern, found; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; // Eliminate the null case. if ( text1Length === 0 || text2Length === 0 ) { return 0; } // Truncate the longer string. if ( text1Length > text2Length ) { text1 = text1.substring( text1Length - text2Length ); } else if ( text1Length < text2Length ) { text2 = text2.substring( 0, text1Length ); } textLength = Math.min( text1Length, text2Length ); // Quick check for the worst case. if ( text1 === text2 ) { return textLength; } // Start by looking for a single character match // and increase length until no match is found. // Performance analysis: https://neil.fraser.name/news/2010/11/04/ best = 0; length = 1; while ( true ) { pattern = text1.substring( textLength - length ); found = text2.indexOf( pattern ); if ( found === -1 ) { return best; } length += found; if ( found === 0 || text1.substring( textLength - length ) === text2.substring( 0, length ) ) { best = length; length++; } } }; /** * Split two texts into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {{chars1: string, chars2: string, lineArray: !Array.}} * An object containing the encoded text1, the encoded text2 and * the array of unique strings. * The zeroth element of the array of unique strings is intentionally blank. * @private */ DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) { var lineArray, lineHash, chars1, chars2; lineArray = []; // e.g. lineArray[4] === 'Hello\n' lineHash = {}; // e.g. lineHash['Hello\n'] === 4 // '\x00' is a valid character, but various debuggers don't like it. // So we'll insert a junk entry to avoid generating a null character. lineArray[ 0 ] = ""; /** * Split a text into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * Modifies linearray and linehash through being a closure. * @param {string} text String to encode. * @return {string} Encoded string. * @private */ function diffLinesToCharsMunge( text ) { var chars, lineStart, lineEnd, lineArrayLength, line; chars = ""; // Walk the text, pulling out a substring for each line. // text.split('\n') would would temporarily double our memory footprint. // Modifying text would create many large strings to garbage collect. lineStart = 0; lineEnd = -1; // Keeping our own length variable is faster than looking it up. lineArrayLength = lineArray.length; while ( lineEnd < text.length - 1 ) { lineEnd = text.indexOf( "\n", lineStart ); if ( lineEnd === -1 ) { lineEnd = text.length - 1; } line = text.substring( lineStart, lineEnd + 1 ); lineStart = lineEnd + 1; if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) : ( lineHash[ line ] !== undefined ) ) { chars += String.fromCharCode( lineHash[ line ] ); } else { chars += String.fromCharCode( lineArrayLength ); lineHash[ line ] = lineArrayLength; lineArray[ lineArrayLength++ ] = line; } } return chars; } chars1 = diffLinesToCharsMunge( text1 ); chars2 = diffLinesToCharsMunge( text2 ); return { chars1: chars1, chars2: chars2, lineArray: lineArray }; }; /** * Rehydrate the text in a diff from a string of line hashes to real lines of * text. * @param {!Array.} diffs Array of diff tuples. * @param {!Array.} lineArray Array of unique strings. * @private */ DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { var x, chars, text, y; for ( x = 0; x < diffs.length; x++ ) { chars = diffs[ x ][ 1 ]; text = []; for ( y = 0; y < chars.length; y++ ) { text[ y ] = lineArray[ chars.charCodeAt( y ) ]; } diffs[ x ][ 1 ] = text.join( "" ); } }; /** * Reorder and merge like edit sections. Merge equalities. * Any edit section can move as long as it doesn't cross an equality. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) { var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; commonlength; while ( pointer < diffs.length ) { switch ( diffs[ pointer ][ 0 ] ) { case DIFF_INSERT: countInsert++; textInsert += diffs[ pointer ][ 1 ]; pointer++; break; case DIFF_DELETE: countDelete++; textDelete += diffs[ pointer ][ 1 ]; pointer++; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if ( countDelete + countInsert > 1 ) { if ( countDelete !== 0 && countInsert !== 0 ) { // Factor out any common prefixes. commonlength = this.diffCommonPrefix( textInsert, textDelete ); if ( commonlength !== 0 ) { if ( ( pointer - countDelete - countInsert ) > 0 && diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] === DIFF_EQUAL ) { diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] += textInsert.substring( 0, commonlength ); } else { diffs.splice( 0, 0, [ DIFF_EQUAL, textInsert.substring( 0, commonlength ) ] ); pointer++; } textInsert = textInsert.substring( commonlength ); textDelete = textDelete.substring( commonlength ); } // Factor out any common suffixies. commonlength = this.diffCommonSuffix( textInsert, textDelete ); if ( commonlength !== 0 ) { diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length - commonlength ) + diffs[ pointer ][ 1 ]; textInsert = textInsert.substring( 0, textInsert.length - commonlength ); textDelete = textDelete.substring( 0, textDelete.length - commonlength ); } } // Delete the offending records and add the merged ones. if ( countDelete === 0 ) { diffs.splice( pointer - countInsert, countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); } else if ( countInsert === 0 ) { diffs.splice( pointer - countDelete, countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); } else { diffs.splice( pointer - countDelete - countInsert, countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); } pointer = pointer - countDelete - countInsert + ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1; } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) { // Merge this equality with the previous one. diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ]; diffs.splice( pointer, 1 ); } else { pointer++; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } } if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) { diffs.pop(); // Remove the dummy entry at the end. } // Second pass: look for single edits surrounded on both sides by equalities // which can be shifted sideways to eliminate an equality. // e.g: ABAC -> ABAC changes = false; pointer = 1; // Intentionally ignore the first and last element (don't need checking). while ( pointer < diffs.length - 1 ) { if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL && diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) { diffPointer = diffs[ pointer ][ 1 ]; position = diffPointer.substring( diffPointer.length - diffs[ pointer - 1 ][ 1 ].length ); // This is a single edit surrounded by equalities. if ( position === diffs[ pointer - 1 ][ 1 ] ) { // Shift the edit over the previous equality. diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length - diffs[ pointer - 1 ][ 1 ].length ); diffs[ pointer + 1 ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ]; diffs.splice( pointer - 1, 1 ); changes = true; } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === diffs[ pointer + 1 ][ 1 ] ) { // Shift the edit over the next equality. diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ]; diffs[ pointer ][ 1 ] = diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) + diffs[ pointer + 1 ][ 1 ]; diffs.splice( pointer + 1, 1 ); changes = true; } } pointer++; } // If shifts were made, the diff needs reordering and another shift sweep. if ( changes ) { this.diffCleanupMerge( diffs ); } }; return function( o, n ) { var diff, output, text; diff = new DiffMatchPatch(); output = diff.DiffMain( o, n ); diff.diffCleanupEfficiency( output ); text = diff.diffPrettyHtml( output ); return text; }; }() ); // Get a reference to the global object, like window in browsers }( (function() { return this; })() )); (function() { // Don't load the HTML Reporter on non-Browser environments if ( typeof window === "undefined" || !window.document ) { return; } // Deprecated QUnit.init - Ref #530 // Re-initialize the configuration options QUnit.init = function() { var tests, banner, result, qunit, config = QUnit.config; config.stats = { all: 0, bad: 0 }; config.moduleStats = { all: 0, bad: 0 }; config.started = 0; config.updateRate = 1000; config.blocking = false; config.autostart = true; config.autorun = false; config.filter = ""; config.queue = []; // Return on non-browser environments // This is necessary to not break on node tests if ( typeof window === "undefined" ) { return; } qunit = id( "qunit" ); if ( qunit ) { qunit.innerHTML = "

" + escapeText( document.title ) + "

" + "

" + "
" + "

" + "
    "; } tests = id( "qunit-tests" ); banner = id( "qunit-banner" ); result = id( "qunit-testresult" ); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
     "; } }; var config = QUnit.config, collapseNext = false, hasOwn = Object.prototype.hasOwnProperty, defined = { document: window.document !== undefined, sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch ( e ) { return false; } }()) }, modulesList = []; /** * Escape text for attribute or text content. */ function escapeText( s ) { if ( !s ) { return ""; } s = s + ""; // Both single quotes and double quotes (for attributes) return s.replace( /['"<>&]/g, function( s ) { switch ( s ) { case "'": return "'"; case "\"": return """; case "<": return "<"; case ">": return ">"; case "&": return "&"; } }); } /** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */ function addEvent( elem, type, fn ) { if ( elem.addEventListener ) { // Standards-based browsers elem.addEventListener( type, fn, false ); } else if ( elem.attachEvent ) { // support: IE <9 elem.attachEvent( "on" + type, function() { var event = window.event; if ( !event.target ) { event.target = event.srcElement || document; } fn.call( elem, event ); }); } } /** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */ function addEvents( elems, type, fn ) { var i = elems.length; while ( i-- ) { addEvent( elems[ i ], type, fn ); } } function hasClass( elem, name ) { return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; } function addClass( elem, name ) { if ( !hasClass( elem, name ) ) { elem.className += ( elem.className ? " " : "" ) + name; } } function toggleClass( elem, name ) { if ( hasClass( elem, name ) ) { removeClass( elem, name ); } else { addClass( elem, name ); } } function removeClass( elem, name ) { var set = " " + elem.className + " "; // Class name may appear multiple times while ( set.indexOf( " " + name + " " ) >= 0 ) { set = set.replace( " " + name + " ", " " ); } // trim for prettiness elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); } function id( name ) { return defined.document && document.getElementById && document.getElementById( name ); } function getUrlConfigHtml() { var i, j, val, escaped, escapedTooltip, selection = false, len = config.urlConfig.length, urlConfigHtml = ""; for ( i = 0; i < len; i++ ) { val = config.urlConfig[ i ]; if ( typeof val === "string" ) { val = { id: val, label: val }; } escaped = escapeText( val.id ); escapedTooltip = escapeText( val.tooltip ); if ( config[ val.id ] === undefined ) { config[ val.id ] = QUnit.urlParams[ val.id ]; } if ( !val.value || typeof val.value === "string" ) { urlConfigHtml += ""; } else { urlConfigHtml += ""; } } return urlConfigHtml; } // Handle "click" events on toolbar checkboxes and "change" for select menus. // Updates the URL with the new state of `config.urlConfig` values. function toolbarChanged() { var updatedUrl, value, field = this, params = {}; // Detect if field is a select menu or a checkbox if ( "selectedIndex" in field ) { value = field.options[ field.selectedIndex ].value || undefined; } else { value = field.checked ? ( field.defaultValue || true ) : undefined; } params[ field.name ] = value; updatedUrl = setUrl( params ); if ( "hidepassed" === field.name && "replaceState" in window.history ) { config[ field.name ] = value || false; if ( value ) { addClass( id( "qunit-tests" ), "hidepass" ); } else { removeClass( id( "qunit-tests" ), "hidepass" ); } // It is not necessary to refresh the whole page window.history.replaceState( null, "", updatedUrl ); } else { window.location = updatedUrl; } } function setUrl( params ) { var key, querystring = "?"; params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); for ( key in params ) { if ( hasOwn.call( params, key ) ) { if ( params[ key ] === undefined ) { continue; } querystring += encodeURIComponent( key ); if ( params[ key ] !== true ) { querystring += "=" + encodeURIComponent( params[ key ] ); } querystring += "&"; } } return location.protocol + "//" + location.host + location.pathname + querystring.slice( 0, -1 ); } function applyUrlParams() { var selectedModule, modulesList = id( "qunit-modulefilter" ), filter = id( "qunit-filter-input" ).value; selectedModule = modulesList ? decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : undefined; window.location = setUrl({ module: ( selectedModule === "" ) ? undefined : selectedModule, filter: ( filter === "" ) ? undefined : filter, // Remove testId filter testId: undefined }); } function toolbarUrlConfigContainer() { var urlConfigContainer = document.createElement( "span" ); urlConfigContainer.innerHTML = getUrlConfigHtml(); addClass( urlConfigContainer, "qunit-url-config" ); // For oldIE support: // * Add handlers to the individual elements instead of the container // * Use "click" instead of "change" for checkboxes addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); return urlConfigContainer; } function toolbarLooseFilter() { var filter = document.createElement( "form" ), label = document.createElement( "label" ), input = document.createElement( "input" ), button = document.createElement( "button" ); addClass( filter, "qunit-filter" ); label.innerHTML = "Filter: "; input.type = "text"; input.value = config.filter || ""; input.name = "filter"; input.id = "qunit-filter-input"; button.innerHTML = "Go"; label.appendChild( input ); filter.appendChild( label ); filter.appendChild( button ); addEvent( filter, "submit", function( ev ) { applyUrlParams(); if ( ev && ev.preventDefault ) { ev.preventDefault(); } return false; }); return filter; } function toolbarModuleFilterHtml() { var i, moduleFilterHtml = ""; if ( !modulesList.length ) { return false; } modulesList.sort(function( a, b ) { return a.localeCompare( b ); }); moduleFilterHtml += "" + ""; return moduleFilterHtml; } function toolbarModuleFilter() { var toolbar = id( "qunit-testrunner-toolbar" ), moduleFilter = document.createElement( "span" ), moduleFilterHtml = toolbarModuleFilterHtml(); if ( !toolbar || !moduleFilterHtml ) { return false; } moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); moduleFilter.innerHTML = moduleFilterHtml; addEvent( moduleFilter.lastChild, "change", applyUrlParams ); toolbar.appendChild( moduleFilter ); } function appendToolbar() { var toolbar = id( "qunit-testrunner-toolbar" ); if ( toolbar ) { toolbar.appendChild( toolbarUrlConfigContainer() ); toolbar.appendChild( toolbarLooseFilter() ); } } function appendHeader() { var header = id( "qunit-header" ); if ( header ) { header.innerHTML = "" + header.innerHTML + " "; } } function appendBanner() { var banner = id( "qunit-banner" ); if ( banner ) { banner.className = ""; } } function appendTestResults() { var tests = id( "qunit-tests" ), result = id( "qunit-testresult" ); if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { tests.innerHTML = ""; result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
     "; } } function storeFixture() { var fixture = id( "qunit-fixture" ); if ( fixture ) { config.fixture = fixture.innerHTML; } } function appendFilteredTest() { var testId = QUnit.config.testId; if ( !testId || testId.length <= 0 ) { return ""; } return "
    Rerunning selected tests: " + escapeText( testId.join(", ") ) + " " + "Run all tests" + "
    "; } function appendUserAgent() { var userAgent = id( "qunit-userAgent" ); if ( userAgent ) { userAgent.innerHTML = ""; userAgent.appendChild( document.createTextNode( "QUnit " + QUnit.version + "; " + navigator.userAgent ) ); } } function appendTestsList( modules ) { var i, l, x, z, test, moduleObj; for ( i = 0, l = modules.length; i < l; i++ ) { moduleObj = modules[ i ]; if ( moduleObj.name ) { modulesList.push( moduleObj.name ); } for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { test = moduleObj.tests[ x ]; appendTest( test.name, test.testId, moduleObj.name ); } } } function appendTest( name, testId, moduleName ) { var title, rerunTrigger, testBlock, assertList, tests = id( "qunit-tests" ); if ( !tests ) { return; } title = document.createElement( "strong" ); title.innerHTML = getNameHtml( name, moduleName ); rerunTrigger = document.createElement( "a" ); rerunTrigger.innerHTML = "Rerun"; rerunTrigger.href = setUrl({ testId: testId }); testBlock = document.createElement( "li" ); testBlock.appendChild( title ); testBlock.appendChild( rerunTrigger ); testBlock.id = "qunit-test-output-" + testId; assertList = document.createElement( "ol" ); assertList.className = "qunit-assert-list"; testBlock.appendChild( assertList ); tests.appendChild( testBlock ); } // HTML Reporter initialization and load QUnit.begin(function( details ) { var qunit = id( "qunit" ); // Fixture is the only one necessary to run without the #qunit element storeFixture(); if ( qunit ) { qunit.innerHTML = "

    " + escapeText( document.title ) + "

    " + "

    " + "
    " + appendFilteredTest() + "

    " + "
      "; } appendHeader(); appendBanner(); appendTestResults(); appendUserAgent(); appendToolbar(); appendTestsList( details.modules ); toolbarModuleFilter(); if ( qunit && config.hidepassed ) { addClass( qunit.lastChild, "hidepass" ); } }); QUnit.done(function( details ) { var i, key, banner = id( "qunit-banner" ), tests = id( "qunit-tests" ), html = [ "Tests completed in ", details.runtime, " milliseconds.
      ", "", details.passed, " assertions of ", details.total, " passed, ", details.failed, " failed." ].join( "" ); if ( banner ) { banner.className = details.failed ? "qunit-fail" : "qunit-pass"; } if ( tests ) { id( "qunit-testresult" ).innerHTML = html; } if ( config.altertitle && defined.document && document.title ) { // show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document.title = [ ( details.failed ? "\u2716" : "\u2714" ), document.title.replace( /^[\u2714\u2716] /i, "" ) ].join( " " ); } // clear own sessionStorage items if all tests passed if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { for ( i = 0; i < sessionStorage.length; i++ ) { key = sessionStorage.key( i++ ); if ( key.indexOf( "qunit-test-" ) === 0 ) { sessionStorage.removeItem( key ); } } } // scroll back to top to show results if ( config.scrolltop && window.scrollTo ) { window.scrollTo( 0, 0 ); } }); function getNameHtml( name, module ) { var nameHtml = ""; if ( module ) { nameHtml = "" + escapeText( module ) + ": "; } nameHtml += "" + escapeText( name ) + ""; return nameHtml; } QUnit.testStart(function( details ) { var running, testBlock, bad; testBlock = id( "qunit-test-output-" + details.testId ); if ( testBlock ) { testBlock.className = "running"; } else { // Report later registered tests appendTest( details.name, details.testId, details.module ); } running = id( "qunit-testresult" ); if ( running ) { bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); running.innerHTML = ( bad ? "Rerunning previously failed test:
      " : "Running:
      " ) + getNameHtml( details.name, details.module ); } }); function stripHtml( string ) { // strip tags, html entity and whitespaces return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); } QUnit.log(function( details ) { var assertList, assertLi, message, expected, actual, diff, showDiff = false, testItem = id( "qunit-test-output-" + details.testId ); if ( !testItem ) { return; } message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); message = "" + message + ""; message += "@ " + details.runtime + " ms"; // pushFailure doesn't provide details.expected // when it calls, it's implicit to also not show expected and diff stuff // Also, we need to check details.expected existence, as it can exist and be undefined if ( !details.result && hasOwn.call( details, "expected" ) ) { if ( details.negative ) { expected = escapeText( "NOT " + QUnit.dump.parse( details.expected ) ); } else { expected = escapeText( QUnit.dump.parse( details.expected ) ); } actual = escapeText( QUnit.dump.parse( details.actual ) ); message += ""; if ( actual !== expected ) { message += ""; // Don't show diff if actual or expected are booleans if ( !( /^(true|false)$/.test( actual ) ) && !( /^(true|false)$/.test( expected ) ) ) { diff = QUnit.diff( expected, actual ); showDiff = stripHtml( diff ).length !== stripHtml( expected ).length + stripHtml( actual ).length; } // Don't show diff if expected and actual are totally different if ( showDiff ) { message += ""; } } else if ( expected.indexOf( "[object Array]" ) !== -1 || expected.indexOf( "[object Object]" ) !== -1 ) { message += ""; } if ( details.source ) { message += ""; } message += "
      Expected:
      " +
      			expected +
      			"
      Result:
      " +
      				actual + "
      Diff:
      " +
      					diff + "
      Message: " + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").

      Hint: Use QUnit.dump.maxDepth to " + " run with a higher max depth or " + "Rerun without max depth.

      Source:
      " +
      				escapeText( details.source ) + "
      "; // this occurs when pushFailure is set and we have an extracted stack trace } else if ( !details.result && details.source ) { message += "" + "" + "
      Source:
      " +
      			escapeText( details.source ) + "
      "; } assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; assertLi = document.createElement( "li" ); assertLi.className = details.result ? "pass" : "fail"; assertLi.innerHTML = message; assertList.appendChild( assertLi ); }); QUnit.testDone(function( details ) { var testTitle, time, testItem, assertList, good, bad, testCounts, skipped, sourceName, tests = id( "qunit-tests" ); if ( !tests ) { return; } testItem = id( "qunit-test-output-" + details.testId ); assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; good = details.passed; bad = details.failed; // store result when possible if ( config.reorder && defined.sessionStorage ) { if ( bad ) { sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); } else { sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); } } if ( bad === 0 ) { // Collapse the passing tests addClass( assertList, "qunit-collapsed" ); } else if ( bad && config.collapse && !collapseNext ) { // Skip collapsing the first failing test collapseNext = true; } else { // Collapse remaining tests addClass( assertList, "qunit-collapsed" ); } // testItem.firstChild is the test name testTitle = testItem.firstChild; testCounts = bad ? "" + bad + ", " + "" + good + ", " : ""; testTitle.innerHTML += " (" + testCounts + details.assertions.length + ")"; if ( details.skipped ) { testItem.className = "skipped"; skipped = document.createElement( "em" ); skipped.className = "qunit-skipped-label"; skipped.innerHTML = "skipped"; testItem.insertBefore( skipped, testTitle ); } else { addEvent( testTitle, "click", function() { toggleClass( assertList, "qunit-collapsed" ); }); testItem.className = bad ? "fail" : "pass"; time = document.createElement( "span" ); time.className = "runtime"; time.innerHTML = details.runtime + " ms"; testItem.insertBefore( time, assertList ); } // Show the source of the test when showing assertions if ( details.source ) { sourceName = document.createElement( "p" ); sourceName.innerHTML = "Source: " + details.source; addClass( sourceName, "qunit-source" ); if ( bad === 0 ) { addClass( sourceName, "qunit-collapsed" ); } addEvent( testTitle, "click", function() { toggleClass( sourceName, "qunit-collapsed" ); }); testItem.appendChild( sourceName ); } }); if ( defined.document ) { // Avoid readyState issue with phantomjs // Ref: #818 var notPhantom = ( function( p ) { return !( p && p.version && p.version.major > 0 ); } )( window.phantom ); if ( notPhantom && document.readyState === "complete" ) { QUnit.load(); } else { addEvent( window, "load", QUnit.load ); } } else { config.pageLoaded = true; config.autorun = true; } })(); qunit-1.22.0/reporter/000077500000000000000000000000001266310060400145745ustar00rootroot00000000000000qunit-1.22.0/reporter/html.js000066400000000000000000000526711266310060400161110ustar00rootroot00000000000000(function() { // Don't load the HTML Reporter on non-Browser environments if ( typeof window === "undefined" || !window.document ) { return; } // Deprecated QUnit.init - Ref #530 // Re-initialize the configuration options QUnit.init = function() { var tests, banner, result, qunit, config = QUnit.config; config.stats = { all: 0, bad: 0 }; config.moduleStats = { all: 0, bad: 0 }; config.started = 0; config.updateRate = 1000; config.blocking = false; config.autostart = true; config.autorun = false; config.filter = ""; config.queue = []; // Return on non-browser environments // This is necessary to not break on node tests if ( typeof window === "undefined" ) { return; } qunit = id( "qunit" ); if ( qunit ) { qunit.innerHTML = "

      " + escapeText( document.title ) + "

      " + "

      " + "
      " + "

      " + "
        "; } tests = id( "qunit-tests" ); banner = id( "qunit-banner" ); result = id( "qunit-testresult" ); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
         "; } }; var config = QUnit.config, collapseNext = false, hasOwn = Object.prototype.hasOwnProperty, defined = { document: window.document !== undefined, sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch ( e ) { return false; } }()) }, modulesList = []; /** * Escape text for attribute or text content. */ function escapeText( s ) { if ( !s ) { return ""; } s = s + ""; // Both single quotes and double quotes (for attributes) return s.replace( /['"<>&]/g, function( s ) { switch ( s ) { case "'": return "'"; case "\"": return """; case "<": return "<"; case ">": return ">"; case "&": return "&"; } }); } /** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */ function addEvent( elem, type, fn ) { if ( elem.addEventListener ) { // Standards-based browsers elem.addEventListener( type, fn, false ); } else if ( elem.attachEvent ) { // support: IE <9 elem.attachEvent( "on" + type, function() { var event = window.event; if ( !event.target ) { event.target = event.srcElement || document; } fn.call( elem, event ); }); } } /** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */ function addEvents( elems, type, fn ) { var i = elems.length; while ( i-- ) { addEvent( elems[ i ], type, fn ); } } function hasClass( elem, name ) { return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; } function addClass( elem, name ) { if ( !hasClass( elem, name ) ) { elem.className += ( elem.className ? " " : "" ) + name; } } function toggleClass( elem, name ) { if ( hasClass( elem, name ) ) { removeClass( elem, name ); } else { addClass( elem, name ); } } function removeClass( elem, name ) { var set = " " + elem.className + " "; // Class name may appear multiple times while ( set.indexOf( " " + name + " " ) >= 0 ) { set = set.replace( " " + name + " ", " " ); } // trim for prettiness elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); } function id( name ) { return defined.document && document.getElementById && document.getElementById( name ); } function getUrlConfigHtml() { var i, j, val, escaped, escapedTooltip, selection = false, len = config.urlConfig.length, urlConfigHtml = ""; for ( i = 0; i < len; i++ ) { val = config.urlConfig[ i ]; if ( typeof val === "string" ) { val = { id: val, label: val }; } escaped = escapeText( val.id ); escapedTooltip = escapeText( val.tooltip ); if ( config[ val.id ] === undefined ) { config[ val.id ] = QUnit.urlParams[ val.id ]; } if ( !val.value || typeof val.value === "string" ) { urlConfigHtml += ""; } else { urlConfigHtml += ""; } } return urlConfigHtml; } // Handle "click" events on toolbar checkboxes and "change" for select menus. // Updates the URL with the new state of `config.urlConfig` values. function toolbarChanged() { var updatedUrl, value, field = this, params = {}; // Detect if field is a select menu or a checkbox if ( "selectedIndex" in field ) { value = field.options[ field.selectedIndex ].value || undefined; } else { value = field.checked ? ( field.defaultValue || true ) : undefined; } params[ field.name ] = value; updatedUrl = setUrl( params ); if ( "hidepassed" === field.name && "replaceState" in window.history ) { config[ field.name ] = value || false; if ( value ) { addClass( id( "qunit-tests" ), "hidepass" ); } else { removeClass( id( "qunit-tests" ), "hidepass" ); } // It is not necessary to refresh the whole page window.history.replaceState( null, "", updatedUrl ); } else { window.location = updatedUrl; } } function setUrl( params ) { var key, querystring = "?"; params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); for ( key in params ) { if ( hasOwn.call( params, key ) ) { if ( params[ key ] === undefined ) { continue; } querystring += encodeURIComponent( key ); if ( params[ key ] !== true ) { querystring += "=" + encodeURIComponent( params[ key ] ); } querystring += "&"; } } return location.protocol + "//" + location.host + location.pathname + querystring.slice( 0, -1 ); } function applyUrlParams() { var selectedModule, modulesList = id( "qunit-modulefilter" ), filter = id( "qunit-filter-input" ).value; selectedModule = modulesList ? decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : undefined; window.location = setUrl({ module: ( selectedModule === "" ) ? undefined : selectedModule, filter: ( filter === "" ) ? undefined : filter, // Remove testId filter testId: undefined }); } function toolbarUrlConfigContainer() { var urlConfigContainer = document.createElement( "span" ); urlConfigContainer.innerHTML = getUrlConfigHtml(); addClass( urlConfigContainer, "qunit-url-config" ); // For oldIE support: // * Add handlers to the individual elements instead of the container // * Use "click" instead of "change" for checkboxes addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); return urlConfigContainer; } function toolbarLooseFilter() { var filter = document.createElement( "form" ), label = document.createElement( "label" ), input = document.createElement( "input" ), button = document.createElement( "button" ); addClass( filter, "qunit-filter" ); label.innerHTML = "Filter: "; input.type = "text"; input.value = config.filter || ""; input.name = "filter"; input.id = "qunit-filter-input"; button.innerHTML = "Go"; label.appendChild( input ); filter.appendChild( label ); filter.appendChild( button ); addEvent( filter, "submit", function( ev ) { applyUrlParams(); if ( ev && ev.preventDefault ) { ev.preventDefault(); } return false; }); return filter; } function toolbarModuleFilterHtml() { var i, moduleFilterHtml = ""; if ( !modulesList.length ) { return false; } modulesList.sort(function( a, b ) { return a.localeCompare( b ); }); moduleFilterHtml += "" + ""; return moduleFilterHtml; } function toolbarModuleFilter() { var toolbar = id( "qunit-testrunner-toolbar" ), moduleFilter = document.createElement( "span" ), moduleFilterHtml = toolbarModuleFilterHtml(); if ( !toolbar || !moduleFilterHtml ) { return false; } moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); moduleFilter.innerHTML = moduleFilterHtml; addEvent( moduleFilter.lastChild, "change", applyUrlParams ); toolbar.appendChild( moduleFilter ); } function appendToolbar() { var toolbar = id( "qunit-testrunner-toolbar" ); if ( toolbar ) { toolbar.appendChild( toolbarUrlConfigContainer() ); toolbar.appendChild( toolbarLooseFilter() ); } } function appendHeader() { var header = id( "qunit-header" ); if ( header ) { header.innerHTML = "" + header.innerHTML + " "; } } function appendBanner() { var banner = id( "qunit-banner" ); if ( banner ) { banner.className = ""; } } function appendTestResults() { var tests = id( "qunit-tests" ), result = id( "qunit-testresult" ); if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { tests.innerHTML = ""; result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
         "; } } function storeFixture() { var fixture = id( "qunit-fixture" ); if ( fixture ) { config.fixture = fixture.innerHTML; } } function appendFilteredTest() { var testId = QUnit.config.testId; if ( !testId || testId.length <= 0 ) { return ""; } return "
        Rerunning selected tests: " + escapeText( testId.join(", ") ) + " " + "Run all tests" + "
        "; } function appendUserAgent() { var userAgent = id( "qunit-userAgent" ); if ( userAgent ) { userAgent.innerHTML = ""; userAgent.appendChild( document.createTextNode( "QUnit " + QUnit.version + "; " + navigator.userAgent ) ); } } function appendTestsList( modules ) { var i, l, x, z, test, moduleObj; for ( i = 0, l = modules.length; i < l; i++ ) { moduleObj = modules[ i ]; if ( moduleObj.name ) { modulesList.push( moduleObj.name ); } for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { test = moduleObj.tests[ x ]; appendTest( test.name, test.testId, moduleObj.name ); } } } function appendTest( name, testId, moduleName ) { var title, rerunTrigger, testBlock, assertList, tests = id( "qunit-tests" ); if ( !tests ) { return; } title = document.createElement( "strong" ); title.innerHTML = getNameHtml( name, moduleName ); rerunTrigger = document.createElement( "a" ); rerunTrigger.innerHTML = "Rerun"; rerunTrigger.href = setUrl({ testId: testId }); testBlock = document.createElement( "li" ); testBlock.appendChild( title ); testBlock.appendChild( rerunTrigger ); testBlock.id = "qunit-test-output-" + testId; assertList = document.createElement( "ol" ); assertList.className = "qunit-assert-list"; testBlock.appendChild( assertList ); tests.appendChild( testBlock ); } // HTML Reporter initialization and load QUnit.begin(function( details ) { var qunit = id( "qunit" ); // Fixture is the only one necessary to run without the #qunit element storeFixture(); if ( qunit ) { qunit.innerHTML = "

        " + escapeText( document.title ) + "

        " + "

        " + "
        " + appendFilteredTest() + "

        " + "
          "; } appendHeader(); appendBanner(); appendTestResults(); appendUserAgent(); appendToolbar(); appendTestsList( details.modules ); toolbarModuleFilter(); if ( qunit && config.hidepassed ) { addClass( qunit.lastChild, "hidepass" ); } }); QUnit.done(function( details ) { var i, key, banner = id( "qunit-banner" ), tests = id( "qunit-tests" ), html = [ "Tests completed in ", details.runtime, " milliseconds.
          ", "", details.passed, " assertions of ", details.total, " passed, ", details.failed, " failed." ].join( "" ); if ( banner ) { banner.className = details.failed ? "qunit-fail" : "qunit-pass"; } if ( tests ) { id( "qunit-testresult" ).innerHTML = html; } if ( config.altertitle && defined.document && document.title ) { // show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document.title = [ ( details.failed ? "\u2716" : "\u2714" ), document.title.replace( /^[\u2714\u2716] /i, "" ) ].join( " " ); } // clear own sessionStorage items if all tests passed if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { for ( i = 0; i < sessionStorage.length; i++ ) { key = sessionStorage.key( i++ ); if ( key.indexOf( "qunit-test-" ) === 0 ) { sessionStorage.removeItem( key ); } } } // scroll back to top to show results if ( config.scrolltop && window.scrollTo ) { window.scrollTo( 0, 0 ); } }); function getNameHtml( name, module ) { var nameHtml = ""; if ( module ) { nameHtml = "" + escapeText( module ) + ": "; } nameHtml += "" + escapeText( name ) + ""; return nameHtml; } QUnit.testStart(function( details ) { var running, testBlock, bad; testBlock = id( "qunit-test-output-" + details.testId ); if ( testBlock ) { testBlock.className = "running"; } else { // Report later registered tests appendTest( details.name, details.testId, details.module ); } running = id( "qunit-testresult" ); if ( running ) { bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); running.innerHTML = ( bad ? "Rerunning previously failed test:
          " : "Running:
          " ) + getNameHtml( details.name, details.module ); } }); function stripHtml( string ) { // strip tags, html entity and whitespaces return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); } QUnit.log(function( details ) { var assertList, assertLi, message, expected, actual, diff, showDiff = false, testItem = id( "qunit-test-output-" + details.testId ); if ( !testItem ) { return; } message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); message = "" + message + ""; message += "@ " + details.runtime + " ms"; // pushFailure doesn't provide details.expected // when it calls, it's implicit to also not show expected and diff stuff // Also, we need to check details.expected existence, as it can exist and be undefined if ( !details.result && hasOwn.call( details, "expected" ) ) { if ( details.negative ) { expected = escapeText( "NOT " + QUnit.dump.parse( details.expected ) ); } else { expected = escapeText( QUnit.dump.parse( details.expected ) ); } actual = escapeText( QUnit.dump.parse( details.actual ) ); message += ""; if ( actual !== expected ) { message += ""; // Don't show diff if actual or expected are booleans if ( !( /^(true|false)$/.test( actual ) ) && !( /^(true|false)$/.test( expected ) ) ) { diff = QUnit.diff( expected, actual ); showDiff = stripHtml( diff ).length !== stripHtml( expected ).length + stripHtml( actual ).length; } // Don't show diff if expected and actual are totally different if ( showDiff ) { message += ""; } } else if ( expected.indexOf( "[object Array]" ) !== -1 || expected.indexOf( "[object Object]" ) !== -1 ) { message += ""; } if ( details.source ) { message += ""; } message += "
          Expected:
          " +
          			expected +
          			"
          Result:
          " +
          				actual + "
          Diff:
          " +
          					diff + "
          Message: " + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").

          Hint: Use QUnit.dump.maxDepth to " + " run with a higher max depth or " + "Rerun without max depth.

          Source:
          " +
          				escapeText( details.source ) + "
          "; // this occurs when pushFailure is set and we have an extracted stack trace } else if ( !details.result && details.source ) { message += "" + "" + "
          Source:
          " +
          			escapeText( details.source ) + "
          "; } assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; assertLi = document.createElement( "li" ); assertLi.className = details.result ? "pass" : "fail"; assertLi.innerHTML = message; assertList.appendChild( assertLi ); }); QUnit.testDone(function( details ) { var testTitle, time, testItem, assertList, good, bad, testCounts, skipped, sourceName, tests = id( "qunit-tests" ); if ( !tests ) { return; } testItem = id( "qunit-test-output-" + details.testId ); assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; good = details.passed; bad = details.failed; // store result when possible if ( config.reorder && defined.sessionStorage ) { if ( bad ) { sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); } else { sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); } } if ( bad === 0 ) { // Collapse the passing tests addClass( assertList, "qunit-collapsed" ); } else if ( bad && config.collapse && !collapseNext ) { // Skip collapsing the first failing test collapseNext = true; } else { // Collapse remaining tests addClass( assertList, "qunit-collapsed" ); } // testItem.firstChild is the test name testTitle = testItem.firstChild; testCounts = bad ? "" + bad + ", " + "" + good + ", " : ""; testTitle.innerHTML += " (" + testCounts + details.assertions.length + ")"; if ( details.skipped ) { testItem.className = "skipped"; skipped = document.createElement( "em" ); skipped.className = "qunit-skipped-label"; skipped.innerHTML = "skipped"; testItem.insertBefore( skipped, testTitle ); } else { addEvent( testTitle, "click", function() { toggleClass( assertList, "qunit-collapsed" ); }); testItem.className = bad ? "fail" : "pass"; time = document.createElement( "span" ); time.className = "runtime"; time.innerHTML = details.runtime + " ms"; testItem.insertBefore( time, assertList ); } // Show the source of the test when showing assertions if ( details.source ) { sourceName = document.createElement( "p" ); sourceName.innerHTML = "Source: " + details.source; addClass( sourceName, "qunit-source" ); if ( bad === 0 ) { addClass( sourceName, "qunit-collapsed" ); } addEvent( testTitle, "click", function() { toggleClass( sourceName, "qunit-collapsed" ); }); testItem.appendChild( sourceName ); } }); if ( defined.document ) { // Avoid readyState issue with phantomjs // Ref: #818 var notPhantom = ( function( p ) { return !( p && p.version && p.version.major > 0 ); } )( window.phantom ); if ( notPhantom && document.readyState === "complete" ) { QUnit.load(); } else { addEvent( window, "load", QUnit.load ); } } else { config.pageLoaded = true; config.autorun = true; } })(); qunit-1.22.0/src/000077500000000000000000000000001266310060400135215ustar00rootroot00000000000000qunit-1.22.0/src/assert.js000066400000000000000000000160761266310060400153720ustar00rootroot00000000000000function Assert( testContext ) { this.test = testContext; } // Assert helpers QUnit.assert = Assert.prototype = { // Specify the number of expected assertions to guarantee that failed test // (no assertions are run at all) don't slip through. expect: function( asserts ) { if ( arguments.length === 1 ) { this.test.expected = asserts; } else { return this.test.expected; } }, // Increment this Test's semaphore counter, then return a function that // decrements that counter a maximum of once. async: function( count ) { var test = this.test, popped = false, acceptCallCount = count; if ( typeof acceptCallCount === "undefined" ) { acceptCallCount = 1; } test.semaphore += 1; test.usedAsync = true; pauseProcessing(); return function done() { if ( popped ) { test.pushFailure( "Too many calls to the `assert.async` callback", sourceFromStacktrace( 2 ) ); return; } acceptCallCount -= 1; if ( acceptCallCount > 0 ) { return; } test.semaphore -= 1; popped = true; resumeProcessing(); }; }, // Exports test.push() to the user API // Alias of pushResult. push: function( result, actual, expected, message, negative ) { var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; return currentAssert.pushResult( { result: result, actual: actual, expected: expected, message: message, negative: negative } ); }, pushResult: function( resultInfo ) { // resultInfo = { result, actual, expected, message, negative } var assert = this, currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; // Backwards compatibility fix. // Allows the direct use of global exported assertions and QUnit.assert.* // Although, it's use is not recommended as it can leak assertions // to other tests from async tests, because we only get a reference to the current test, // not exactly the test where assertion were intended to be called. if ( !currentTest ) { throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", sourceFromStacktrace( 2 ) ); // Allow this assertion to continue running anyway... } if ( !( assert instanceof Assert ) ) { assert = currentTest.assert; } return assert.test.pushResult( resultInfo ); }, ok: function( result, message ) { message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !!result, actual: result, expected: true, message: message } ); }, notOk: function( result, message ) { message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !result, actual: result, expected: false, message: message } ); }, equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected == actual, actual: actual, expected: expected, message: message } ); }, notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected != actual, actual: actual, expected: expected, message: message, negative: true } ); }, propEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notPropEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, deepEqual: function( actual, expected, message ) { this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notDeepEqual: function( actual, expected, message ) { this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, strictEqual: function( actual, expected, message ) { this.pushResult( { result: expected === actual, actual: actual, expected: expected, message: message } ); }, notStrictEqual: function( actual, expected, message ) { this.pushResult( { result: expected !== actual, actual: actual, expected: expected, message: message, negative: true } ); }, "throws": function( block, expected, message ) { var actual, expectedType, expectedOutput = expected, ok = false, currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; // 'expected' is optional unless doing string comparison if ( message == null && typeof expected === "string" ) { message = expected; expected = null; } currentTest.ignoreGlobalErrors = true; try { block.call( currentTest.testEnvironment ); } catch (e) { actual = e; } currentTest.ignoreGlobalErrors = false; if ( actual ) { expectedType = QUnit.objectType( expected ); // we don't want to validate thrown error if ( !expected ) { ok = true; expectedOutput = null; // expected is a regexp } else if ( expectedType === "regexp" ) { ok = expected.test( errorString( actual ) ); // expected is a string } else if ( expectedType === "string" ) { ok = expected === errorString( actual ); // expected is a constructor, maybe an Error constructor } else if ( expectedType === "function" && actual instanceof expected ) { ok = true; // expected is an Error object } else if ( expectedType === "object" ) { ok = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; // expected is a validation function which returns true if validation passed } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { expectedOutput = null; ok = true; } } currentTest.assert.pushResult( { result: ok, actual: actual, expected: expectedOutput, message: message } ); } }; // Provide an alternative to assert.throws(), for environments that consider throws a reserved word // Known to us are: Closure Compiler, Narwhal (function() { /*jshint sub:true */ Assert.prototype.raises = Assert.prototype[ "throws" ]; }()); function errorString( error ) { var name, message, resultErrorString = error.toString(); if ( resultErrorString.substring( 0, 7 ) === "[object" ) { name = error.name ? error.name.toString() : "Error"; message = error.message ? error.message.toString() : ""; if ( name && message ) { return name + ": " + message; } else if ( name ) { return name; } else if ( message ) { return message; } else { return "Error"; } } else { return resultErrorString; } } qunit-1.22.0/src/core.js000066400000000000000000000175231266310060400150170ustar00rootroot00000000000000QUnit.urlParams = urlParams; // Figure out if we're running the tests from a server or not QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); // Expose the current QUnit version QUnit.version = "@VERSION"; extend( QUnit, { // call on start of module test to prepend name to all tests module: function( name, testEnvironment, executeNow ) { var module, moduleFns; var currentModule = config.currentModule; if ( arguments.length === 2 ) { if ( testEnvironment instanceof Function ) { executeNow = testEnvironment; testEnvironment = undefined; } } // DEPRECATED: handles setup/teardown functions, // beforeEach and afterEach should be used instead if ( testEnvironment && testEnvironment.setup ) { testEnvironment.beforeEach = testEnvironment.setup; delete testEnvironment.setup; } if ( testEnvironment && testEnvironment.teardown ) { testEnvironment.afterEach = testEnvironment.teardown; delete testEnvironment.teardown; } module = createModule(); moduleFns = { beforeEach: setHook( module, "beforeEach" ), afterEach: setHook( module, "afterEach" ) }; if ( executeNow instanceof Function ) { config.moduleStack.push( module ); setCurrentModule( module ); executeNow.call( module.testEnvironment, moduleFns ); config.moduleStack.pop(); module = module.parentModule || currentModule; } setCurrentModule( module ); function createModule() { var parentModule = config.moduleStack.length ? config.moduleStack.slice( -1 )[ 0 ] : null; var moduleName = parentModule !== null ? [ parentModule.name, name ].join( " > " ) : name; var module = { name: moduleName, parentModule: parentModule, tests: [] }; var env = {}; if ( parentModule ) { extend( env, parentModule.testEnvironment ); delete env.beforeEach; delete env.afterEach; } extend( env, testEnvironment ); module.testEnvironment = env; config.modules.push( module ); return module; } function setCurrentModule( module ) { config.currentModule = module; } }, // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. asyncTest: asyncTest, test: test, skip: skip, only: only, // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. start: function( count ) { var globalStartAlreadyCalled = globalStartCalled; if ( !config.current ) { globalStartCalled = true; if ( runStarted ) { throw new Error( "Called start() outside of a test context while already started" ); } else if ( globalStartAlreadyCalled || count > 1 ) { throw new Error( "Called start() outside of a test context too many times" ); } else if ( config.autostart ) { throw new Error( "Called start() outside of a test context when " + "QUnit.config.autostart was true" ); } else if ( !config.pageLoaded ) { // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it config.autostart = true; return; } } else { // If a test is running, adjust its semaphore config.current.semaphore -= count || 1; // If semaphore is non-numeric, throw error if ( isNaN( config.current.semaphore ) ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() with a non-numeric decrement.", sourceFromStacktrace( 2 ) ); return; } // Don't start until equal number of stop-calls if ( config.current.semaphore > 0 ) { return; } // throw an Error if start is called more often than stop if ( config.current.semaphore < 0 ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() while already started (test's semaphore was 0 already)", sourceFromStacktrace( 2 ) ); return; } } resumeProcessing(); }, // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. stop: function( count ) { // If there isn't a test running, don't allow QUnit.stop() to be called if ( !config.current ) { throw new Error( "Called stop() outside of a test context" ); } // If a test is running, adjust its semaphore config.current.semaphore += count || 1; pauseProcessing(); }, config: config, is: is, objectType: objectType, extend: extend, load: function() { config.pageLoaded = true; // Initialize the configuration options extend( config, { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: 0, updateRate: 1000, autostart: true, filter: "" }, true ); config.blocking = false; if ( config.autostart ) { resumeProcessing(); } }, stack: function( offset ) { offset = ( offset || 0 ) + 2; return sourceFromStacktrace( offset ); } }); registerLoggingCallbacks( QUnit ); function begin() { var i, l, modulesLog = []; // If the test run hasn't officially begun yet if ( !config.started ) { // Record the time of the test run's beginning config.started = now(); verifyLoggingCallbacks(); // Delete the loose unnamed module if unused. if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { config.modules.shift(); } // Avoid unnecessary information by not logging modules' test environments for ( i = 0, l = config.modules.length; i < l; i++ ) { modulesLog.push({ name: config.modules[ i ].name, tests: config.modules[ i ].tests }); } // The test run is officially beginning now runLoggingCallbacks( "begin", { totalTests: Test.count, modules: modulesLog }); } config.blocking = false; process( true ); } function process( last ) { function next() { process( last ); } var start = now(); config.depth = ( config.depth || 0 ) + 1; while ( config.queue.length && !config.blocking ) { if ( !defined.setTimeout || config.updateRate <= 0 || ( ( now() - start ) < config.updateRate ) ) { if ( config.current ) { // Reset async tracking for each phase of the Test lifecycle config.current.usedAsync = false; } config.queue.shift()(); } else { setTimeout( next, 13 ); break; } } config.depth--; if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } function pauseProcessing() { config.blocking = true; if ( config.testTimeout && defined.setTimeout ) { clearTimeout( config.timeout ); config.timeout = setTimeout(function() { if ( config.current ) { config.current.semaphore = 0; QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); } else { throw new Error( "Test timed out" ); } resumeProcessing(); }, config.testTimeout ); } } function resumeProcessing() { runStarted = true; // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) if ( defined.setTimeout ) { setTimeout(function() { if ( config.current && config.current.semaphore > 0 ) { return; } if ( config.timeout ) { clearTimeout( config.timeout ); } begin(); }, 13 ); } else { begin(); } } function done() { var runtime, passed; config.autorun = true; // Log the last module results if ( config.previousModule ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started }); } delete config.previousModule; runtime = now() - config.started; passed = config.stats.all - config.stats.bad; runLoggingCallbacks( "done", { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime }); } function setHook( module, hookName ) { if ( module.testEnvironment === undefined ) { module.testEnvironment = {}; } return function( callback ) { module.testEnvironment[ hookName ] = callback; }; } qunit-1.22.0/src/core/000077500000000000000000000000001266310060400144515ustar00rootroot00000000000000qunit-1.22.0/src/core/config.js000066400000000000000000000043201266310060400162530ustar00rootroot00000000000000/** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ var config = { // The queue of tests to run queue: [], // block until document ready blocking: true, // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // by default, modify document.title when suite is done altertitle: true, // HTML Reporter: collapse every test except the first failing test // If false, all failing tests will be expanded collapse: true, // by default, scroll to top of the page when suite is done scrolltop: true, // depth up-to which object will be dumped maxDepth: 5, // when enabled, all tests must call expect() requireExpects: false, // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ { id: "hidepassed", label: "Hide passed tests", tooltip: "Only show tests and assertions that fail. Stored as query-strings." }, { id: "noglobals", label: "Check for Globals", tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." } ], // Set of all modules. modules: [], // Stack of nested modules moduleStack: [], // The first unnamed module currentModule: { name: "", tests: [] }, callbacks: {} }; var urlParams = defined.document ? getUrlParams() : {}; // Push a loose unnamed module to the modules collection config.modules.push( config.currentModule ); if ( urlParams.filter === true ) { delete urlParams.filter; } // String search anywhere in moduleName+testName config.filter = urlParams.filter; config.testId = []; if ( urlParams.testId ) { // Ensure that urlParams.testId is an array urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); for (var i = 0; i < urlParams.testId.length; i++ ) { config.testId.push( urlParams.testId[ i ] ); } } qunit-1.22.0/src/core/initialize.js000066400000000000000000000013621266310060400171520ustar00rootroot00000000000000var QUnit = {}; var Date = global.Date; var now = Date.now || function() { return new Date().getTime(); }; var setTimeout = global.setTimeout; var clearTimeout = global.clearTimeout; // Store a local window from the global to allow direct references. var window = global.window; var defined = { document: window && window.document !== undefined, setTimeout: setTimeout !== undefined, sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch ( e ) { return false; } }() ) }; var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); var globalStartCalled = false; var runStarted = false; qunit-1.22.0/src/core/logging.js000066400000000000000000000042211266310060400164340ustar00rootroot00000000000000var loggingCallbacks = {}; // Register logging callbacks function registerLoggingCallbacks( obj ) { var i, l, key, callbackNames = [ "begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone" ]; function registerLoggingCallback( key ) { var loggingCallback = function( callback ) { if ( objectType( callback ) !== "function" ) { throw new Error( "QUnit logging methods require a callback function as their first parameters." ); } config.callbacks[ key ].push( callback ); }; // DEPRECATED: This will be removed on QUnit 2.0.0+ // Stores the registered functions allowing restoring // at verifyLoggingCallbacks() if modified loggingCallbacks[ key ] = loggingCallback; return loggingCallback; } for ( i = 0, l = callbackNames.length; i < l; i++ ) { key = callbackNames[ i ]; // Initialize key collection of logging callback if ( objectType( config.callbacks[ key ] ) === "undefined" ) { config.callbacks[ key ] = []; } obj[ key ] = registerLoggingCallback( key ); } } function runLoggingCallbacks( key, args ) { var i, l, callbacks; callbacks = config.callbacks[ key ]; for ( i = 0, l = callbacks.length; i < l; i++ ) { callbacks[ i ]( args ); } } // DEPRECATED: This will be removed on 2.0.0+ // This function verifies if the loggingCallbacks were modified by the user // If so, it will restore it, assign the given callback and print a console warning function verifyLoggingCallbacks() { var loggingCallback, userCallback; for ( loggingCallback in loggingCallbacks ) { if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { userCallback = QUnit[ loggingCallback ]; // Restore the callback function QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; // Assign the deprecated given callback QUnit[ loggingCallback ]( userCallback ); if ( global.console && global.console.warn ) { global.console.warn( "QUnit." + loggingCallback + " was replaced with a new value.\n" + "Please, check out the documentation on how to apply logging callbacks.\n" + "Reference: https://api.qunitjs.com/category/callbacks/" ); } } } } qunit-1.22.0/src/core/onerror.js000066400000000000000000000016771266310060400165100ustar00rootroot00000000000000( function() { if ( !defined.document ) { return; } // `onErrorFnPrev` initialized at top of scope // Preserve other handlers var onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will suppress the default browser handler, // returning false will let it run. window.onerror = function( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); } // Treat return value as window.onerror itself does, // Only do our handling if not suppressed. if ( ret !== true ) { if ( QUnit.config.current ) { if ( QUnit.config.current.ignoreGlobalErrors ) { return true; } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { QUnit.test( "global failure", extend(function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); }, { validTest: true } ) ); } return false; } return ret; }; } )(); qunit-1.22.0/src/core/stacktrace.js000066400000000000000000000024061266310060400171350ustar00rootroot00000000000000// Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 4 : offset; var stack, include, i; if ( e.stack ) { stack = e.stack.split( "\n" ); if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); } if ( fileName ) { include = []; for ( i = offset; i < stack.length; i++ ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) { break; } include.push( stack[ i ] ); } if ( include.length ) { return include.join( "\n" ); } } return stack[ offset ]; // Support: Safari <=6 only } else if ( e.sourceURL ) { // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } function sourceFromStacktrace( offset ) { var error = new Error(); // Support: Safari <=7 only, IE <=10 - 11 only // Not all browsers generate the `stack` property for `new Error()`, see also #636 if ( !error.stack ) { try { throw error; } catch ( err ) { error = err; } } return extractStacktrace( error, offset ); } qunit-1.22.0/src/core/utilities.js000066400000000000000000000057611266310060400170330ustar00rootroot00000000000000var toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty; // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var i, j, result = a.slice(); for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { if ( result[ i ] === b[ j ] ) { result.splice( i, 1 ); i--; break; } } } return result; } // from jquery.js function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ function objectValues ( obj ) { var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { val = obj[ key ]; vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } } return vals; } function extend( a, b, undefOnly ) { for ( var prop in b ) { if ( hasOwn.call( b, prop ) ) { // Avoid "Member not found" error in IE8 caused by messing with window.constructor // This block runs on every environment, so `global` is being used instead of `window` // to avoid errors on node. if ( prop !== "constructor" || a !== global ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { a[ prop ] = b[ prop ]; } } } } return a; } function objectType( obj ) { if ( typeof obj === "undefined" ) { return "undefined"; } // Consider: typeof null === object if ( obj === null ) { return "null"; } var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), type = match && match[ 1 ]; switch ( type ) { case "Number": if ( isNaN( obj ) ) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Set": case "Map": case "Date": case "RegExp": case "Function": case "Symbol": return type.toLowerCase(); } if ( typeof obj === "object" ) { return "object"; } } // Safe object type checking function is( type, obj ) { return QUnit.objectType( obj ) === type; } var getUrlParams = function() { var i, param, name, value; var urlParams = {}; var location = window.location; var params = location.search.slice( 1 ).split( "&" ); var length = params.length; for ( i = 0; i < length; i++ ) { if ( params[ i ] ) { param = params[ i ].split( "=" ); name = decodeURIComponent( param[ 0 ] ); // allow just a key to turn on a flag, e.g., test.html?noglobals value = param.length === 1 || decodeURIComponent( param.slice( 1 ).join( "=" ) ) ; if ( urlParams[ name ] ) { urlParams[ name ] = [].concat( urlParams[ name ], value ); } else { urlParams[ name ] = value; } } } return urlParams; }; qunit-1.22.0/src/diff.js000066400000000000000000001101621266310060400147700ustar00rootroot00000000000000/* * This file is a modified version of google-diff-match-patch's JavaScript implementation * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), * modifications are licensed as more fully set forth in LICENSE.txt. * * The original source of google-diff-match-patch is attributable and licensed as follows: * * Copyright 2006 Google Inc. * https://code.google.com/p/google-diff-match-patch/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * More Info: * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * */ QUnit.diff = ( function() { function DiffMatchPatch() { } // DIFF FUNCTIONS /** * The data structure representing a diff is an array of tuples: * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] * which means: delete 'Hello', add 'Goodbye' and keep ' world.' */ var DIFF_DELETE = -1, DIFF_INSERT = 1, DIFF_EQUAL = 0; /** * Find the differences between two texts. Simplifies the problem by stripping * any common prefix or suffix off the texts before diffing. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean=} optChecklines Optional speedup flag. If present and false, * then don't run a line-level diff first to identify the changed areas. * Defaults to true, which does a faster, slightly less optimal diff. * @return {!Array.} Array of diff tuples. */ DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) { var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; // The diff must be complete in up to 1 second. deadline = ( new Date() ).getTime() + 1000; // Check for null inputs. if ( text1 === null || text2 === null ) { throw new Error( "Null input. (DiffMain)" ); } // Check for equality (speedup). if ( text1 === text2 ) { if ( text1 ) { return [ [ DIFF_EQUAL, text1 ] ]; } return []; } if ( typeof optChecklines === "undefined" ) { optChecklines = true; } checklines = optChecklines; // Trim off common prefix (speedup). commonlength = this.diffCommonPrefix( text1, text2 ); commonprefix = text1.substring( 0, commonlength ); text1 = text1.substring( commonlength ); text2 = text2.substring( commonlength ); // Trim off common suffix (speedup). commonlength = this.diffCommonSuffix( text1, text2 ); commonsuffix = text1.substring( text1.length - commonlength ); text1 = text1.substring( 0, text1.length - commonlength ); text2 = text2.substring( 0, text2.length - commonlength ); // Compute the diff on the middle block. diffs = this.diffCompute( text1, text2, checklines, deadline ); // Restore the prefix and suffix. if ( commonprefix ) { diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); } if ( commonsuffix ) { diffs.push( [ DIFF_EQUAL, commonsuffix ] ); } this.diffCleanupMerge( diffs ); return diffs; }; /** * Reduce the number of edits by eliminating operationally trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Is there an insertion operation before the last equality. preIns = false; // Is there a deletion operation before the last equality. preDel = false; // Is there an insertion operation after the last equality. postIns = false; // Is there a deletion operation after the last equality. postDel = false; while ( pointer < diffs.length ) { // Equality found. if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) { // Candidate found. equalities[ equalitiesLength++ ] = pointer; preIns = postIns; preDel = postDel; lastequality = diffs[ pointer ][ 1 ]; } else { // Not a candidate, and can never become one. equalitiesLength = 0; lastequality = null; } postIns = postDel = false; // An insertion or deletion. } else { if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { postDel = true; } else { postIns = true; } /* * Five types to be split: * ABXYCD * AXCD * ABXC * AXCD * ABXC */ if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || ( ( lastequality.length < 2 ) && ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { // Duplicate record. diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); // Change second copy to insert. diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; equalitiesLength--; // Throw away the equality we just deleted; lastequality = null; if ( preIns && preDel ) { // No changes made which could affect previous entry, keep going. postIns = postDel = true; equalitiesLength = 0; } else { equalitiesLength--; // Throw away the previous equality. pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; postIns = postDel = false; } changes = true; } } pointer++; } if ( changes ) { this.diffCleanupMerge( diffs ); } }; /** * Convert a diff array into a pretty HTML report. * @param {!Array.} diffs Array of diff tuples. * @param {integer} string to be beautified. * @return {string} HTML representation. */ DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { var op, data, x, html = []; for ( x = 0; x < diffs.length; x++ ) { op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal) data = diffs[ x ][ 1 ]; // Text of change. switch ( op ) { case DIFF_INSERT: html[ x ] = "" + data + ""; break; case DIFF_DELETE: html[ x ] = "" + data + ""; break; case DIFF_EQUAL: html[ x ] = "" + data + ""; break; } } return html.join( "" ); }; /** * Determine the common prefix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the start of each * string. */ DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { var pointermid, pointermax, pointermin, pointerstart; // Quick check for common null cases. if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min( text1.length, text2.length ); pointermid = pointermax; pointerstart = 0; while ( pointermin < pointermid ) { if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { pointermin = pointermid; pointerstart = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); } return pointermid; }; /** * Determine the common suffix of two strings. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of each string. */ DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { var pointermid, pointermax, pointermin, pointerend; // Quick check for common null cases. if ( !text1 || !text2 || text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) { return 0; } // Binary search. // Performance analysis: https://neil.fraser.name/news/2007/10/09/ pointermin = 0; pointermax = Math.min( text1.length, text2.length ); pointermid = pointermax; pointerend = 0; while ( pointermin < pointermid ) { if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) === text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { pointermin = pointermid; pointerend = pointermin; } else { pointermax = pointermid; } pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); } return pointermid; }; /** * Find the differences between two texts. Assumes that the texts do not * have any common prefix or suffix. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {boolean} checklines Speedup flag. If false, then don't run a * line-level diff first to identify the changed areas. * If true, then run a faster, slightly less optimal diff. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; if ( !text1 ) { // Just add some text (speedup). return [ [ DIFF_INSERT, text2 ] ]; } if ( !text2 ) { // Just delete some text (speedup). return [ [ DIFF_DELETE, text1 ] ]; } longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; i = longtext.indexOf( shorttext ); if ( i !== -1 ) { // Shorter text is inside the longer text (speedup). diffs = [ [ DIFF_INSERT, longtext.substring( 0, i ) ], [ DIFF_EQUAL, shorttext ], [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] ]; // Swap insertions for deletions if diff is reversed. if ( text1.length > text2.length ) { diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE; } return diffs; } if ( shorttext.length === 1 ) { // Single character string. // After the previous speedup, the character can't be an equality. return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ]; } // Check to see if the problem can be split in two. hm = this.diffHalfMatch( text1, text2 ); if ( hm ) { // A half-match was found, sort out the return data. text1A = hm[ 0 ]; text1B = hm[ 1 ]; text2A = hm[ 2 ]; text2B = hm[ 3 ]; midCommon = hm[ 4 ]; // Send both pairs off for separate processing. diffsA = this.DiffMain( text1A, text2A, checklines, deadline ); diffsB = this.DiffMain( text1B, text2B, checklines, deadline ); // Merge the results. return diffsA.concat( [ [ DIFF_EQUAL, midCommon ] ], diffsB ); } if ( checklines && text1.length > 100 && text2.length > 100 ) { return this.diffLineMode( text1, text2, deadline ); } return this.diffBisect( text1, text2, deadline ); }; /** * Do the two texts share a substring which is at least half the length of the * longer text? * This speedup can produce non-minimal diffs. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {Array.} Five element Array, containing the prefix of * text1, the suffix of text1, the prefix of text2, the suffix of * text2 and the common middle. Or null if there was no match. * @private */ DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) { var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; longtext = text1.length > text2.length ? text1 : text2; shorttext = text1.length > text2.length ? text2 : text1; if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) { return null; // Pointless. } dmp = this; // 'this' becomes 'window' in a closure. /** * Does a substring of shorttext exist within longtext such that the substring * is at least half the length of longtext? * Closure, but does not reference any external variables. * @param {string} longtext Longer string. * @param {string} shorttext Shorter string. * @param {number} i Start index of quarter length substring within longtext. * @return {Array.} Five element Array, containing the prefix of * longtext, the suffix of longtext, the prefix of shorttext, the suffix * of shorttext and the common middle. Or null if there was no match. * @private */ function diffHalfMatchI( longtext, shorttext, i ) { var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; // Start with a 1/4 length substring at position i as a seed. seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) ); j = -1; bestCommon = ""; while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) { prefixLength = dmp.diffCommonPrefix( longtext.substring( i ), shorttext.substring( j ) ); suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ), shorttext.substring( 0, j ) ); if ( bestCommon.length < suffixLength + prefixLength ) { bestCommon = shorttext.substring( j - suffixLength, j ) + shorttext.substring( j, j + prefixLength ); bestLongtextA = longtext.substring( 0, i - suffixLength ); bestLongtextB = longtext.substring( i + prefixLength ); bestShorttextA = shorttext.substring( 0, j - suffixLength ); bestShorttextB = shorttext.substring( j + prefixLength ); } } if ( bestCommon.length * 2 >= longtext.length ) { return [ bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon ]; } else { return null; } } // First check if the second quarter is the seed for a half-match. hm1 = diffHalfMatchI( longtext, shorttext, Math.ceil( longtext.length / 4 ) ); // Check again based on the third quarter. hm2 = diffHalfMatchI( longtext, shorttext, Math.ceil( longtext.length / 2 ) ); if ( !hm1 && !hm2 ) { return null; } else if ( !hm2 ) { hm = hm1; } else if ( !hm1 ) { hm = hm2; } else { // Both matched. Select the longest. hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2; } // A half-match was found, sort out the return data. text1A, text1B, text2A, text2B; if ( text1.length > text2.length ) { text1A = hm[ 0 ]; text1B = hm[ 1 ]; text2A = hm[ 2 ]; text2B = hm[ 3 ]; } else { text2A = hm[ 0 ]; text2B = hm[ 1 ]; text1A = hm[ 2 ]; text1B = hm[ 3 ]; } midCommon = hm[ 4 ]; return [ text1A, text1B, text2A, text2B, midCommon ]; }; /** * Do a quick line-level diff on both strings, then rediff the parts for * greater accuracy. * This speedup can produce non-minimal diffs. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time when the diff should be complete by. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) { var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; // Scan the text on a line-by-line basis first. a = this.diffLinesToChars( text1, text2 ); text1 = a.chars1; text2 = a.chars2; linearray = a.lineArray; diffs = this.DiffMain( text1, text2, false, deadline ); // Convert the diff back to original text. this.diffCharsToLines( diffs, linearray ); // Eliminate freak matches (e.g. blank lines) this.diffCleanupSemantic( diffs ); // Rediff any replacement blocks, this time character-by-character. // Add a dummy entry at the end. diffs.push( [ DIFF_EQUAL, "" ] ); pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; while ( pointer < diffs.length ) { switch ( diffs[ pointer ][ 0 ] ) { case DIFF_INSERT: countInsert++; textInsert += diffs[ pointer ][ 1 ]; break; case DIFF_DELETE: countDelete++; textDelete += diffs[ pointer ][ 1 ]; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if ( countDelete >= 1 && countInsert >= 1 ) { // Delete the offending records and add the merged ones. diffs.splice( pointer - countDelete - countInsert, countDelete + countInsert ); pointer = pointer - countDelete - countInsert; a = this.DiffMain( textDelete, textInsert, false, deadline ); for ( j = a.length - 1; j >= 0; j-- ) { diffs.splice( pointer, 0, a[ j ] ); } pointer = pointer + a.length; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } pointer++; } diffs.pop(); // Remove the dummy entry at the end. return diffs; }; /** * Find the 'middle snake' of a diff, split the problem in two * and return the recursively constructed diff. * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) { var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; maxD = Math.ceil( ( text1Length + text2Length ) / 2 ); vOffset = maxD; vLength = 2 * maxD; v1 = new Array( vLength ); v2 = new Array( vLength ); // Setting all elements to -1 is faster in Chrome & Firefox than mixing // integers and undefined. for ( x = 0; x < vLength; x++ ) { v1[ x ] = -1; v2[ x ] = -1; } v1[ vOffset + 1 ] = 0; v2[ vOffset + 1 ] = 0; delta = text1Length - text2Length; // If the total number of characters is odd, then the front path will collide // with the reverse path. front = ( delta % 2 !== 0 ); // Offsets for start and end of k loop. // Prevents mapping of space beyond the grid. k1start = 0; k1end = 0; k2start = 0; k2end = 0; for ( d = 0; d < maxD; d++ ) { // Bail out if deadline is reached. if ( ( new Date() ).getTime() > deadline ) { break; } // Walk the front path one step. for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) { k1Offset = vOffset + k1; if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { x1 = v1[ k1Offset + 1 ]; } else { x1 = v1[ k1Offset - 1 ] + 1; } y1 = x1 - k1; while ( x1 < text1Length && y1 < text2Length && text1.charAt( x1 ) === text2.charAt( y1 ) ) { x1++; y1++; } v1[ k1Offset ] = x1; if ( x1 > text1Length ) { // Ran off the right of the graph. k1end += 2; } else if ( y1 > text2Length ) { // Ran off the bottom of the graph. k1start += 2; } else if ( front ) { k2Offset = vOffset + delta - k1; if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) { // Mirror x2 onto top-left coordinate system. x2 = text1Length - v2[ k2Offset ]; if ( x1 >= x2 ) { // Overlap detected. return this.diffBisectSplit( text1, text2, x1, y1, deadline ); } } } } // Walk the reverse path one step. for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) { k2Offset = vOffset + k2; if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { x2 = v2[ k2Offset + 1 ]; } else { x2 = v2[ k2Offset - 1 ] + 1; } y2 = x2 - k2; while ( x2 < text1Length && y2 < text2Length && text1.charAt( text1Length - x2 - 1 ) === text2.charAt( text2Length - y2 - 1 ) ) { x2++; y2++; } v2[ k2Offset ] = x2; if ( x2 > text1Length ) { // Ran off the left of the graph. k2end += 2; } else if ( y2 > text2Length ) { // Ran off the top of the graph. k2start += 2; } else if ( !front ) { k1Offset = vOffset + delta - k2; if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) { x1 = v1[ k1Offset ]; y1 = vOffset + x1 - k1Offset; // Mirror x2 onto top-left coordinate system. x2 = text1Length - x2; if ( x1 >= x2 ) { // Overlap detected. return this.diffBisectSplit( text1, text2, x1, y1, deadline ); } } } } } // Diff took too long and hit the deadline or // number of diffs equals number of characters, no commonality at all. return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ]; }; /** * Given the location of the 'middle snake', split the diff in two parts * and recurse. * @param {string} text1 Old string to be diffed. * @param {string} text2 New string to be diffed. * @param {number} x Index of split point in text1. * @param {number} y Index of split point in text2. * @param {number} deadline Time at which to bail if not yet complete. * @return {!Array.} Array of diff tuples. * @private */ DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { var text1a, text1b, text2a, text2b, diffs, diffsb; text1a = text1.substring( 0, x ); text2a = text2.substring( 0, y ); text1b = text1.substring( x ); text2b = text2.substring( y ); // Compute both diffs serially. diffs = this.DiffMain( text1a, text2a, false, deadline ); diffsb = this.DiffMain( text1b, text2b, false, deadline ); return diffs.concat( diffsb ); }; /** * Reduce the number of edits by eliminating semantically trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) { var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; changes = false; equalities = []; // Stack of indices where equalities are found. equalitiesLength = 0; // Keeping our own length var is faster in JS. /** @type {?string} */ lastequality = null; // Always equal to diffs[equalities[equalitiesLength - 1]][1] pointer = 0; // Index of current position. // Number of characters that changed prior to the equality. lengthInsertions1 = 0; lengthDeletions1 = 0; // Number of characters that changed after the equality. lengthInsertions2 = 0; lengthDeletions2 = 0; while ( pointer < diffs.length ) { if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. equalities[ equalitiesLength++ ] = pointer; lengthInsertions1 = lengthInsertions2; lengthDeletions1 = lengthDeletions2; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = diffs[ pointer ][ 1 ]; } else { // An insertion or deletion. if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) { lengthInsertions2 += diffs[ pointer ][ 1 ].length; } else { lengthDeletions2 += diffs[ pointer ][ 1 ].length; } // Eliminate an equality that is smaller or equal to the edits on both // sides of it. if ( lastequality && ( lastequality.length <= Math.max( lengthInsertions1, lengthDeletions1 ) ) && ( lastequality.length <= Math.max( lengthInsertions2, lengthDeletions2 ) ) ) { // Duplicate record. diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); // Change second copy to insert. diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; // Throw away the equality we just deleted. equalitiesLength--; // Throw away the previous equality (it needs to be reevaluated). equalitiesLength--; pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; // Reset the counters. lengthInsertions1 = 0; lengthDeletions1 = 0; lengthInsertions2 = 0; lengthDeletions2 = 0; lastequality = null; changes = true; } } pointer++; } // Normalize the diff. if ( changes ) { this.diffCleanupMerge( diffs ); } // Find any overlaps between deletions and insertions. // e.g: abcxxxxxxdef // -> abcxxxdef // e.g: xxxabcdefxxx // -> defxxxabc // Only extract an overlap if it is as big as the edit ahead or behind it. pointer = 1; while ( pointer < diffs.length ) { if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE && diffs[ pointer ][ 0 ] === DIFF_INSERT ) { deletion = diffs[ pointer - 1 ][ 1 ]; insertion = diffs[ pointer ][ 1 ]; overlapLength1 = this.diffCommonOverlap( deletion, insertion ); overlapLength2 = this.diffCommonOverlap( insertion, deletion ); if ( overlapLength1 >= overlapLength2 ) { if ( overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2 ) { // Overlap found. Insert an equality and trim the surrounding edits. diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); diffs[ pointer - 1 ][ 1 ] = deletion.substring( 0, deletion.length - overlapLength1 ); diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 ); pointer++; } } else { if ( overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2 ) { // Reverse overlap found. // Insert an equality and swap and trim the surrounding edits. diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT; diffs[ pointer - 1 ][ 1 ] = insertion.substring( 0, insertion.length - overlapLength2 ); diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE; diffs[ pointer + 1 ][ 1 ] = deletion.substring( overlapLength2 ); pointer++; } } pointer++; } pointer++; } }; /** * Determine if the suffix of one string is the prefix of another. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {number} The number of characters common to the end of the first * string and the start of the second string. * @private */ DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) { var text1Length, text2Length, textLength, best, length, pattern, found; // Cache the text lengths to prevent multiple calls. text1Length = text1.length; text2Length = text2.length; // Eliminate the null case. if ( text1Length === 0 || text2Length === 0 ) { return 0; } // Truncate the longer string. if ( text1Length > text2Length ) { text1 = text1.substring( text1Length - text2Length ); } else if ( text1Length < text2Length ) { text2 = text2.substring( 0, text1Length ); } textLength = Math.min( text1Length, text2Length ); // Quick check for the worst case. if ( text1 === text2 ) { return textLength; } // Start by looking for a single character match // and increase length until no match is found. // Performance analysis: https://neil.fraser.name/news/2010/11/04/ best = 0; length = 1; while ( true ) { pattern = text1.substring( textLength - length ); found = text2.indexOf( pattern ); if ( found === -1 ) { return best; } length += found; if ( found === 0 || text1.substring( textLength - length ) === text2.substring( 0, length ) ) { best = length; length++; } } }; /** * Split two texts into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * @param {string} text1 First string. * @param {string} text2 Second string. * @return {{chars1: string, chars2: string, lineArray: !Array.}} * An object containing the encoded text1, the encoded text2 and * the array of unique strings. * The zeroth element of the array of unique strings is intentionally blank. * @private */ DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) { var lineArray, lineHash, chars1, chars2; lineArray = []; // e.g. lineArray[4] === 'Hello\n' lineHash = {}; // e.g. lineHash['Hello\n'] === 4 // '\x00' is a valid character, but various debuggers don't like it. // So we'll insert a junk entry to avoid generating a null character. lineArray[ 0 ] = ""; /** * Split a text into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. * Modifies linearray and linehash through being a closure. * @param {string} text String to encode. * @return {string} Encoded string. * @private */ function diffLinesToCharsMunge( text ) { var chars, lineStart, lineEnd, lineArrayLength, line; chars = ""; // Walk the text, pulling out a substring for each line. // text.split('\n') would would temporarily double our memory footprint. // Modifying text would create many large strings to garbage collect. lineStart = 0; lineEnd = -1; // Keeping our own length variable is faster than looking it up. lineArrayLength = lineArray.length; while ( lineEnd < text.length - 1 ) { lineEnd = text.indexOf( "\n", lineStart ); if ( lineEnd === -1 ) { lineEnd = text.length - 1; } line = text.substring( lineStart, lineEnd + 1 ); lineStart = lineEnd + 1; if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) : ( lineHash[ line ] !== undefined ) ) { chars += String.fromCharCode( lineHash[ line ] ); } else { chars += String.fromCharCode( lineArrayLength ); lineHash[ line ] = lineArrayLength; lineArray[ lineArrayLength++ ] = line; } } return chars; } chars1 = diffLinesToCharsMunge( text1 ); chars2 = diffLinesToCharsMunge( text2 ); return { chars1: chars1, chars2: chars2, lineArray: lineArray }; }; /** * Rehydrate the text in a diff from a string of line hashes to real lines of * text. * @param {!Array.} diffs Array of diff tuples. * @param {!Array.} lineArray Array of unique strings. * @private */ DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { var x, chars, text, y; for ( x = 0; x < diffs.length; x++ ) { chars = diffs[ x ][ 1 ]; text = []; for ( y = 0; y < chars.length; y++ ) { text[ y ] = lineArray[ chars.charCodeAt( y ) ]; } diffs[ x ][ 1 ] = text.join( "" ); } }; /** * Reorder and merge like edit sections. Merge equalities. * Any edit section can move as long as it doesn't cross an equality. * @param {!Array.} diffs Array of diff tuples. */ DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) { var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. pointer = 0; countDelete = 0; countInsert = 0; textDelete = ""; textInsert = ""; commonlength; while ( pointer < diffs.length ) { switch ( diffs[ pointer ][ 0 ] ) { case DIFF_INSERT: countInsert++; textInsert += diffs[ pointer ][ 1 ]; pointer++; break; case DIFF_DELETE: countDelete++; textDelete += diffs[ pointer ][ 1 ]; pointer++; break; case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if ( countDelete + countInsert > 1 ) { if ( countDelete !== 0 && countInsert !== 0 ) { // Factor out any common prefixes. commonlength = this.diffCommonPrefix( textInsert, textDelete ); if ( commonlength !== 0 ) { if ( ( pointer - countDelete - countInsert ) > 0 && diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] === DIFF_EQUAL ) { diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] += textInsert.substring( 0, commonlength ); } else { diffs.splice( 0, 0, [ DIFF_EQUAL, textInsert.substring( 0, commonlength ) ] ); pointer++; } textInsert = textInsert.substring( commonlength ); textDelete = textDelete.substring( commonlength ); } // Factor out any common suffixies. commonlength = this.diffCommonSuffix( textInsert, textDelete ); if ( commonlength !== 0 ) { diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length - commonlength ) + diffs[ pointer ][ 1 ]; textInsert = textInsert.substring( 0, textInsert.length - commonlength ); textDelete = textDelete.substring( 0, textDelete.length - commonlength ); } } // Delete the offending records and add the merged ones. if ( countDelete === 0 ) { diffs.splice( pointer - countInsert, countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); } else if ( countInsert === 0 ) { diffs.splice( pointer - countDelete, countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); } else { diffs.splice( pointer - countDelete - countInsert, countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); } pointer = pointer - countDelete - countInsert + ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1; } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) { // Merge this equality with the previous one. diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ]; diffs.splice( pointer, 1 ); } else { pointer++; } countInsert = 0; countDelete = 0; textDelete = ""; textInsert = ""; break; } } if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) { diffs.pop(); // Remove the dummy entry at the end. } // Second pass: look for single edits surrounded on both sides by equalities // which can be shifted sideways to eliminate an equality. // e.g: ABAC -> ABAC changes = false; pointer = 1; // Intentionally ignore the first and last element (don't need checking). while ( pointer < diffs.length - 1 ) { if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL && diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) { diffPointer = diffs[ pointer ][ 1 ]; position = diffPointer.substring( diffPointer.length - diffs[ pointer - 1 ][ 1 ].length ); // This is a single edit surrounded by equalities. if ( position === diffs[ pointer - 1 ][ 1 ] ) { // Shift the edit over the previous equality. diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length - diffs[ pointer - 1 ][ 1 ].length ); diffs[ pointer + 1 ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ]; diffs.splice( pointer - 1, 1 ); changes = true; } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === diffs[ pointer + 1 ][ 1 ] ) { // Shift the edit over the next equality. diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ]; diffs[ pointer ][ 1 ] = diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) + diffs[ pointer + 1 ][ 1 ]; diffs.splice( pointer + 1, 1 ); changes = true; } } pointer++; } // If shifts were made, the diff needs reordering and another shift sweep. if ( changes ) { this.diffCleanupMerge( diffs ); } }; return function( o, n ) { var diff, output, text; diff = new DiffMatchPatch(); output = diff.DiffMain( o, n ); diff.diffCleanupEfficiency( output ); text = diff.diffPrettyHtml( output ); return text; }; }() ); qunit-1.22.0/src/dump.js000066400000000000000000000156051266310060400150330ustar00rootroot00000000000000// Based on jsDump by Ariel Flesler // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html QUnit.dump = (function() { function quote( str ) { return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; } function literal( o ) { return o + ""; } function join( pre, arr, post ) { var s = dump.separator(), base = dump.indent(), inner = dump.indent( 1 ); if ( arr.join ) { arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } return [ pre, inner + arr, base + post ].join( s ); } function array( arr, stack ) { var i = arr.length, ret = new Array( i ); if ( dump.maxDepth && dump.depth > dump.maxDepth ) { return "[object Array]"; } this.up(); while ( i-- ) { ret[ i ] = this.parse( arr[ i ], undefined, stack ); } this.down(); return join( "[", ret, "]" ); } var reName = /^function (\w+)/, dump = { // objType is used mostly internally, you can fix a (custom) type in advance parse: function( obj, objType, stack ) { stack = stack || []; var res, parser, parserType, inStack = inArray( obj, stack ); if ( inStack !== -1 ) { return "recursion(" + ( inStack - stack.length ) + ")"; } objType = objType || this.typeOf( obj ); parser = this.parsers[ objType ]; parserType = typeof parser; if ( parserType === "function" ) { stack.push( obj ); res = parser.call( this, obj, stack ); stack.pop(); return res; } return ( parserType === "string" ) ? parser : this.parsers.error; }, typeOf: function( obj ) { var type; if ( obj === null ) { type = "null"; } else if ( typeof obj === "undefined" ) { type = "undefined"; } else if ( QUnit.is( "regexp", obj ) ) { type = "regexp"; } else if ( QUnit.is( "date", obj ) ) { type = "date"; } else if ( QUnit.is( "function", obj ) ) { type = "function"; } else if ( obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined ) { type = "window"; } else if ( obj.nodeType === 9 ) { type = "document"; } else if ( obj.nodeType ) { type = "node"; } else if ( // native arrays toString.call( obj ) === "[object Array]" || // NodeList objects ( typeof obj.length === "number" && obj.item !== undefined && ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && obj[ 0 ] === undefined ) ) ) ) { type = "array"; } else if ( obj.constructor === Error.prototype.constructor ) { type = "error"; } else { type = typeof obj; } return type; }, separator: function() { return this.multiline ? this.HTML ? "
          " : "\n" : this.HTML ? " " : " "; }, // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { if ( !this.multiline ) { return ""; } var chr = this.indentChar; if ( this.HTML ) { chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } return new Array( this.depth + ( extra || 0 ) ).join( chr ); }, up: function( a ) { this.depth += a || 1; }, down: function( a ) { this.depth -= a || 1; }, setParser: function( name, parser ) { this.parsers[ name ] = parser; }, // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, // depth: 1, maxDepth: QUnit.config.maxDepth, // This is the list of parsers, to modify them, use dump.setParser parsers: { window: "[Window]", document: "[Document]", error: function( error ) { return "Error(\"" + error.message + "\")"; }, unknown: "[Unknown]", "null": "null", "undefined": "undefined", "function": function( fn ) { var ret = "function", // functions never have name in IE name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; if ( name ) { ret += " " + name; } ret += "( "; ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); return join( ret, dump.parse( fn, "functionCode" ), "}" ); }, array: array, nodelist: array, "arguments": array, object: function( map, stack ) { var keys, key, val, i, nonEnumerableProperties, ret = []; if ( dump.maxDepth && dump.depth > dump.maxDepth ) { return "[object Object]"; } dump.up(); keys = []; for ( key in map ) { keys.push( key ); } // Some properties are not always enumerable on Error objects. nonEnumerableProperties = [ "message", "name" ]; for ( i in nonEnumerableProperties ) { key = nonEnumerableProperties[ i ]; if ( key in map && inArray( key, keys ) < 0 ) { keys.push( key ); } } keys.sort(); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; val = map[ key ]; ret.push( dump.parse( key, "key" ) + ": " + dump.parse( val, undefined, stack ) ); } dump.down(); return join( "{", ret, "}" ); }, node: function( node ) { var len, i, val, open = dump.HTML ? "<" : "<", close = dump.HTML ? ">" : ">", tag = node.nodeName.toLowerCase(), ret = open + tag, attrs = node.attributes; if ( attrs ) { for ( i = 0, len = attrs.length; i < len; i++ ) { val = attrs[ i ].nodeValue; // IE6 includes all attributes in .attributes, even ones not explicitly // set. Those have values like undefined, null, 0, false, "" or // "inherit". if ( val && val !== "inherit" ) { ret += " " + attrs[ i ].nodeName + "=" + dump.parse( val, "attribute" ); } } } ret += close; // Show content of TextNode or CDATASection if ( node.nodeType === 3 || node.nodeType === 4 ) { ret += node.nodeValue; } return ret + open + "/" + tag + close; }, // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, l = fn.length; if ( !l ) { return ""; } args = new Array( l ); while ( l-- ) { // 97 is 'a' args[ l ] = String.fromCharCode( 97 + l ); } return " " + args.join( ", " ) + " "; }, // object calls it internally, the key part of an item in a map key: quote, // function calls it internally, it's the content of the function functionCode: "[code]", // node calls it internally, it's a html attribute value attribute: quote, string: quote, date: quote, regexp: literal, number: literal, "boolean": literal }, // if true, entities are escaped ( <, >, \t, space and \n ) HTML: false, // indentation unit indentChar: " ", // if true, items in a collection, are separated by a \n, else just a space. multiline: true }; return dump; }()); // back compat QUnit.jsDump = QUnit.dump; qunit-1.22.0/src/equiv.js000066400000000000000000000125341266310060400152150ustar00rootroot00000000000000// Test for equality any JavaScript type. // Author: Philippe Rathé QUnit.equiv = (function() { // Stack to decide between skip/abort functions var callers = []; // Stack to avoiding loops from circular referencing var parents = []; var parentsB = []; var getProto = Object.getPrototypeOf || function( obj ) { /*jshint proto: true */ return obj.__proto__; }; function useStrictEquality( b, a ) { // To catch short annotation VS 'new' annotation of a declaration. e.g.: // `var i = 1;` // `var j = new Number(1);` if ( typeof a === "object" ) { a = a.valueOf(); } if ( typeof b === "object" ) { b = b.valueOf(); } return a === b; } function compareConstructors( a, b ) { var protoA = getProto( a ); var protoB = getProto( b ); // Comparing constructors is more strict than using `instanceof` if ( a.constructor === b.constructor ) { return true; } // Ref #851 // If the obj prototype descends from a null constructor, treat it // as a null prototype. if ( protoA && protoA.constructor === null ) { protoA = null; } if ( protoB && protoB.constructor === null ) { protoB = null; } // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. if ( ( protoA === null && protoB === Object.prototype ) || ( protoB === null && protoA === Object.prototype ) ) { return true; } return false; } function getRegExpFlags( regexp ) { return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ]; } var callbacks = { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "symbol": useStrictEquality, "date": useStrictEquality, "nan": function() { return true; }, "regexp": function( b, a ) { return a.source === b.source && // Include flags in the comparison getRegExpFlags( a ) === getRegExpFlags( b ); }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function() { var caller = callers[ callers.length - 1 ]; return caller !== Object && typeof caller !== "undefined"; }, "array": function( b, a ) { var i, j, len, loop, aCircular, bCircular; len = a.length; if ( len !== b.length ) { // safe and faster return false; } // Track reference to avoid circular references parents.push( a ); parentsB.push( b ); for ( i = 0; i < len; i++ ) { loop = false; for ( j = 0; j < parents.length; j++ ) { aCircular = parents[ j ] === a[ i ]; bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { parents.pop(); parentsB.pop(); return false; } } } if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { parents.pop(); parentsB.pop(); return false; } } parents.pop(); parentsB.pop(); return true; }, "set": function( b, a ) { var aArray, bArray; aArray = []; a.forEach( function( v ) { aArray.push( v ); }); bArray = []; b.forEach( function( v ) { bArray.push( v ); }); return innerEquiv( bArray, aArray ); }, "map": function( b, a ) { var aArray, bArray; aArray = []; a.forEach( function( v, k ) { aArray.push( [ k, v ] ); }); bArray = []; b.forEach( function( v, k ) { bArray.push( [ k, v ] ); }); return innerEquiv( bArray, aArray ); }, "object": function( b, a ) { var i, j, loop, aCircular, bCircular; // Default to true var eq = true; var aProperties = []; var bProperties = []; if ( compareConstructors( a, b ) === false ) { return false; } // Stack constructor before traversing properties callers.push( a.constructor ); // Track reference to avoid circular references parents.push( a ); parentsB.push( b ); // Be strict: don't ensure hasOwnProperty and go deep for ( i in a ) { loop = false; for ( j = 0; j < parents.length; j++ ) { aCircular = parents[ j ] === a[ i ]; bCircular = parentsB[ j ] === b[ i ]; if ( aCircular || bCircular ) { if ( a[ i ] === b[ i ] || aCircular && bCircular ) { loop = true; } else { eq = false; break; } } } aProperties.push( i ); if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { eq = false; break; } } parents.pop(); parentsB.pop(); // Unstack, we are done callers.pop(); for ( i in b ) { // Collect b's properties bProperties.push( i ); } // Ensures identical properties name return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); } }; function typeEquiv( a, b ) { var type = QUnit.objectType( a ); return QUnit.objectType( b ) === type && callbacks[ type ]( b, a ); } // The real equiv function function innerEquiv( a, b ) { // We're done when there's nothing more to compare if ( arguments.length < 2 ) { return true; } // Require type-specific equality return ( a === b || typeEquiv( a, b ) ) && // ...across all consecutive argument pairs ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) ); } return innerEquiv; }()); qunit-1.22.0/src/export.js000066400000000000000000000025341266310060400154040ustar00rootroot00000000000000// Deprecated // Extend assert methods to QUnit for Backwards compatibility (function() { var i, assertions = Assert.prototype; function applyCurrent( current ) { return function() { var assert = new Assert( QUnit.config.current ); current.apply( assert, arguments ); }; } for ( i in assertions ) { QUnit[ i ] = applyCurrent( assertions[ i ] ); } })(); // For browser, export only select globals if ( defined.document ) { (function() { var i, l, keys = [ "test", "module", "expect", "asyncTest", "start", "stop", "ok", "notOk", "equal", "notEqual", "propEqual", "notPropEqual", "deepEqual", "notDeepEqual", "strictEqual", "notStrictEqual", "throws", "raises" ]; for ( i = 0, l = keys.length; i < l; i++ ) { window[ keys[ i ] ] = QUnit[ keys[ i ] ]; } })(); window.QUnit = QUnit; } // For nodejs if ( typeof module !== "undefined" && module && module.exports ) { module.exports = QUnit; // For consistency with CommonJS environments' exports module.exports.QUnit = QUnit; } // For CommonJS with exports, but without module.exports, like Rhino if ( typeof exports !== "undefined" && exports ) { exports.QUnit = QUnit; } if ( typeof define === "function" && define.amd ) { define( function() { return QUnit; } ); QUnit.config.autostart = false; } qunit-1.22.0/src/intro.js000066400000000000000000000003241266310060400152110ustar00rootroot00000000000000/*! * QUnit @VERSION * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: @DATE */ (function( global ) { qunit-1.22.0/src/outro.js000066400000000000000000000001511266310060400152240ustar00rootroot00000000000000// Get a reference to the global object, like window in browsers }( (function() { return this; })() )); qunit-1.22.0/src/qunit.css000066400000000000000000000124151266310060400153760ustar00rootroot00000000000000/*! * QUnit @VERSION * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: @DATE */ /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } /** Header */ #qunit-header { padding: 0.5em 0 0.5em 1em; color: #8699A4; background-color: #0D3349; font-size: 1.5em; line-height: 1em; font-weight: 400; border-radius: 5px 5px 0 0; } #qunit-header a { text-decoration: none; color: #C2CCD1; } #qunit-header a:hover, #qunit-header a:focus { color: #FFF; } #qunit-testrunner-toolbar label { display: inline-block; padding: 0 0.5em 0 0.1em; } #qunit-banner { height: 5px; } #qunit-testrunner-toolbar { padding: 0.5em 1em 0.5em 1em; color: #5E740B; background-color: #EEE; overflow: hidden; } #qunit-filteredTest { padding: 0.5em 1em 0.5em 1em; background-color: #F4FF77; color: #366097; } #qunit-userAgent { padding: 0.5em 1em 0.5em 1em; background-color: #2B81AF; color: #FFF; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } #qunit-modulefilter-container { float: right; padding: 0.2em; } .qunit-url-config { display: inline-block; padding: 0.1em; } .qunit-filter { display: block; float: right; margin-left: 1em; } /** Tests: Pass/Fail */ #qunit-tests { list-style-position: inside; } #qunit-tests li { padding: 0.4em 1em 0.4em 1em; border-bottom: 1px solid #FFF; list-style-position: inside; } #qunit-tests > li { display: none; } #qunit-tests li.running, #qunit-tests li.pass, #qunit-tests li.fail, #qunit-tests li.skipped { display: list-item; } #qunit-tests.hidepass { position: relative; } #qunit-tests.hidepass li.running, #qunit-tests.hidepass li.pass { visibility: hidden; position: absolute; width: 0; height: 0; padding: 0; border: 0; margin: 0; } #qunit-tests li strong { cursor: pointer; } #qunit-tests li.skipped strong { cursor: default; } #qunit-tests li a { padding: 0.5em; color: #C2CCD1; text-decoration: none; } #qunit-tests li p a { padding: 0.25em; color: #6B6464; } #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } #qunit-tests li .runtime { float: right; font-size: smaller; } .qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #FFF; border-radius: 5px; } .qunit-source { margin: 0.6em 0 0.3em; } .qunit-collapsed { display: none; } #qunit-tests table { border-collapse: collapse; margin-top: 0.2em; } #qunit-tests th { text-align: right; vertical-align: top; padding: 0 0.5em 0 0; } #qunit-tests td { vertical-align: top; } #qunit-tests pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } #qunit-tests del { background-color: #E0F2BE; color: #374E0C; text-decoration: none; } #qunit-tests ins { background-color: #FFCACA; color: #500; text-decoration: none; } /*** Test Counts */ #qunit-tests b.counts { color: #000; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; background-color: #FFF; border-bottom: none; list-style-position: inside; } /*** Passing Styles */ #qunit-tests li li.pass { color: #3C510C; background-color: #FFF; border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999; } #qunit-banner.qunit-pass { background-color: #C6E746; } /*** Failing Styles */ #qunit-tests li li.fail { color: #710909; background-color: #FFF; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; } #qunit-tests .fail { color: #000; background-color: #EE5757; } #qunit-tests .fail .test-name, #qunit-tests .fail .module-name { color: #000; } #qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-expected { color: #008000; } #qunit-banner.qunit-fail { background-color: #EE5757; } /*** Skipped tests */ #qunit-tests .skipped { background-color: #EBECE9; } #qunit-tests .qunit-skipped-label { background-color: #F4FF77; display: inline-block; font-style: normal; color: #366097; line-height: 1.8em; padding: 0 0.5em; margin: -0.4em 0.4em -0.4em 0; } /** Result */ #qunit-testresult { padding: 0.5em 1em 0.5em 1em; color: #2B81AF; background-color: #D2E0E6; border-bottom: 1px solid #FFF; } #qunit-testresult .module-name { font-weight: 700; } /** Fixture */ #qunit-fixture { position: absolute; top: -10000px; left: -10000px; width: 1000px; height: 1000px; } qunit-1.22.0/src/test.js000066400000000000000000000333671266310060400150520ustar00rootroot00000000000000var focused = false; var priorityCount = 0; function Test( settings ) { var i, l; ++Test.count; extend( this, settings ); this.assertions = []; this.semaphore = 0; this.usedAsync = false; this.module = config.currentModule; this.stack = sourceFromStacktrace( 3 ); // Register unique strings for ( i = 0, l = this.module.tests; i < l.length; i++ ) { if ( this.module.tests[ i ].name === this.testName ) { this.testName += " "; } } this.testId = generateHash( this.module.name, this.testName ); this.module.tests.push({ name: this.testName, testId: this.testId }); if ( settings.skip ) { // Skipped tests will fully ignore any sent callback this.callback = function() {}; this.async = false; this.expected = 0; } else { this.assert = new Assert( this ); } } Test.count = 0; Test.prototype = { before: function() { if ( // Emit moduleStart when we're switching from one module to another this.module !== config.previousModule || // They could be equal (both undefined) but if the previousModule property doesn't // yet exist it means this is the first test in a suite that isn't wrapped in a // module, in which case we'll just emit a moduleStart event for 'undefined'. // Without this, reporters can get testStart before moduleStart which is a problem. !hasOwn.call( config, "previousModule" ) ) { if ( hasOwn.call( config, "previousModule" ) ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started }); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0, started: now() }; runLoggingCallbacks( "moduleStart", { name: this.module.name, tests: this.module.tests }); } config.current = this; if ( this.module.testEnvironment ) { delete this.module.testEnvironment.beforeEach; delete this.module.testEnvironment.afterEach; } this.testEnvironment = extend( {}, this.module.testEnvironment ); this.started = now(); runLoggingCallbacks( "testStart", { name: this.testName, module: this.module.name, testId: this.testId }); if ( !config.pollution ) { saveGlobal(); } }, run: function() { var promise; config.current = this; if ( this.async ) { QUnit.stop(); } this.callbackStarted = now(); if ( config.notrycatch ) { runTest( this ); return; } try { runTest( this ); } catch ( e ) { this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { QUnit.start(); } } function runTest( test ) { promise = test.callback.call( test.testEnvironment, test.assert ); test.resolvePromise( promise ); } }, after: function() { checkPollution(); }, queueHook: function( hook, hookName ) { var promise, test = this; return function runHook() { config.current = test; if ( config.notrycatch ) { callHook(); return; } try { callHook(); } catch ( error ) { test.pushFailure( hookName + " failed on " + test.testName + ": " + ( error.message || error ), extractStacktrace( error, 0 ) ); } function callHook() { promise = hook.call( test.testEnvironment, test.assert ); test.resolvePromise( promise, hookName ); } }; }, // Currently only used for module level hooks, can be used to add global level ones hooks: function( handler ) { var hooks = []; function processHooks( test, module ) { if ( module.parentModule ) { processHooks( test, module.parentModule ); } if ( module.testEnvironment && QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); } } // Hooks are ignored on skipped tests if ( !this.skip ) { processHooks( this, this.module ); } return hooks; }, finish: function() { config.current = this; if ( config.requireExpects && this.expected === null ) { this.pushFailure( "Expected number of assertions to be defined, but expect() was " + "not called.", this.stack ); } else if ( this.expected !== null && this.expected !== this.assertions.length ) { this.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); } else if ( this.expected === null && !this.assertions.length ) { this.pushFailure( "Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack ); } var i, bad = 0; this.runtime = now() - this.started; config.stats.all += this.assertions.length; config.moduleStats.all += this.assertions.length; for ( i = 0; i < this.assertions.length; i++ ) { if ( !this.assertions[ i ].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } runLoggingCallbacks( "testDone", { name: this.testName, module: this.module.name, skipped: !!this.skip, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, runtime: this.runtime, // HTML Reporter use assertions: this.assertions, testId: this.testId, // Source of Test source: this.stack, // DEPRECATED: this property will be removed in 2.0.0, use runtime instead duration: this.runtime }); // QUnit.reset() is deprecated and will be replaced for a new // fixture reset function on QUnit 2.0/2.1. // It's still called here for backwards compatibility handling QUnit.reset(); config.current = undefined; }, queue: function() { var priority, test = this; if ( !this.valid() ) { return; } function run() { // each of these can by async synchronize([ function() { test.before(); }, test.hooks( "beforeEach" ), function() { test.run(); }, test.hooks( "afterEach" ).reverse(), function() { test.after(); }, function() { test.finish(); } ]); } // Prioritize previously failed tests, detected from sessionStorage priority = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); return synchronize( run, priority ); }, pushResult: function( resultInfo ) { // resultInfo = { result, actual, expected, message, negative } var source, details = { module: this.module.name, name: this.testName, result: resultInfo.result, message: resultInfo.message, actual: resultInfo.actual, expected: resultInfo.expected, testId: this.testId, negative: resultInfo.negative || false, runtime: now() - this.started }; if ( !resultInfo.result ) { source = sourceFromStacktrace(); if ( source ) { details.source = source; } } runLoggingCallbacks( "log", details ); this.assertions.push({ result: !!resultInfo.result, message: resultInfo.message }); }, pushFailure: function( message, source, actual ) { if ( !( this instanceof Test ) ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) ); } var details = { module: this.module.name, name: this.testName, result: false, message: message || "error", actual: actual || null, testId: this.testId, runtime: now() - this.started }; if ( source ) { details.source = source; } runLoggingCallbacks( "log", details ); this.assertions.push({ result: false, message: message }); }, resolvePromise: function( promise, phase ) { var then, message, test = this; if ( promise != null ) { then = promise.then; if ( QUnit.objectType( then ) === "function" ) { QUnit.stop(); then.call( promise, function() { QUnit.start(); }, function( error ) { message = "Promise rejected " + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + " " + test.testName + ": " + ( error.message || error ); test.pushFailure( message, extractStacktrace( error, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Unblock QUnit.start(); } ); } } }, valid: function() { var filter = config.filter, regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), fullName = ( this.module.name + ": " + this.testName ); function testInModuleChain( testModule ) { var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; if ( testModuleName === module ) { return true; } else if ( testModule.parentModule ) { return testInModuleChain( testModule.parentModule ); } else { return false; } } // Internally-generated tests are always valid if ( this.callback && this.callback.validTest ) { return true; } if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { return false; } if ( module && !testInModuleChain( this.module ) ) { return false; } if ( !filter ) { return true; } return regexFilter ? this.regexFilter( !!regexFilter[1], regexFilter[2], regexFilter[3], fullName ) : this.stringFilter( filter, fullName ); }, regexFilter: function( exclude, pattern, flags, fullName ) { var regex = new RegExp( pattern, flags ); var match = regex.test( fullName ); return match !== exclude; }, stringFilter: function( filter, fullName ) { filter = filter.toLowerCase(); fullName = fullName.toLowerCase(); var include = filter.charAt( 0 ) !== "!"; if ( !include ) { filter = filter.slice( 1 ); } // If the filter matches, we need to honour include if ( fullName.indexOf( filter ) !== -1 ) { return include; } // Otherwise, do the opposite return !include; } }; // Resets the test setup. Useful for tests that modify the DOM. /* DEPRECATED: Use multiple tests instead of resetting inside a test. Use testStart or testDone for custom cleanup. This method will throw an error in 2.0, and will be removed in 2.1 */ QUnit.reset = function() { // Return on non-browser environments // This is necessary to not break on node tests if ( !defined.document ) { return; } var fixture = defined.document && document.getElementById && document.getElementById( "qunit-fixture" ); if ( fixture ) { fixture.innerHTML = config.fixture; } }; QUnit.pushFailure = function() { if ( !QUnit.config.current ) { throw new Error( "pushFailure() assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } // Gets current test obj var currentTest = QUnit.config.current; return currentTest.pushFailure.apply( currentTest, arguments ); }; // Based on Java's String.hashCode, a simple but not // rigorously collision resistant hashing function function generateHash( module, testName ) { var hex, i = 0, hash = 0, str = module + "\x1C" + testName, len = str.length; for ( ; i < len; i++ ) { hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); hash |= 0; } // Convert the possibly negative integer hash code into an 8 character hex string, which isn't // strictly necessary but increases user understanding that the id is a SHA-like hash hex = ( 0x100000000 + hash ).toString( 16 ); if ( hex.length < 8 ) { hex = "0000000" + hex; } return hex.slice( -8 ); } function synchronize( callback, priority ) { var last = !priority; if ( QUnit.objectType( callback ) === "array" ) { while ( callback.length ) { synchronize( callback.shift() ); } return; } if ( priority ) { config.queue.splice( priorityCount++, 0, callback ); } else { config.queue.push( callback ); } if ( config.autorun && !config.blocking ) { process( last ); } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in global ) { if ( hasOwn.call( global, key ) ) { // in Opera sometimes DOM element ids show up here, ignore them if ( /^qunit-test-output/.test( key ) ) { continue; } config.pollution.push( key ); } } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); } } // Will be exposed as QUnit.asyncTest function asyncTest( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } QUnit.test( testName, expected, callback, true ); } // Will be exposed as QUnit.test function test( testName, expected, callback, async ) { if ( focused ) { return; } var newTest; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test({ testName: testName, expected: expected, async: async, callback: callback }); newTest.queue(); } // Will be exposed as QUnit.skip function skip( testName ) { if ( focused ) { return; } var test = new Test({ testName: testName, skip: true }); test.queue(); } // Will be exposed as QUnit.only function only( testName, expected, callback, async ) { var newTest; if ( focused ) { return; } QUnit.config.queue.length = 0; focused = true; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test({ testName: testName, expected: expected, async: async, callback: callback }); newTest.queue(); } qunit-1.22.0/test/000077500000000000000000000000001266310060400137115ustar00rootroot00000000000000qunit-1.22.0/test/amd.html000066400000000000000000000010041266310060400153330ustar00rootroot00000000000000 QUnit AMD Test Suite
          qunit-1.22.0/test/amd.js000066400000000000000000000004711266310060400150120ustar00rootroot00000000000000/* global beginData */ define( [ "qunit" ], function( QUnit ) { return function() { QUnit.module( "AMD autostart" ); QUnit.test( "Prove the test run started as expected", function( assert ) { assert.expect( 1 ); assert.strictEqual( beginData.totalTests, 1, "Should have expected 1 test" ); } ); }; } ); qunit-1.22.0/test/autostart.html000066400000000000000000000016431266310060400166310ustar00rootroot00000000000000 QUnit Autostart Test Suite
          qunit-1.22.0/test/autostart.js000066400000000000000000000004651266310060400163020ustar00rootroot00000000000000/*global times, beginData */ QUnit.start(); QUnit.module( "autostart" ); QUnit.test( "Prove the test run started as expected", function( assert ) { assert.expect( 2 ); assert.ok( times.autostartOff <= times.runStarted ); assert.strictEqual( beginData.totalTests, 1, "Should have expected 1 test" ); }); qunit-1.22.0/test/globals-node.js000066400000000000000000000011471266310060400166200ustar00rootroot00000000000000/*jshint node:true */ // Don't execute this file directly on Node, this is part of // Grunt's test-on-node task (function() { QUnit.module( "globals for Node.js only" ); QUnit.test( "QUnit exports", function( assert ) { var qunit = require( "../dist/qunit" ); assert.ok( qunit, "Required module QUnit truthy" ); assert.strictEqual( qunit, QUnit, "Required module QUnit matches global QUnit" ); assert.ok( qunit.hasOwnProperty( "QUnit" ), "Required module QUnit has property QUnit" ); assert.strictEqual( qunit.QUnit, qunit, "Required module QUnit's property QUnit is self-referencing" ); }); })(); qunit-1.22.0/test/headless.html000066400000000000000000000012531266310060400163700ustar00rootroot00000000000000 QUnit Headless Test Suite
          test markup
          qunit-1.22.0/test/index.html000066400000000000000000000014201266310060400157030ustar00rootroot00000000000000 QUnit Main Test Suite
          test markup
          qunit-1.22.0/test/logs.html000066400000000000000000000005571266310060400155520ustar00rootroot00000000000000 QUnit Logs Test Suite
          test markup
          qunit-1.22.0/test/logs.js000066400000000000000000000202301266310060400152100ustar00rootroot00000000000000var totalTests, moduleContext, moduleDoneContext, testContext, testDoneContext, logContext, testAutorun, beginModules, module1Test1, module1Test2, module2Test1, module2Test2, module2Test3, module2Test4, begin = 0, moduleStart = 0, moduleDone = 0, testStart = 0, testDone = 0, log = 0, module1Context = { name: "logs1", tests: [ (module1Test1 = { "name": "test1", "testId": "646e9e25" }), (module1Test2 = { "name": "test2", "testId": "646e9e26" }) ] }, module2Context = { name: "logs2", tests: [ (module2Test1 = { "name": "test1", "testId": "9954d966" }), (module2Test2 = { "name": "test2", "testId": "9954d967" }), (module2Test3 = { "name": "a skipped test", "testId": "3e797d3a" }), (module2Test4 = { "name": "test the log for the skipped test", "testId": "d3266148" }) ] }; QUnit.begin(function( args ) { totalTests = args.totalTests; beginModules = args.modules; begin++; }); QUnit.moduleStart(function( context ) { moduleStart++; moduleContext = context; }); QUnit.moduleDone(function( context ) { moduleDone++; moduleDoneContext = context; }); QUnit.testStart(function( context ) { testStart++; testContext = context; }); QUnit.testDone(function( context ) { testDone++; testDoneContext = context; }); QUnit.log(function( context ) { log++; logContext = context; }); QUnit.module( module1Context.name ); QUnit.test( module1Test1.name, function( assert ) { assert.expect( 18 ); assert.equal( typeof totalTests, "number", "QUnit.begin should pass total amount of tests to callback" ); while ( beginModules.length > 2 ) { beginModules.pop(); } assert.deepEqual( beginModules, [ module1Context, module2Context ], "QUnit.begin details registered modules and their respective tests" ); assert.equal( begin, 1, "QUnit.begin calls" ); assert.equal( moduleStart, 1, "QUnit.moduleStart calls" ); assert.equal( testStart, 1, "QUnit.testStart calls" ); assert.equal( testDone, 0, "QUnit.testDone calls" ); assert.equal( moduleDone, 0, "QUnit.moduleDone calls" ); assert.equal( logContext.runtime >= 0 && logContext.runtime < 50, true, "log runtime was a reasonable number" ); delete logContext.runtime; assert.deepEqual( logContext, { name: module1Test1.name, module: module1Context.name, result: true, message: "log runtime was a reasonable number", actual: true, expected: true, negative: false, testId: module1Test1.testId }, "log context after equal(actual, expected, message)" ); assert.equal( "foo", "foo" ); delete logContext.runtime; assert.deepEqual( logContext, { name: module1Test1.name, module: module1Context.name, result: true, message: undefined, actual: "foo", expected: "foo", negative: false, testId: module1Test1.testId }, "log context after equal(actual, expected)" ); assert.ok( true, "ok(true, message)" ); delete logContext.runtime; assert.deepEqual( logContext, { module: module1Context.name, name: module1Test1.name, result: true, message: "ok(true, message)", actual: true, expected: true, negative: false, testId: module1Test1.testId }, "log context after ok(true, message)" ); assert.strictEqual( testDoneContext, undefined, "testDone context" ); assert.deepEqual( testContext, { module: module1Context.name, name: module1Test1.name, testId: module1Test1.testId }, "test context" ); assert.strictEqual( moduleDoneContext, undefined, "moduleDone context" ); assert.deepEqual( moduleContext, module1Context, "module context" ); assert.equal( log, 17, "QUnit.log calls" ); }); QUnit.test( module1Test2.name, function( assert ) { assert.expect( 12 ); assert.equal( begin, 1, "QUnit.begin calls" ); assert.equal( moduleStart, 1, "QUnit.moduleStart calls" ); assert.equal( testStart, 2, "QUnit.testStart calls" ); assert.equal( testDone, 1, "QUnit.testDone calls" ); assert.equal( moduleDone, 0, "QUnit.moduleDone calls" ); assert.equal( testDoneContext.runtime >= 0 && testDoneContext.runtime < 1000, true, "test runtime was a reasonable number" ); assert.ok( testDoneContext.assertions instanceof Array, "testDone context: assertions" ); // TODO: more tests for testDoneContext.assertions delete testDoneContext.runtime; // DEPRECATED: remove this delete when removing the duration property delete testDoneContext.duration; // Delete testDoneContext.assertions so we can easily jump to next assertion delete testDoneContext.assertions; // Delete testDoneContext.source delete testDoneContext.source; assert.deepEqual( testDoneContext, { module: module1Context.name, name: module1Test1.name, failed: 0, passed: 18, total: 18, testId: module1Test1.testId, skipped: false }, "testDone context" ); assert.deepEqual( testContext, { module: module1Context.name, name: module1Test2.name, testId: module1Test2.testId }, "test context" ); assert.strictEqual( moduleDoneContext, undefined, "moduleDone context" ); assert.deepEqual( moduleContext, module1Context, "module context" ); assert.equal( log, 29, "QUnit.log calls" ); }); QUnit.module( module2Context.name ); QUnit.test( module2Test1.name, function( assert ) { assert.expect( 10 ); assert.equal( begin, 1, "QUnit.begin calls" ); assert.equal( moduleStart, 2, "QUnit.moduleStart calls" ); assert.equal( testStart, 3, "QUnit.testStart calls" ); assert.equal( testDone, 2, "QUnit.testDone calls" ); assert.equal( moduleDone, 1, "QUnit.moduleDone calls" ); assert.deepEqual( testContext, { module: module2Context.name, name: module2Test1.name, testId: module2Test1.testId }, "test context" ); assert.equal( moduleDoneContext.runtime >= 0 && moduleDoneContext.runtime < 5000, true, "module runtime was a reasonable number" ); delete moduleDoneContext.runtime; assert.deepEqual( moduleDoneContext, { name: module1Context.name, tests: module1Context.tests, failed: 0, passed: 30, total: 30 }, "moduleDone context" ); assert.deepEqual( moduleContext, module2Context, "module context" ); assert.equal( log, 39, "QUnit.log calls" ); }); QUnit.test( module2Test2.name, function( assert ) { assert.expect( 8 ); assert.equal( begin, 1, "QUnit.begin calls" ); assert.equal( moduleStart, 2, "QUnit.moduleStart calls" ); assert.equal( testStart, 4, "QUnit.testStart calls" ); assert.equal( testDone, 3, "QUnit.testDone calls" ); assert.equal( moduleDone, 1, "QUnit.moduleDone calls" ); assert.deepEqual( testContext, { module: module2Context.name, name: module2Test2.name, testId: module2Test2.testId }, "test context" ); assert.deepEqual( moduleContext, module2Context, "module context" ); assert.equal( log, 47, "QUnit.log calls" ); }); QUnit.skip( module2Test3.name ); QUnit.test( module2Test4.name, function( assert ) { assert.expect( 1 ); delete testDoneContext.runtime; delete testDoneContext.duration; delete testDoneContext.source; assert.deepEqual( testDoneContext, { assertions: [], module: module2Context.name, name: module2Test3.name, failed: 0, passed: 0, total: 0, skipped: true, testId: module2Test3.testId }, "testDone context" ); }); testAutorun = true; QUnit.done(function() { if ( !testAutorun ) { return; } testAutorun = false; moduleStart = moduleDone = 0; // Since these tests run *after* done, and as such // QUnit is not able to know whether more tests are coming // the module starts/ends after each test. QUnit.module( "autorun" ); setTimeout(function() { QUnit.test( "first", function( assert ) { assert.equal( moduleStart, 1, "test started" ); assert.equal( moduleDone, 0, "test in progress" ); }); QUnit.test( "second", function( assert ) { assert.equal( moduleStart, 2, "test started" ); assert.equal( moduleDone, 1, "test in progress" ); }); }, 5000 ); }); QUnit.module( "deprecated log methods" ); QUnit.test( "QUnit.reset()", function( assert ) { // Skip non-browsers if ( typeof window === "undefined" || !window.document ) { assert.expect( 0 ); return; } var myFixture = document.getElementById( "qunit-fixture" ); myFixture.innerHTML = "something different from QUnit.config.fixture"; QUnit.reset(); assert.strictEqual( myFixture.innerHTML, QUnit.config.fixture, "restores #qunit-fixture" ); }); qunit-1.22.0/test/main/000077500000000000000000000000001266310060400146355ustar00rootroot00000000000000qunit-1.22.0/test/main/assert.js000066400000000000000000000177651266310060400165140ustar00rootroot00000000000000QUnit.module( "assert" ); QUnit.test( "ok", function( assert ) { assert.ok( true ); assert.ok( 1 ); assert.ok( "1" ); assert.ok( Infinity ); assert.ok( {} ); assert.ok( [] ); }); QUnit.test( "notOk", function( assert ) { assert.notOk( false ); assert.notOk( 0 ); assert.notOk( "" ); assert.notOk( null ); assert.notOk( undefined ); assert.notOk( NaN ); }); QUnit.test( "equal", function( assert ) { assert.equal( 1, 1 ); assert.equal( "foo", "foo" ); assert.equal( "foo", [ "foo" ] ); assert.equal( "foo", { toString: function() { return "foo"; } } ); assert.equal( 0, [ 0 ] ); }); QUnit.test( "notEqual", function( assert ) { assert.notEqual( 1, 2 ); assert.notEqual( "foo", "bar" ); assert.notEqual( {}, {} ); assert.notEqual( [], [] ); }); QUnit.test( "strictEqual", function( assert ) { assert.strictEqual( 1, 1 ); assert.strictEqual( "foo", "foo" ); }); QUnit.test( "notStrictEqual", function( assert ) { assert.notStrictEqual( 1, 2 ); assert.notStrictEqual( "foo", "bar" ); assert.notStrictEqual( "foo", [ "foo" ] ); assert.notStrictEqual( "1", 1 ); assert.notStrictEqual( "foo", { toString: function() { return "foo"; } } ); }); QUnit.test( "propEqual", function( assert ) { assert.expect( 5 ); var objectCreate = Object.create || function( origin ) { function O() {} O.prototype = origin; var r = new O(); return r; }; function Foo( x, y, z ) { this.x = x; this.y = y; this.z = z; } Foo.prototype.doA = function() {}; Foo.prototype.doB = function() {}; Foo.prototype.bar = "prototype"; function Bar() { } Bar.prototype = objectCreate( Foo.prototype ); Bar.prototype.constructor = Bar; assert.propEqual( new Foo( 1, "2", [] ), { x: 1, y: "2", z: [] } ); assert.notPropEqual( new Foo( "1", 2, 3 ), { x: 1, y: "2", z: 3 }, "Primitive values are strictly compared" ); assert.notPropEqual( new Foo( 1, "2", [] ), { x: 1, y: "2", z: {} }, "Array type is preserved" ); assert.notPropEqual( new Foo( 1, "2", {} ), { x: 1, y: "2", z: [] }, "Empty array is not the same as empty object" ); assert.propEqual( new Foo( 1, "2", new Foo( [ 3 ], new Bar(), null ) ), { x: 1, y: "2", z: { x: [ 3 ], y: {}, z: null } }, "Complex nesting of different types, inheritance and constructors" ); }); QUnit.test( "throws", function( assert ) { assert.expect( 16 ); function CustomError( message ) { this.message = message; } CustomError.prototype.toString = function() { return this.message; }; assert.throws( function() { throw "my error"; } ); assert.throws( function() { throw "my error"; }, "simple string throw, no 'expected' value given" ); // This test is for IE 7 and prior which does not properly // implement Error.prototype.toString assert.throws( function() { throw new Error( "error message" ); }, /error message/, "use regexp against instance of Error" ); assert.throws( function() { throw new TypeError(); }, Error, "thrown TypeError without a message is an instance of Error" ); assert.throws( function() { throw new TypeError(); }, TypeError, "thrown TypeError without a message is an instance of TypeError" ); assert.throws( function() { throw new TypeError( "error message" ); }, Error, "thrown TypeError with a message is an instance of Error" ); // This test is for IE 8 and prior which goes against the standards // by considering that the native Error constructors, such TypeError, // are also instances of the Error constructor. As such, the assertion // sometimes went down the wrong path. assert.throws( function() { throw new TypeError( "error message" ); }, TypeError, "thrown TypeError with a message is an instance of TypeError" ); assert.throws( function() { throw new CustomError( "some error description" ); }, CustomError, "thrown error is an instance of CustomError" ); assert.throws( function() { throw new Error( "some error description" ); }, /description/, "use a regex to match against the stringified error" ); assert.throws( function() { throw new Error( "foo" ); }, new Error( "foo" ), "thrown error object is similar to the expected Error object" ); assert.throws( function() { throw new CustomError( "some error description" ); }, new CustomError( "some error description" ), "thrown error object is similar to the expected CustomError object" ); assert.throws( function() { throw { name: "SomeName", message: "some message" }; }, { name: "SomeName", message: "some message" }, "thrown error object is similar to the expected plain object" ); assert.throws( function() { throw new CustomError( "some error description" ); }, function( err ) { return err instanceof CustomError && /description/.test( err ); }, "custom validation function" ); assert.throws( function() { /*jshint ignore:start */ ( window.execScript || function( data ) { window.eval.call( window, data ); })( "throw 'error';" ); /*jshint ignore:end */ }, "globally-executed errors caught" ); this.CustomError = CustomError; assert.throws( function() { throw new this.CustomError( "some error description" ); }, /description/, "throw error from property of 'this' context" ); assert.throws( function() { throw "some error description"; }, "some error description", "handle string typed thrown errors" ); }); QUnit.test( "raises, alias for throws", function( assert ) { assert.expect( 1 ); assert.raises(function() { throw "my error"; }); }); QUnit.module( "failing assertions", { beforeEach: function( assert ) { var originalPushResult = assert.pushResult; assert.pushResult = function( resultInfo ) { // inverts the result so we can test failing assertions resultInfo.result = !resultInfo.result; originalPushResult( resultInfo ); }; } }); QUnit.test( "ok", function( assert ) { assert.ok( false ); assert.ok( 0 ); assert.ok( "" ); assert.ok( null ); assert.ok( undefined ); assert.ok( NaN ); }); QUnit.test( "notOk", function( assert ) { assert.notOk( true ); assert.notOk( 1 ); assert.notOk( "1" ); assert.notOk( Infinity ); assert.notOk( {} ); assert.notOk( [] ); }); QUnit.test( "equal", function( assert ) { assert.equal( 1, 2 ); assert.equal( "foo", "bar" ); assert.equal( {}, {} ); assert.equal( [], [] ); }); QUnit.test( "notEqual", function( assert ) { assert.notEqual( 1, 1 ); assert.notEqual( "foo", "foo" ); assert.notEqual( "foo", [ "foo" ] ); assert.notEqual( "foo", { toString: function() { return "foo"; } } ); assert.notEqual( 0, [ 0 ] ); }); QUnit.test( "strictEqual", function( assert ) { assert.strictEqual( 1, 2 ); assert.strictEqual( "foo", "bar" ); assert.strictEqual( "foo", [ "foo" ] ); assert.strictEqual( "1", 1 ); assert.strictEqual( "foo", { toString: function() { return "foo"; } } ); }); QUnit.test( "notStrictEqual", function( assert ) { assert.notStrictEqual( 1, 1 ); assert.notStrictEqual( "foo", "foo" ); }); QUnit.test( "deepEqual", function( assert ) { assert.deepEqual( [ "foo", "bar" ], [ "foo" ] ); }); QUnit.test( "notDeepEqual", function( assert ) { assert.notDeepEqual( [ "foo", "bar" ], [ "foo", "bar" ] ); }); QUnit.test( "propEqual", function( assert ) { function Foo( x, y, z ) { this.x = x; this.y = y; this.z = z; } Foo.prototype.baz = function() {}; Foo.prototype.bar = "prototype"; assert.propEqual( new Foo( "1", 2, 3 ), { x: 1, y: "2", z: 3 } ); }); QUnit.test( "notPropEqual", function( assert ) { function Foo( x, y, z ) { this.x = x; this.y = y; this.z = z; } Foo.prototype.baz = function() {}; Foo.prototype.bar = "prototype"; assert.notPropEqual( new Foo( 1, "2", [] ), { x: 1, y: "2", z: [] } ); }); QUnit.test( "throws", function( assert ) { assert.throws( function() { return; }, "throws fails without a thrown error" ); assert.throws( function() { throw "foo"; }, /bar/, "throws fail when regexp doesn't match the error message" ); }); qunit-1.22.0/test/main/async.js000066400000000000000000000325441266310060400163200ustar00rootroot00000000000000var globalStartError, globalStopError; function _setupForFailingAssertionsAfterAsyncDone( assert ) { var errorRegex = new RegExp( "Assertion after the final `assert\\.async` " + "was resolved" ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { // Increment the semaphore, preventing post-`done` assertions from causing another failure assert.test.semaphore++; throw new Error( msg ); }; // Provide a wrapper for `assert.throws` to allow test to pass this post-`done` assertion this._assertCatch = function( fn ) { assert.throws.call( assert, fn, errorRegex ); // Decrement the semaphore to undo the effects of the duck-punched `test.pushFailure` above assert.test.semaphore--; }; } QUnit.begin(function() { try { QUnit.start(); } catch ( thrownError ) { globalStartError = thrownError.message; } }); try { QUnit.stop(); } catch ( thrownError ) { globalStopError = thrownError.message; } QUnit.module( "global start/stop errors" ); QUnit.test( "Call start() when already started", function( assert ) { assert.expect( 1 ); assert.equal( globalStartError, "Called start() outside of a test context while already " + "started" ); }); QUnit.test( "Call stop() outside of test context", function( assert ) { assert.expect( 1 ); assert.equal( globalStopError, "Called stop() outside of a test context" ); }); QUnit.module( "start/stop" ); QUnit.test( "parallel calls", function( assert ) { assert.expect( 2 ); QUnit.stop(); setTimeout(function() { assert.ok( true ); QUnit.start(); }); QUnit.stop(); setTimeout(function() { assert.ok( true ); QUnit.start(); }); }); QUnit.test( "waterfall calls", function( assert ) { assert.expect( 2 ); QUnit.stop(); setTimeout(function() { assert.ok( true, "first" ); QUnit.start(); QUnit.stop(); setTimeout(function() { assert.ok( true, "second" ); QUnit.start(); }); }); }); QUnit.test( "fails if start is called more than stop", function( assert ) { assert.expect( 1 ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; assert.throws(function() { QUnit.start(); }, new RegExp( "Called start\\(\\) while already started \\(test's semaphore was 0 " + "already\\)" ) ); }); QUnit.test( "fails if start is called with a non-numeric argument", function( assert ) { QUnit.stop(); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; assert.throws(function() { QUnit.start( "ok" ); }, /Called start\(\) with a non\-numeric decrement\./ ); }); QUnit.module( "asyncTest" ); QUnit.asyncTest( "asyncTest", function( assert ) { assert.expect( 1 ); setTimeout(function() { assert.ok( true ); QUnit.start(); }); }); QUnit.module( "assert.async" ); QUnit.test( "single call", function( assert ) { var done = assert.async(); assert.expect( 1 ); setTimeout(function() { assert.ok( true ); done(); }); }); QUnit.test( "multiple call", function( assert ) { var done = assert.async( 4 ); assert.expect( 4 ); setTimeout(function() { assert.ok( true ); done(); }); setTimeout(function() { assert.ok( true ); done(); }); setTimeout(function() { assert.ok( true ); done(); }); setTimeout(function() { assert.ok( true ); done(); }); }); QUnit.test( "parallel calls", function( assert ) { var done1 = assert.async(), done2 = assert.async(); assert.expect( 2 ); setTimeout(function() { assert.ok( true ); done1(); }); setTimeout(function() { assert.ok( true ); done2(); }); }); QUnit.test( "waterfall calls", function( assert ) { var done2, done1 = assert.async(); assert.expect( 2 ); setTimeout(function() { assert.ok( true, "first" ); done1(); done2 = assert.async(); setTimeout(function() { assert.ok( true, "second" ); done2(); }); }); }); QUnit.test( "fails if callback is called more than once in test", function( assert ) { // Having an outer async flow in this test avoids the need to manually modify QUnit internals // in order to avoid post-`done` assertions causing additional failures var done = assert.async(); assert.expect( 1 ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; var overDone = assert.async(); overDone(); assert.throws(function() { overDone(); }, new RegExp( "Too many calls to the `assert.async` callback" ) ); done(); }); QUnit.test( "fails if callback is called more than callback call count", function( assert ) { // Having an outer async flow in this test avoids the need to manually modify QUnit internals // in order to avoid post-`done` assertions causing additional failures var done = assert.async(); assert.expect( 1 ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; var overDone = assert.async( 3 ); overDone(); overDone(); overDone(); assert.throws(function() { overDone(); }, new RegExp( "Too many calls to the `assert.async` callback" ) ); done(); }); QUnit.module( "assert.async fails if callback is called more than once in", { beforeEach: function( assert ) { // Having an outer async flow in this test avoids the need to manually modify QUnit // internals in order to avoid post-`done` assertions causing additional failures var done = assert.async(); assert.expect( 1 ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; var overDone = assert.async(); overDone(); assert.throws(function() { overDone(); }, new RegExp( "Too many calls to the `assert.async` callback" ) ); done(); } }); QUnit.test( "beforeEach", function( /* assert */ ) { // noop }); QUnit.module( "assert.async fails if callback is called more than once in", { afterEach: function( assert ) { // Having an outer async flow in this test avoids the need to manually modify QUnit // internals in order to avoid post-`done` assertions causing additional failures var done = assert.async(); assert.expect( 1 ); // Duck-punch to force an Error to be thrown instead of a `pushFailure` call assert.test.pushFailure = function( msg ) { throw new Error( msg ); }; var overDone = assert.async(); overDone(); assert.throws(function() { overDone(); }, new RegExp( "Too many calls to the `assert.async` callback" ) ); done(); } }); QUnit.test( "afterEach", function( /* assert */ ) { // noop }); QUnit.module( "assert.async in beforeEach", { beforeEach: function( assert ) { var done = assert.async(), testContext = this; setTimeout(function() { testContext.state = "beforeEach"; done(); }); } }); QUnit.test( "beforeEach synchronized", function( assert ) { assert.expect( 1 ); assert.equal( this.state, "beforeEach", "beforeEach synchronized before test callback was " + "executed" ); }); QUnit.module( "assert.async before afterEach", { afterEach: function( assert ) { assert.equal( this.state, "done", "test callback synchronized before afterEach was " + "executed" ); } }); QUnit.test( "afterEach will synchronize", function( assert ) { assert.expect( 1 ); var done = assert.async(), testContext = this; setTimeout(function() { testContext.state = "done"; done(); }); }); QUnit.module( "assert.async in afterEach", { afterEach: function( assert ) { var done = assert.async(); setTimeout(function() { assert.ok( true, "afterEach synchronized before test was finished" ); done(); }); } }); QUnit.test( "afterEach will synchronize", function( assert ) { assert.expect( 1 ); }); QUnit.module( "assert.async callback event loop timing" ); QUnit.test( "`done` can be called synchronously", function( assert ) { var done; assert.expect( 1 ); done = assert.async(); assert.ok( true ); done(); }); QUnit.test( "sole `done` is called last", function( assert ) { var done; assert.expect( 1 ); done = assert.async(); setTimeout(function() { assert.ok( true, "should pass if called before `done`" ); done(); }); }); QUnit.test( "multiple `done` calls, no assertions after final `done`", function( assert ) { var done1, done2; assert.expect( 2 ); done1 = assert.async(); done2 = assert.async(); setTimeout(function() { done1(); assert.ok( true, "should pass if called after this `done` but before final `done`" ); }); setTimeout(function() { assert.ok( true, "should pass if called before final `done`" ); done2(); }); }); QUnit.module( "assertions after final assert.async callback in test callback fail", { beforeEach: function( assert ) { _setupForFailingAssertionsAfterAsyncDone.call( this, assert ); } }); QUnit.test( "sole `done` is called synchronously BEFORE passing assertion", function( assert ) { assert.expect( 1 ); assert.async()(); this._assertCatch(function() { // FAIL!!! (with duck-punch to force an Error to be thrown instead of a `pushFailure` call) assert.ok( true, "should fail with a special `done`-related error message if called " + "after `done` even if result is passing" ); }); }); QUnit.test( "sole `done` is called BEFORE assertion", function( assert ) { var testContext = this, done = assert.async(); assert.expect( 1 ); setTimeout(function() { done(); testContext._assertCatch(function() { // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after `done` even if result is passing" ); }); }); }); QUnit.test( "multiple `done` calls, final `done` is called BEFORE assertion", function( assert ) { var testContext = this, done1 = assert.async(), done2 = assert.async(); assert.expect( 2 ); setTimeout(function() { done1(); assert.ok( true, "should pass as this is not after the final `done`" ); }); setTimeout(function() { done2(); testContext._assertCatch(function() { // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after final `done` even if result is passing" ); }); }); }); QUnit.test( "cannot allow assertions between first `done` call and second `assert.async` call", function( assert ) { var done2, testContext = this, done1 = assert.async(); assert.expect( 1 ); setTimeout(function() { done1(); testContext._assertCatch(function() { // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after final `done` even if result is passing" ); done2 = assert.async(); setTimeout(function() { assert.ok( false, "Should never reach this point anyway" ); done2(); }); }); }); }); QUnit.module( "assert after last done in beforeEach fail, but allow other phases to run", { beforeEach: function( assert ) { _setupForFailingAssertionsAfterAsyncDone.call( this, assert ); // THIS IS THE ACTUAL TEST! assert.expect( 3 ); this._assertCatch(function() { assert.async()(); // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after final `done` even if result is passing" ); }); }, afterEach: function( assert ) { assert.ok( true, "This assertion should still run in afterEach" ); } }); QUnit.test( "beforeEach will fail but test and afterEach will still run", function( assert ) { assert.ok( true, "This assertion should still run in the test callback" ); }); QUnit.module( "assert after last done in test fail, but allow other phases to run", { beforeEach: function( assert ) { _setupForFailingAssertionsAfterAsyncDone.call( this, assert ); assert.expect( 3 ); assert.ok( true, "This assertion should still run in beforeEach" ); }, afterEach: function( assert ) { assert.ok( true, "This assertion should still run in afterEach" ); } }); QUnit.test( "test will fail, but beforeEach and afterEach will still run", function( assert ) { this._assertCatch(function() { assert.async()(); // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after final `done` even if result is passing" ); }); }); QUnit.module( "assert after last done in afterEach fail, but allow other phases to run", { beforeEach: function( assert ) { _setupForFailingAssertionsAfterAsyncDone.call( this, assert ); assert.expect( 3 ); assert.ok( true, "This assertion should still run in beforeEach" ); }, afterEach: function( assert ) { this._assertCatch(function() { assert.async()(); // FAIL!!! (with duck-punch to force an Error to be thrown instead of `pushFailure`) assert.ok( true, "should fail with a special `done`-related error message if called " + "after final `done` even if result is passing" ); }); } }); QUnit.test( "afterEach will fail but beforeEach and test will still run", function( assert ) { assert.ok( true, "This assertion should still run in the test callback" ); }); qunit-1.22.0/test/main/deepEqual.js000066400000000000000000001537411266310060400171130ustar00rootroot00000000000000/* globals Set:false, Map:false, Symbol:false */ QUnit.module( "equiv" ); QUnit.test( "Primitive types and constants", function( assert ) { assert.equal( QUnit.equiv( null, null ), true, "null" ); assert.equal( QUnit.equiv( null, {} ), false, "null" ); assert.equal( QUnit.equiv( null, undefined ), false, "null" ); assert.equal( QUnit.equiv( null, 0 ), false, "null" ); assert.equal( QUnit.equiv( null, false ), false, "null" ); assert.equal( QUnit.equiv( null, "" ), false, "null" ); assert.equal( QUnit.equiv( null, [] ), false, "null" ); assert.equal( QUnit.equiv( undefined, undefined ), true, "undefined" ); assert.equal( QUnit.equiv( undefined, null ), false, "undefined" ); assert.equal( QUnit.equiv( undefined, 0 ), false, "undefined" ); assert.equal( QUnit.equiv( undefined, false ), false, "undefined" ); assert.equal( QUnit.equiv( undefined, {} ), false, "undefined" ); assert.equal( QUnit.equiv( undefined, [] ), false, "undefined" ); assert.equal( QUnit.equiv( undefined, "" ), false, "undefined" ); // Nan usually doest not equal to Nan using the '==' operator. // Only isNaN() is able to do it. assert.equal( QUnit.equiv( 0 / 0, 0 / 0 ), true, "NaN" ); // NaN VS NaN assert.equal( QUnit.equiv( 1 / 0, 2 / 0 ), true, "Infinity" ); // Infinity VS Infinity assert.equal( QUnit.equiv( -1 / 0, 2 / 0 ), false, "-Infinity, Infinity" ); // -Infinity VS Infinity assert.equal( QUnit.equiv( -1 / 0, -2 / 0 ), true, "-Infinity, -Infinity" ); // -Infinity VS -Infinity assert.equal( QUnit.equiv( 0 / 0, 1 / 0 ), false, "NaN, Infinity" ); // Nan VS Infinity assert.equal( QUnit.equiv( 1 / 0, 0 / 0 ), false, "NaN, Infinity" ); // Nan VS Infinity assert.equal( QUnit.equiv( 0 / 0, null ), false, "NaN" ); assert.equal( QUnit.equiv( 0 / 0, undefined ), false, "NaN" ); assert.equal( QUnit.equiv( 0 / 0, 0 ), false, "NaN" ); assert.equal( QUnit.equiv( 0 / 0, false ), false, "NaN" ); assert.equal( QUnit.equiv( 0 / 0, function() {} ), false, "NaN" ); assert.equal( QUnit.equiv( 1 / 0, null ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, undefined ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, 0 ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, 1 ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, false ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, true ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 1 / 0, function() {} ), false, "NaN, Infinity" ); assert.equal( QUnit.equiv( 0, 0 ), true, "number" ); assert.equal( QUnit.equiv( 0, 1 ), false, "number" ); assert.equal( QUnit.equiv( 1, 0 ), false, "number" ); assert.equal( QUnit.equiv( 1, 1 ), true, "number" ); assert.equal( QUnit.equiv( 1.1, 1.1 ), true, "number" ); assert.equal( QUnit.equiv( 0.0000005, 0.0000005 ), true, "number" ); assert.equal( QUnit.equiv( 0, "" ), false, "number" ); assert.equal( QUnit.equiv( 0, "0" ), false, "number" ); assert.equal( QUnit.equiv( 1, "1" ), false, "number" ); assert.equal( QUnit.equiv( 0, false ), false, "number" ); assert.equal( QUnit.equiv( 1, true ), false, "number" ); assert.equal( QUnit.equiv( true, true ), true, "boolean" ); assert.equal( QUnit.equiv( true, false ), false, "boolean" ); assert.equal( QUnit.equiv( false, true ), false, "boolean" ); assert.equal( QUnit.equiv( false, 0 ), false, "boolean" ); assert.equal( QUnit.equiv( false, null ), false, "boolean" ); assert.equal( QUnit.equiv( false, undefined ), false, "boolean" ); assert.equal( QUnit.equiv( true, 1 ), false, "boolean" ); assert.equal( QUnit.equiv( true, null ), false, "boolean" ); assert.equal( QUnit.equiv( true, undefined ), false, "boolean" ); assert.equal( QUnit.equiv( "", "" ), true, "string" ); assert.equal( QUnit.equiv( "a", "a" ), true, "string" ); assert.equal( QUnit.equiv( "foobar", "foobar" ), true, "string" ); assert.equal( QUnit.equiv( "foobar", "foo" ), false, "string" ); assert.equal( QUnit.equiv( "", 0 ), false, "string" ); assert.equal( QUnit.equiv( "", false ), false, "string" ); assert.equal( QUnit.equiv( "", null ), false, "string" ); assert.equal( QUnit.equiv( "", undefined ), false, "string" ); // Rename for lint validation. // We know this is bad, we are asserting whether we can coop with bad code like this. var SafeNumber = Number, SafeString = String, SafeBoolean = Boolean, SafeObject = Object; // primitives vs. objects assert.equal( QUnit.equiv( 0, new SafeNumber() ), true, "number 0 primitive vs. object" ); assert.equal( QUnit.equiv( new SafeNumber(), 0 ), true, "number 0 object vs. primitive" ); assert.equal( QUnit.equiv( new SafeNumber(), new SafeNumber() ), true, "empty number objects" ); assert.equal( QUnit.equiv( 1, new SafeNumber( 1 ) ), true, "number 1 primitive vs. object" ); assert.equal( QUnit.equiv( new SafeNumber( 1 ), 1 ), true, "number 1 object vs. primitive" ); assert.equal( QUnit.equiv( new SafeNumber( 1 ), new SafeNumber( 1 ) ), true, "number 1 objects" ); assert.equal( QUnit.equiv( 0, new SafeNumber( 1 ) ), false, "differing number primitive vs. object" ); assert.equal( QUnit.equiv( new SafeNumber( 0 ), 1 ), false, "differing number object vs. primitive" ); assert.equal( QUnit.equiv( "", new SafeString() ), true, "empty string primitive vs. object" ); assert.equal( QUnit.equiv( new SafeString(), "" ), true, "empty string object vs. primitive" ); assert.equal( QUnit.equiv( new SafeString(), new SafeString() ), true, "empty string objects" ); assert.equal( QUnit.equiv( "My String", new SafeString( "My String" ) ), true, "nonempty string primitive vs. object" ); assert.equal( QUnit.equiv( new SafeString( "My String" ), "My String" ), true, "nonempty string object vs. primitive" ); assert.equal( QUnit.equiv( new SafeString( "My String" ), new SafeString( "My String" ) ), true, "nonempty string objects" ); assert.equal( QUnit.equiv( "Bad String", new SafeString( "My String" ) ), false, "differing string primitive vs. object" ); assert.equal( QUnit.equiv( new SafeString( "Bad String" ), "My String" ), false, "differing string object vs. primitive" ); assert.equal( QUnit.equiv( false, new SafeBoolean() ), true, "boolean false primitive vs. object" ); assert.equal( QUnit.equiv( new SafeBoolean(), false ), true, "boolean empty object vs. primitive" ); assert.equal( QUnit.equiv( new SafeBoolean(), new SafeBoolean() ), true, "empty boolean objects" ); assert.equal( QUnit.equiv( true, new SafeBoolean( true ) ), true, "boolean true primitive vs. object" ); assert.equal( QUnit.equiv( new SafeBoolean( true ), true ), true, "boolean true object vs. primitive" ); assert.equal( QUnit.equiv( new SafeBoolean( true ), new SafeBoolean( true ) ), true, "boolean true objects" ); assert.equal( QUnit.equiv( true, new SafeBoolean( 1 ) ), true, "boolean true primitive vs. truthy object" ); assert.equal( QUnit.equiv( false, new SafeBoolean( false ) ), true, "boolean false primitive vs. false object" ); assert.equal( QUnit.equiv( new SafeBoolean( false ), false ), true, "boolean false object vs. primitive" ); assert.equal( QUnit.equiv( new SafeBoolean( false ), new SafeBoolean( false ) ), true, "boolean false objects" ); assert.equal( QUnit.equiv( false, new SafeBoolean( 0 ) ), true, "boolean false primitive vs. 0 object" ); assert.equal( QUnit.equiv( true, new SafeBoolean( false ) ), false, "differing boolean primitive vs. object" ); assert.equal( QUnit.equiv( new SafeBoolean( false ), true ), false, "differing boolean object vs. primitive" ); assert.equal( QUnit.equiv( new SafeObject(), {} ), true, "empty object instantiation vs. literal" ); assert.equal( QUnit.equiv( {}, new SafeObject() ), true, "empty object literal vs. instantiation" ); assert.equal( QUnit.equiv( new SafeObject(), { a: 1 } ), false, "empty object instantiation vs. nonempty literal" ); assert.equal( QUnit.equiv( { a: 1 }, new SafeObject() ), false, "nonempty object literal vs. empty instantiation" ); assert.equal( QUnit.equiv( { a: undefined }, new SafeObject() ), false, "other nonempty object literal vs. empty instantiation" ); assert.equal( QUnit.equiv( new SafeObject(), { a: undefined } ), false, "empty object instantiation vs. other nonempty literal" ); }); QUnit.test( "Objects basics", function( assert ) { assert.equal( QUnit.equiv( {}, {} ), true ); assert.equal( QUnit.equiv( {}, null ), false ); assert.equal( QUnit.equiv( {}, undefined ), false ); assert.equal( QUnit.equiv( {}, 0 ), false ); assert.equal( QUnit.equiv( {}, false ), false ); // This test is a hard one, it is very important // REASONS: // 1) They are of the same type "object" // 2) [] instanceof Object is true // 3) Their properties are the same (doesn't exists) assert.equal( QUnit.equiv( {}, [] ), false ); assert.equal( QUnit.equiv( { a: 1 }, { a: 1 } ), true ); assert.equal( QUnit.equiv( { a: 1 }, { a: "1" } ), false ); assert.equal( QUnit.equiv( { a: [] }, { a: [] } ), true ); assert.equal( QUnit.equiv( { a: {} }, { a: null } ), false ); assert.equal( QUnit.equiv( { a: 1 }, {} ), false ); assert.equal( QUnit.equiv( {}, { a: 1 } ), false ); // Hard ones assert.equal( QUnit.equiv( { a: undefined }, {} ), false ); assert.equal( QUnit.equiv( {}, { a: undefined } ), false ); assert.equal( QUnit.equiv( { a: [ { bar: undefined } ] }, { a: [ { bat: undefined } ] } ), false ); }); QUnit[ typeof Object.create === "function" ? "test" : "skip" ]( "Objects with null prototypes", function( assert ) { var nonEmptyWithNoProto; // Objects with no prototype, created via Object.create(null), are used // e.g. as dictionaries. // Being able to test equivalence against object literals is quite useful. assert.equal( QUnit.equiv( Object.create( null ), {} ), true, "empty object without prototype VS empty object" ); assert.equal( QUnit.equiv( {}, Object.create( null ) ), true, "empty object VS empty object without prototype" ); nonEmptyWithNoProto = Object.create( null ); nonEmptyWithNoProto.foo = "bar"; assert.equal( QUnit.equiv( nonEmptyWithNoProto, { foo: "bar" } ), true, "object without prototype VS object" ); assert.equal( QUnit.equiv( { foo: "bar" }, nonEmptyWithNoProto ), true, "object VS object without prototype" ); }); // Ref #851 QUnit[ typeof Object.create === "function" ? "test" : "skip" ]( "Object prototype constructor is null", function( assert ) { // Ref #851 // Unfortunately, in practice `Object.create(null)` is fairly costly. // To mitigate this cost a specialized NullObject can be used. This // Object has similar safe characteristics, but with dramatically // reduced allocation costs. function NullObject() {} NullObject.prototype = Object.create( null, { constructor: { value: null } }); var a = new NullObject(); a.foo = 1; var b = { foo: 1 }; assert.ok( QUnit.equiv( a, b ) ); assert.ok( QUnit.equiv( b, a ) ); }); QUnit.test( "Arrays basics", function( assert ) { assert.equal( QUnit.equiv( [], [] ), true ); // May be a hard one, can invoke a crash at execution. // because their types are both "object" but null isn't // like a true object, it doesn't have any property at all. assert.equal( QUnit.equiv( [], null ), false ); assert.equal( QUnit.equiv( [], undefined ), false ); assert.equal( QUnit.equiv( [], false ), false ); assert.equal( QUnit.equiv( [], 0 ), false ); assert.equal( QUnit.equiv( [], "" ), false ); // May be a hard one, but less hard // than {} with [] (note the order) assert.equal( QUnit.equiv( [], {} ), false ); assert.equal( QUnit.equiv( [ null ], [] ), false ); assert.equal( QUnit.equiv( [ undefined ], [] ), false ); assert.equal( QUnit.equiv( [], [ null ] ), false ); assert.equal( QUnit.equiv( [], [ undefined ] ), false ); assert.equal( QUnit.equiv( [ null ], [ undefined ]), false ); assert.equal( QUnit.equiv( [ [] ], [ [] ] ), true ); assert.equal( QUnit.equiv( [ [], [], [] ], [ [], [], [] ] ), true ); assert.equal( QUnit.equiv( [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]), true ); assert.equal( QUnit.equiv( [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]), // shorter false ); assert.equal( QUnit.equiv( [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ {} ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]), // deepest element not an array false ); // same multidimensional assert.equal( QUnit.equiv( [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,4,[ 1,2,[ 1,2,3,4,[ 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]], [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,4,[ 1,2,[ 1,2,3,4,[ 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]]), true, "Multidimensional" ); // different multidimensional assert.equal( QUnit.equiv( [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,4,[ 1,2,[ 1,2,3,4,[ 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]], [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,4,[ 1,2,[ "1",2,3,4,[ // string instead of number 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]]), false, "Multidimensional" ); // different multidimensional assert.equal( QUnit.equiv( [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,4,[ 1,2,[ 1,2,3,4,[ 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]], [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,6,7,8,9, [ 1,2,3,4,5,[ [ 6,7,8,9, [ [ 1,2,3,4,[ 2,3,[ // missing an element (4) 1,2,[ 1,2,3,4,[ 1,2,3,4,5,6,7,8,9,[ 0 ],1,2,3,4,5,6,7,8,9 ],5,6,7,8,9 ],4,5,6,7,8,9 ],5,6,7,8,9 ],5,6,7 ] ] ] ] ]]]), false, "Multidimensional" ); }); QUnit.test( "Functions", function( assert ) { var f0 = function() {}, f1 = function() {}, // f2 and f3 have the same code, formatted differently f2 = function() { return 0; }, f3 = function() { /* jshint asi:true */ return 0 // this comment and no semicoma as difference }; assert.equal( QUnit.equiv( function() {}, function() {} ), false, "Anonymous functions" ); // exact source code assert.equal( QUnit.equiv(function() {}, function() { return true; }), false, "Anonymous functions" ); assert.equal( QUnit.equiv( f0, f0 ), true, "Function references" ); // same references assert.equal( QUnit.equiv( f0, f1 ), false, "Function references" ); // exact source code, different references assert.equal( QUnit.equiv( f2, f3 ), false, "Function references" ); // equivalent source code, different references assert.equal( QUnit.equiv( f1, f2 ), false, "Function references" ); // different source code, different references assert.equal( QUnit.equiv( function() {}, true ), false ); assert.equal( QUnit.equiv( function() {}, undefined ), false ); assert.equal( QUnit.equiv( function() {}, null ), false ); assert.equal( QUnit.equiv( function() {}, {} ), false ); }); QUnit.test( "Date instances", function( assert ) { // Date, we don't need to test Date.parse() because it returns a number. // Only test the Date instances by setting them a fix date. // The date use is midnight January 1, 1970 var d1, d2, d3; d1 = new Date(); d1.setTime( 0 ); // fix the date d2 = new Date(); d2.setTime( 0 ); // fix the date d3 = new Date(); // The very now // Anyway their types differs, just in case the code fails in the order in which it deals with date assert.equal( QUnit.equiv( d1, 0 ), false ); // d1.valueOf() returns 0, but d1 and 0 are different // test same values date and different instances equality assert.equal( QUnit.equiv( d1, d2 ), true ); // test different date and different instances difference assert.equal( QUnit.equiv( d1, d3 ), false ); }); QUnit.test( "RegExp", function( assert ) { // Must test cases that imply those traps: // var a = /./; // a instanceof Object; // Oops // a instanceof RegExp; // Oops // typeof a === "function"; // Oops, false in IE and Opera, true in FF and Safari ("object") // Tests same regex with same modifiers in different order var regex1, regex2, regex3, r3a, r3b, ru1, ru2, r1 = /foo/, r2 = /foo/gim, r3 = /foo/gmi, r4 = /foo/igm, r5 = /foo/img, r6 = /foo/mig, r7 = /foo/mgi, ri1 = /foo/i, ri2 = /foo/i, rm1 = /foo/m, rm2 = /foo/m, rg1 = /foo/g, rg2 = /foo/g; assert.equal( QUnit.equiv( r2, r3 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r2, r4 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r2, r5 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r2, r6 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r2, r7 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r1, r2 ), false, "Modifier" ); assert.equal( QUnit.equiv( ri1, ri2 ), true, "Modifier" ); assert.equal( QUnit.equiv( r1, ri1 ), false, "Modifier" ); assert.equal( QUnit.equiv( ri1, rm1 ), false, "Modifier" ); assert.equal( QUnit.equiv( r1, rm1 ), false, "Modifier" ); assert.equal( QUnit.equiv( rm1, ri1 ), false, "Modifier" ); assert.equal( QUnit.equiv( rm1, rm2 ), true, "Modifier" ); assert.equal( QUnit.equiv( rg1, rm1 ), false, "Modifier" ); assert.equal( QUnit.equiv( rm1, rg1 ), false, "Modifier" ); assert.equal( QUnit.equiv( rg1, rg2 ), true, "Modifier" ); // Compare unicode modifier try { r2 = new RegExp( "foo", "umig" ); r3 = new RegExp( "foo", "mgiu" ); assert.equal( QUnit.equiv( r2, r3 ), true, "Modifier order" ); assert.equal( QUnit.equiv( r1, r2 ), false, "Modifier" ); ru1 = new RegExp( "/u{1D306}", "u" ); ru2 = new RegExp( "/u{1D306}", "u" ); assert.equal( QUnit.equiv( ru1, rg1 ), false, "Modifier" ); assert.equal( QUnit.equiv( rg1, ru1 ), false, "Modifier" ); assert.equal( QUnit.equiv( ru1, ru2 ), true, "Modifier" ); } catch ( e ) {} // Different regex, same modifiers r1 = /[a-z]/gi; r2 = /[0-9]/gi; // oops! different assert.equal( QUnit.equiv( r1, r2 ), false, "Regex pattern" ); r1 = /0/ig; r2 = /"0"/ig; // oops! different assert.equal( QUnit.equiv( r1, r2 ), false, "Regex pattern" ); r1 = /[\n\r\u2028\u2029]/g; r2 = /[\n\r\u2028\u2029]/g; r3 = /[\n\r\u2028\u2028]/g; // differs from r1 r4 = /[\n\r\u2028\u2029]/; // differs from r1 assert.equal( QUnit.equiv( r1, r2 ), true, "Regex pattern" ); assert.equal( QUnit.equiv( r1, r3 ), false, "Regex pattern" ); assert.equal( QUnit.equiv( r1, r4 ), false, "Regex pattern" ); // More complex regex regex1 = "^[-_.a-z0-9]+@([-_a-z0-9]+\\.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$"; regex2 = "^[-_.a-z0-9]+@([-_a-z0-9]+\\.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$"; // regex 3 is different: '.' not escaped regex3 = "^[-_.a-z0-9]+@([-_a-z0-9]+.)+([A-Za-z][A-Za-z]|[A-Za-z][A-Za-z][A-Za-z])|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$"; r1 = new RegExp( regex1 ); r2 = new RegExp( regex2 ); r3 = new RegExp( regex3 ); // diff from r21, not same pattern r3a = new RegExp( regex3, "gi" ); // diff from r23, not same modifier r3b = new RegExp( regex3, "ig" ); // same as r23a assert.equal( QUnit.equiv( r1, r2 ), true, "Complex Regex" ); assert.equal( QUnit.equiv( r1, r3 ), false, "Complex Regex" ); assert.equal( QUnit.equiv( r3, r3a ), false, "Complex Regex" ); assert.equal( QUnit.equiv( r3a, r3b ), true, "Complex Regex" ); // typeof r1 is "function" in some browsers and "object" in others so we must cover this test r1 = / /; assert.equal( QUnit.equiv( r1, function() {} ), false, "Regex internal" ); assert.equal( QUnit.equiv( r1, {} ), false, "Regex internal" ); }); QUnit.test( "Complex objects", function( assert ) { function fn1() { return "fn1"; } function fn2() { return "fn2"; } // Try to invert the order of some properties to make sure it is covered. // It can failed when properties are compared between unsorted arrays. assert.equal( QUnit.equiv( { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, q: [], p: 1 / 0, o: 99 }, l: undefined, m: null } }, d: 0, i: true, h: "false" } }, e: undefined, g: "", h: "h", f: {}, i: [] }, { a: 1, b: null, c: [ {} ], d: { b: false, a: 3.14159, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", t: undefined, u: 0, s: [ 1, 2, 3 ], v: { w: { x: { z: null, y: "Yahoo!" } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, i: true, h: "false" } }, e: undefined, g: "", f: {}, h: "h", i: [] } ), true); assert.equal( QUnit.equiv( { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { //r: "r", // different: missing a property s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] }, { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] } ), false); assert.equal( QUnit.equiv( { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] }, { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], //t: undefined, // different: missing a property with an undefined value u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] } ), false); assert.equal( QUnit.equiv( { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] }, { a: 1, b: null, c: [ {} ], d: { a: 3.14159, b: false, c: { d: 0, e: fn1, f: [[[]]], g: { j: { k: { n: { r: "r", s: [ 1, 2, 3 ], t: undefined, u: 0, v: { w: { x: { y: "Yahoo!", z: null } } } }, o: 99, p: 1 / 0, q: {} // different was [] }, l: undefined, m: null } }, h: "false", i: true } }, e: undefined, f: {}, g: "", h: "h", i: [] } ), false ); var same1 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, same2 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff1 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3, 4 ] ], // different: 4 was add to the array bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff2 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], newprop: undefined, // different: newprop was added bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff3 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ α" // different: missing last char ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff4 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1,2,undefined,{}, [], [ 1, 2, 3 ] ], // different: undefined instead of null bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff5 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], bat: undefined // different: property name not "bar" }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn1 }, diff6 = { a: [ "string", null, 0, "1", 1, { prop: null, foo: [ 1, 2, null, {}, [], [ 1, 2, 3 ] ], bar: undefined }, 3, "Hey!", "Κάνε πάντα γνωÏίζουμε ας των, μηχανής επιδιόÏθωσης επιδιοÏθώσεις ÏŽÏ‚ μια. Κλπ ας" ], unicode: "è€ æ±‰è¯ä¸å˜åœ¨ 港澳和海外的åŽäººåœˆä¸ 贵州 我去了书店 现在尚有争", b: "b", c: fn2 // different: fn2 instead of fn1 }; assert.equal( QUnit.equiv( same1, same2 ), true ); assert.equal( QUnit.equiv( same2, same1 ), true ); assert.equal( QUnit.equiv( same2, diff1 ), false ); assert.equal( QUnit.equiv( diff1, same2 ), false ); assert.equal( QUnit.equiv( same1, diff1 ), false ); assert.equal( QUnit.equiv( same1, diff2 ), false ); assert.equal( QUnit.equiv( same1, diff3 ), false ); assert.equal( QUnit.equiv( same1, diff3 ), false ); assert.equal( QUnit.equiv( same1, diff4 ), false ); assert.equal( QUnit.equiv( same1, diff5 ), false ); assert.equal( QUnit.equiv( same1, diff6 ), false ); assert.equal( QUnit.equiv( diff5, diff1 ), false ); }); QUnit.test( "Complex Arrays", function( assert ) { function fn() {} assert.equal( QUnit.equiv( [ 1, 2, 3, true, {}, null, [ { a: [ "", "1", 0 ] }, 5, 6, 7 ], "foo" ], [ 1, 2, 3, true, {}, null, [ { a: [ "", "1", 0 ] }, 5, 6, 7 ], "foo" ] ), true ); assert.equal( QUnit.equiv( [ 1, 2, 3, true, {}, null, [ { a: [ "", "1", 0 ] }, 5, 6, 7 ], "foo" ], [ 1, 2, 3, true, {}, null, [ { b: [ "", "1", 0 ] // not same property name }, 5, 6, 7 ], "foo" ] ), false ); var a = [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1,[ 3,4,6,9, null, [], [] ] ], e: fn, f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 3 ]]]], "3" ], {}, 1 / 0 ], [], [ [ [], "foo", null, { n: 1 / 0, z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {} ] ] ]; assert.equal( QUnit.equiv( a, [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1, [ 3,4,6,9, null, [], [] ]], e: fn, f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 3 ]]]], "3" ], {}, 1 / 0 ], [], [[[], "foo", null, { n: 1 / 0, z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {}]]]), true); assert.equal( QUnit.equiv( a, [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1, [ 3,4,6,9, null, [], [] ]], e: fn, f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 2 ]]]], "3"], {}, 1 / 0 // different: [[[[[2]]]]] instead of [[[[[3]]]]] ], [], [[[], "foo", null, { n: 1 / 0, z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {}]]]), false); assert.equal( QUnit.equiv( a, [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1, [ 3,4,6,9, null, [], [] ]], e: fn, f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 3 ]]]], "3" ], {}, 1 / 0 ], [], [[[], "foo", null, { n: -1 / 0, // different, -Infinity instead of Infinity z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {}]]]), false); assert.equal( QUnit.equiv( a, [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1, [ 3,4,6,9, null, [], [] ]], e: fn, f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 3 ]]]], "3" ], {}, 1 / 0 ], [], [[[], "foo", { // different: null is missing n: 1 / 0, z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {}]]]), false); assert.equal( QUnit.equiv( a, [ { b: fn, c: false, "do": "reserved word", "for": { ar: [ 3, 5, 9, "hey!", [], { ar: [ 1, [ 3,4,6,9, null, [], [] ]], e: fn // different: missing property f: undefined } ] }, e: 0.43445 }, 5, "string", 0, fn, false, null, undefined, 0, [ 4,5,6,7,8,9,11,22,33,44,55,"66", null, [], [[[[[ 3 ]]]], "3" ], {}, 1 / 0 ], [], [[[], "foo", null, { n: 1 / 0, z: { a: [ 3, 4, 5, 6, "yep!", undefined, undefined ], b: {} } }, {}]]]), false); }); QUnit.test( "Prototypal inheritance", function( assert ) { function Gizmo( id ) { this.id = id; } function Hoozit( id ) { this.id = id; } Hoozit.prototype = new Gizmo(); var gizmo = new Gizmo( "ok" ), hoozit = new Hoozit( "ok" ); // Try this test many times after test on instances that hold function // to make sure that our code does not mess with last object constructor memoization. assert.equal( QUnit.equiv( function() {}, function() {} ), false ); // Hoozit inherit from Gizmo // hoozit instanceof Hoozit; // true // hoozit instanceof Gizmo; // true assert.equal( QUnit.equiv( hoozit, gizmo ), true ); Gizmo.prototype.bar = true; // not a function just in case we skip them // Hoozit inherit from Gizmo // They are equivalent assert.equal( QUnit.equiv( hoozit, gizmo ), true ); // Make sure this is still true !important // The reason for this is that I forgot to reset the last // caller to where it were called from. assert.equal( QUnit.equiv( function() {}, function() {} ), false ); // Make sure this is still true !important assert.equal( QUnit.equiv( hoozit, gizmo ), true ); Hoozit.prototype.foo = true; // not a function just in case we skip them // Gizmo does not inherit from Hoozit // gizmo instanceof Gizmo; // true // gizmo instanceof Hoozit; // false // They are not equivalent assert.equal( QUnit.equiv( hoozit, gizmo ), false ); // Make sure this is still true !important assert.equal( QUnit.equiv( function() {}, function() {} ), false ); }); QUnit.test( "Instances", function( assert ) { var a1, a2, b1, b2, car, carSame, carDiff, human; function A() {} a1 = new A(); a2 = new A(); function B() { this.fn = function() {}; } b1 = new B(); b2 = new B(); assert.equal( QUnit.equiv( a1, a2 ), true, "Same property, same constructor" ); // b1.fn and b2.fn are functions but they are different references // But we decided to skip function for instances. assert.equal( QUnit.equiv( b1, b2 ), true, "Same property, same constructor" ); // failed assert.equal( QUnit.equiv( a1, b1 ), false, "Same properties but different constructor" ); function Car( year ) { var privateVar = 0; this.year = year; this.isOld = function() { return privateVar > 10; }; } function Human( year ) { var privateVar = 1; this.year = year; this.isOld = function() { return privateVar > 80; }; } car = new Car( 30 ); carSame = new Car( 30 ); carDiff = new Car( 10 ); human = new Human( 30 ); /** * difference: * - year: 30 * same: * - year: 30, * - isOld: function () {} */ assert.equal( QUnit.equiv( car, car ), true ); assert.equal( QUnit.equiv( car, carDiff ), false ); assert.equal( QUnit.equiv( car, carSame ), true ); assert.equal( QUnit.equiv( car, human ), false ); }); QUnit.test( "Complex instance nesting (with function values in literals and/or in nested instances)", function( assert ) { var a1, a2, b1, b2, c1, c2, d1, d2, e1, e2; function A( fn ) { this.a = {}; this.fn = fn; this.b = { a: [] }; this.o = {}; this.fn1 = fn; } function B( fn ) { this.fn = fn; this.fn1 = function() {}; this.a = new A(function() {}); } function fnOutside() { } function C( fn ) { function fnInside() { } this.x = 10; this.fn = fn; this.fn1 = function() {}; this.fn2 = fnInside; this.fn3 = { a: true, b: fnOutside // ok make reference to a function in all instances scope }; this.o1 = {}; // This function will be ignored. // Even if it is not visible for all instances (e.g. locked in a closures), // it is from a property that makes part of an instance (e.g. from the C constructor) this.b1 = new B(function() {}); this.b2 = new B({ x: { b2: new B(function() {}) } }); } function D( fn ) { function fnInside() { } this.x = 10; this.fn = fn; this.fn1 = function() {}; this.fn2 = fnInside; this.fn3 = { a: true, b: fnOutside, // ok make reference to a function in all instances scope // This function won't be ignored. // It isn't visible for all C instances // and it is not in a property of an instance. // (in an Object instances e.g. the object literal) c: fnInside }; this.o1 = {}; // This function will be ignored. // Even if it is not visible for all instances (e.g. locked in a closures), // it is from a property that makes part of an instance (e.g. from the C constructor) this.b1 = new B(function() {}); this.b2 = new B({ x: { b2: new B(function() {}) } }); } function E( fn ) { function fnInside() { } this.x = 10; this.fn = fn; this.fn1 = function() {}; this.fn2 = fnInside; this.fn3 = { a: true, b: fnOutside // ok make reference to a function in all instances scope }; this.o1 = {}; // This function will be ignored. // Even if it is not visible for all instances (e.g. locked in a closures), // it is from a property that makes part of an instance (e.g. from the C constructor) this.b1 = new B(function() {}); this.b2 = new B({ x: { b1: new B( { a: function() {} } ), b2: new B(function() {}) } }); } a1 = new A(function() {}); a2 = new A(function() {}); assert.equal( QUnit.equiv( a1, a2 ), true ); assert.equal( QUnit.equiv( a1, a2 ), true ); // different instances b1 = new B(function() {}); b2 = new B(function() {}); assert.equal( QUnit.equiv( b1, b2 ), true ); c1 = new C(function() {}); c2 = new C(function() {}); assert.equal( QUnit.equiv( c1, c2 ), true ); d1 = new D(function() {}); d2 = new D(function() {}); assert.equal( QUnit.equiv( d1, d2 ), false ); e1 = new E(function() {}); e2 = new E(function() {}); assert.equal( QUnit.equiv( e1, e2 ), false ); } ); QUnit.test( "Object with circular references", function( assert ) { var circularA = { abc: null }, circularB = { abc: null }; circularA.abc = circularA; circularB.abc = circularB; assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on object (ambiguous test)" ); circularA.def = 1; circularB.def = 1; assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on object (ambiguous test)" ); circularA.def = 1; circularB.def = 0; assert.equal( QUnit.equiv( circularA, circularB ), false, "Should not repeat test on object (unambiguous test)" ); }); QUnit.test( "Array with circular references", function( assert ) { var circularA = [], circularB = []; circularA.push( circularA ); circularB.push( circularB ); assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on array (ambiguous test)" ); circularA.push( "abc" ); circularB.push( "abc" ); assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on array (ambiguous test)" ); circularA.push( "hello" ); circularB.push( "goodbye" ); assert.equal( QUnit.equiv( circularA, circularB ), false, "Should not repeat test on array (unambiguous test)" ); }); QUnit.test( "Mixed object/array with references to self wont loop", function( assert ) { var circularA = [ { abc: null } ], circularB = [ { abc: null } ]; circularA[ 0 ].abc = circularA; circularB[ 0 ].abc = circularB; circularA.push( circularA ); circularB.push( circularB ); assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on object/array (ambiguous test)" ); circularA[ 0 ].def = 1; circularB[ 0 ].def = 1; assert.equal( QUnit.equiv( circularA, circularB ), true, "Should not repeat test on object/array (ambiguous test)" ); circularA[ 0 ].def = 1; circularB[ 0 ].def = 0; assert.equal( QUnit.equiv( circularA, circularB ), false, "Should not repeat test on object/array (unambiguous test)" ); }); QUnit.test( "Compare self-referent to tree", function( assert ) { var temp, circularA = [ 0 ], treeA = [ 0, null ], circularO = {}, treeO = { o: null }; circularA[ 1 ] = circularA; circularO.o = circularO; assert.equal( QUnit.equiv( circularA, treeA ), false, "Array: Should not consider circular equal to tree" ); assert.equal( QUnit.equiv( circularO, treeO ), false, "Object: Should not consider circular equal to tree" ); temp = [ 0, circularA ]; assert.equal( QUnit.equiv( circularA, temp ), true, "Array: Reference is circular for one, but equal on other" ); assert.equal( QUnit.equiv( temp, circularA ), true, "Array: Reference is circular for one, but equal on other" ); temp = { o: circularO }; assert.equal( QUnit.equiv( circularO, temp ), true, "Object: Reference is circular for one, but equal on other" ); assert.equal( QUnit.equiv( temp, circularO ), true, "Object: Reference is circular for one, but equal on other" ); }); QUnit.test( "Test that must be done at the end because they extend some primitive's prototype", function( assert ) { // Try that a function looks like our regular expression. // This tests if we check that a and b are really both instance of RegExp Function.prototype.global = true; Function.prototype.multiline = true; Function.prototype.ignoreCase = false; Function.prototype.source = "my regex"; var re = /my regex/gm; assert.equal( QUnit.equiv( re, function() {}), false, "A function that looks that a regex isn't a regex" ); // This test will ensures it works in both ways, // and ALSO especially that we can make differences // between RegExp and Function constructor because // typeof on a RegExpt instance is "function" assert.equal( QUnit.equiv(function() {}, re ), false, "Same conversely, but ensures that function and regexp are distinct because their constructor are different" ); } ); QUnit.module( "equiv Object-wrapped primitives" ); QUnit.test( "Number", function( assert ) { var SafeNumber = Number; assert.ok( QUnit.equiv( new SafeNumber( 1 ), new SafeNumber( 1 ) ), "Number objects with same values are equivalent." ); assert.ok( QUnit.equiv( new SafeNumber( 0 / 0 ), new SafeNumber( 0 / 0 ) ), "NaN Number objects are equivalent." ); assert.ok( QUnit.equiv( new SafeNumber( 1 / 0 ), new SafeNumber( 2 / 0 ) ), "Infinite Number objects are equivalent." ); assert.notOk( QUnit.equiv( new SafeNumber( 1 ), new SafeNumber( 2 ) ), "Number objects with different values are not equivalent." ); assert.notOk( QUnit.equiv( new SafeNumber( 0 / 0 ), new SafeNumber( 1 / 0 ) ), "NaN Number objects and infinite Number objects are not equivalent." ); assert.notOk( QUnit.equiv( new SafeNumber( 1 / 0 ), new SafeNumber( -1 / 0 ) ), "Positive and negative infinite Number objects are not equivalent." ); }); QUnit.test( "String", function( assert ) { var SafeString = String; assert.ok( QUnit.equiv( new SafeString( "foo" ), new SafeString( "foo" ) ), "String objects with same values are equivalent." ); assert.ok( QUnit.equiv( new SafeString( "" ), new SafeString( "" ) ), "Empty String objects are equivalent." ); assert.notOk( QUnit.equiv( new SafeString( "foo" ), new SafeString( "bar" ) ), "String objects with different values are not equivalent." ); assert.notOk( QUnit.equiv( new SafeString( "" ), new SafeString( "foo" ) ), "Empty and nonempty String objects are not equivalent." ); }); QUnit.test( "Boolean", function( assert ) { var SafeBoolean = Boolean; assert.ok( QUnit.equiv( new SafeBoolean( true ), new SafeBoolean( true ) ), "True Boolean objects are equivalent." ); assert.ok( QUnit.equiv( new SafeBoolean( false ), new SafeBoolean( false ) ), "False Boolean objects are equivalent." ); assert.notOk( QUnit.equiv( new SafeBoolean( true ), new SafeBoolean( false ) ), "Boolean objects with different values are not equivalent." ); }); QUnit.module( "equiv Maps and Sets" ); var hasES6Set = ( function() { if ( typeof Set !== "function" ) { return false; } try { // some platforms don't support iterables in Set constructors var s = new Set( [ 1, 2, 3 ] ); if ( s.size !== 3 || !s.has( 2 ) ) { return false; } // in IE 11, QUnit.objectType( new Set() ) === "object" return ( QUnit.objectType( s ) === "set" ); } catch ( e ) { return false; } } )(); var hasES6Map = ( function() { if ( typeof Map !== "function" ) { return false; } try { // some platforms don't support array-like iterables in Map constructors var m = new Map( [ [ 1, 2 ] ] ); if ( m.size !== 2 || !m.has( 1 ) ) { return false; } // in IE 11, QUnit.objectType( new Map() ) === "object" return ( QUnit.objectType( m ) === "map" ); } catch ( e ) { return false; } } )(); QUnit[ hasES6Set ? "test" : "skip" ]( "Sets", function ( assert ) { var s1, s2, s3, s4, o1, o2, o3, m1, m2, m3; // Empty sets s1 = new Set(); s2 = new Set( [] ); assert.equal( QUnit.equiv( s1, s2 ), true, "Empty sets" ); // Simple cases s1 = new Set( [ 1 ] ); s2 = new Set( [ 1 ] ); s3 = new Set( [ 3 ] ); assert.equal( QUnit.equiv( s1, s2 ), true, "Single element sets [1] vs [1]" ); assert.equal( QUnit.equiv( s1, s3 ), false, "Single element sets [1] vs [3]" ); // Tricky values s1 = new Set( [ undefined, null, false, 0, NaN, Infinity, -Infinity ] ); s2 = new Set( [ undefined, null, false, 0, NaN, Infinity, -Infinity ] ); assert.equal( QUnit.equiv( s1, s2 ), true, "Multiple-element sets of tricky values" ); // Sets Containing objects o1 = { foo: 0, bar: true }; o2 = { foo: 0, bar: true }; o3 = { foo: 1, bar: true }; s1 = new Set( [ o1, o3 ] ); s2 = new Set( [ o1, o3 ] ); assert.equal( QUnit.equiv( s1, s2 ), true, "Sets containing same objects" ); s1 = new Set( [ o1 ] ); s2 = new Set( [ o2 ] ); assert.equal( QUnit.equiv( s1, s2 ), true, "Sets containing deeply-equal objects" ); s1 = new Set( [ o1 ] ); s2 = new Set( [ o3 ] ); assert.equal( QUnit.equiv( s1, s2 ), false, "Sets containing different objects" ); // Sets containing sets s1 = new Set( [ 1, 2, 3 ] ); s2 = new Set( [ 1, 2, 3 ] ); s3 = new Set( [ s1 ] ); s4 = new Set( [ s2 ] ); assert.equal( QUnit.equiv( s3, s4 ), true, "Sets containing deeply-equal sets" ); // Sets containing different sets s1 = new Set( [ 1, 2, 3 ] ); s2 = new Set( [ 1, 2, 3, 4 ] ); s3 = new Set( [ s1 ] ); s4 = new Set( [ s2 ] ); assert.equal( QUnit.equiv( s3, s4 ), false, "Sets containing different sets" ); // Sets containing maps m1 = new Map( [ [ 1, 1 ] ] ); m2 = new Map( [ [ 1, 1 ] ] ); m3 = new Map( [ [ 1, 3 ] ] ); s3 = new Set( [ m1 ] ); s4 = new Set( [ m2 ] ); assert.equal( QUnit.equiv( s3, s4 ), true, "Sets containing different but deeply-equal maps" ); s3 = new Set( [ m1 ] ); s4 = new Set( [ m3 ] ); assert.equal( QUnit.equiv( s3, s4 ), false, "Sets containing different maps" ); }); QUnit[ hasES6Map ? "test" : "skip" ]( "Maps", function ( assert ) { var m1, m2, m3, m4, o1, o2, o3, s1, s2, s3; // Empty maps m1 = new Map(); m2 = new Map( [] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Empty maps" ); // Simple cases m1 = new Map( [ [ 1, 1 ] ] ); m2 = new Map( [ [ 1, 1 ] ] ); m3 = new Map( [ [ 1, 3 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Single element maps [1,1] vs [1,1]" ); assert.equal( QUnit.equiv( m1, m3 ), false, "Single element maps [1,1] vs [1,3]" ); // Tricky values m1 = new Map( [ [ undefined, undefined ], [ null, null ], [ false, false ], [ 0, 0 ], [ NaN, NaN ], [ Infinity, Infinity ], [ -Infinity, -Infinity ] ] ); m2 = new Map( [ [ undefined, undefined ], [ null, null ], [ false, false ], [ 0, 0 ], [ NaN, NaN ], [ Infinity, Infinity ], [ -Infinity, -Infinity ] ] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Multiple-element maps of tricky values" ); // Same keys, different values m1 = new Map( [ [ 1, "one" ], [ 2, "two" ] ] ); m2 = new Map( [ [ 1, 1 ], [ 2, 2 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), false, "Maps with same keys, different values" ); // Maps Containing objects o1 = { foo: 0, bar: true }; o2 = { foo: 0, bar: true }; o3 = { foo: 1, bar: true }; m1 = new Map( [ [ 1, o1 ], [ 2, o3 ] ] ); m2 = new Map( [ [ 1, o1 ], [ 2, o3 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Maps containing same objects" ); m1 = new Map( [ [ 1, o1 ] ] ); m2 = new Map( [ [ 1, o2 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Maps containing different but deeply-equal objects" ); // Maps containing different objects m1 = new Map( [ [ 1, o1 ] ] ); m2 = new Map( [ [ 1, o3 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), false, "Maps containing different objects" ); // Maps containing maps m1 = new Map( [ [ 1, 1 ] ] ); m2 = new Map( [ [ 1, 1 ] ] ); m3 = new Map( [ [ "myMap", m1 ] ] ); m4 = new Map( [ [ "myMap", m2 ] ] ); assert.equal( QUnit.equiv( m3, m4 ), true, "Maps containing deeply-equal maps" ); // Maps containing different maps m1 = new Map( [ [ 1, 1 ] ] ); m2 = new Map( [ [ 1, 2 ] ] ); m3 = new Map( [ [ "myMap", m1 ] ] ); m4 = new Map( [ [ "myMap", m2 ] ] ); assert.equal( QUnit.equiv( m3, m4 ), false, "Maps containing different maps" ); // Maps containing sets s1 = new Set( [ 1, 2, 3 ] ); s2 = new Set( [ 1, 2, 3 ] ); s3 = new Set( [ 1, 2, 3, 4 ] ); m1 = new Map( [ [ 1, s1 ] ] ); m2 = new Map( [ [ 1, s2 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), true, "Maps containing different but deeply-equal sets" ); // Maps containing different sets m1 = new Map( [ [ 1, s1 ] ] ); m2 = new Map( [ [ 1, s3 ] ] ); assert.equal( QUnit.equiv( m1, m2 ), false, "Maps containing different sets" ); }); QUnit.module( "equiv Symbols" ); var hasES6Symbol = ( function() { return typeof Symbol === "function"; } )(); QUnit[ hasES6Symbol ? "test" : "skip" ]( "regular checks", function ( assert ) { var a = Symbol( 1 ); var b = Symbol( 1 ); assert.equal( QUnit.equiv( a, a ), true, "Same symbol is equivalent" ); assert.equal( QUnit.equiv( a, b ), false, "Not equivalent to another similar symbol built build on the same token" ); } ); qunit-1.22.0/test/main/dump.js000066400000000000000000000144611266310060400161460ustar00rootroot00000000000000QUnit.module( "dump", { teardown: function() { QUnit.dump.maxDepth = null; } }); QUnit.test( "dump output", function( assert ) { assert.equal( QUnit.dump.parse( [ 1, 2 ] ), "[\n 1,\n 2\n]" ); assert.equal( QUnit.dump.parse( { top: 5, left: 0 } ), "{\n \"left\": 0,\n \"top\": 5\n}" ); if ( typeof document !== "undefined" && document.getElementById( "qunit-header" ) ) { assert.equal( QUnit.dump.parse( document.getElementById( "qunit-header" ) ), "

          " ); assert.equal( QUnit.dump.parse( document.getElementsByTagName( "h1" ) ), "[\n

          \n]" ); } }); QUnit.test( "dump output, shallow", function( assert ) { var obj = { top: { middle: { bottom: 0 } }, left: 0 }; assert.expect( 4 ); QUnit.dump.maxDepth = 1; assert.equal( QUnit.dump.parse( obj ), "{\n \"left\": 0,\n \"top\": [object Object]\n}" ); QUnit.dump.maxDepth = 2; assert.equal( QUnit.dump.parse( obj ), "{\n \"left\": 0,\n \"top\": {\n \"middle\": [object Object]\n }\n}" ); QUnit.dump.maxDepth = 3; assert.equal( QUnit.dump.parse( obj ), "{\n \"left\": 0,\n \"top\": {\n \"middle\": {\n \"bottom\": 0\n }\n }\n}" ); QUnit.dump.maxDepth = 5; assert.equal( QUnit.dump.parse( obj ), "{\n \"left\": 0,\n \"top\": {\n \"middle\": {\n \"bottom\": 0\n }\n }\n}" ); }); QUnit.test( "dump, TypeError properties", function( assert ) { function CustomError( message ) { this.message = message; } CustomError.prototype.toString = function() { return this.message; }; var customError = new CustomError( "sad puppy" ), typeError = new TypeError( "crying kitten" ), expectedCustomMessage = "\"message\": \"sad puppy\"", expectedTypeMessage = "\"message\": \"crying kitten\"", expectedTypeName = "\"name\": \"TypeError\"", dumpedCustomError = QUnit.dump.parse( customError ), dumpedTypeError = QUnit.dump.parse( typeError ), dumpedTypeErrorWithEnumerable; // Test when object has some enumerable properties by adding one typeError.hasCheeseburger = true; dumpedTypeErrorWithEnumerable = QUnit.dump.parse( typeError ); assert.pushResult( { result: dumpedCustomError.indexOf(expectedCustomMessage) >= 0, actual: dumpedCustomError, expected: expectedCustomMessage, message: "custom error contains message field" } ); assert.pushResult( { result: dumpedTypeError.indexOf(expectedTypeMessage) >= 0, actual: dumpedTypeError, expected: expectedTypeMessage, message: "type error contains message field" } ); assert.pushResult( { result: dumpedTypeError.indexOf(expectedTypeName) >= 0, actual: dumpedTypeError, expected: expectedTypeName, message: "type error contains name field" } ); assert.pushResult( { result: dumpedTypeErrorWithEnumerable.indexOf(expectedTypeMessage) >= 0, actual: dumpedTypeErrorWithEnumerable, expected: expectedTypeMessage, message: "type error with enumerable field contains message field" } ); }); QUnit.module( "dump, recursions", { Wrap: function( x ) { this.wrap = x; if ( x === undefined ) { this.first = true; } }, chainwrap: function( depth, first, prev ) { depth = depth || 0; var last = prev || new this.Wrap(); first = first || last; if ( depth === 1 ) { first.wrap = last; } if ( depth > 1 ) { last = this.chainwrap( depth - 1, first, new this.Wrap( last ) ); } return last; } }); QUnit.test( "Check dump recursion", function( assert ) { assert.expect( 4 ); var noref, nodump, selfref, selfdump, parentref, parentdump, circref, circdump; noref = this.chainwrap( 0 ); nodump = QUnit.dump.parse( noref ); assert.equal( nodump, "{\n \"first\": true,\n \"wrap\": undefined\n}" ); selfref = this.chainwrap( 1 ); selfdump = QUnit.dump.parse( selfref ); assert.equal( selfdump, "{\n \"first\": true,\n \"wrap\": recursion(-1)\n}" ); parentref = this.chainwrap( 2 ); parentdump = QUnit.dump.parse( parentref ); assert.equal( parentdump, "{\n \"wrap\": {\n \"first\": true,\n \"wrap\": recursion(-2)\n }\n}" ); circref = this.chainwrap( 10 ); circdump = QUnit.dump.parse( circref ); assert.ok( new RegExp( "recursion\\(-10\\)" ).test( circdump ), "(" + circdump + ") should show -10 recursion level" ); }); QUnit.test( "Check equal/deepEqual recursion", function( assert ) { var noRecursion, selfref, circref; noRecursion = this.chainwrap( 0 ); assert.equal( noRecursion, noRecursion, "I should be equal to me." ); assert.deepEqual( noRecursion, noRecursion, "... and so in depth." ); selfref = this.chainwrap( 1 ); assert.equal( selfref, selfref, "Even so if I nest myself." ); assert.deepEqual( selfref, selfref, "... into the depth." ); circref = this.chainwrap( 10 ); assert.equal( circref, circref, "Or hide that through some levels of indirection." ); assert.deepEqual( circref, circref, "... and checked on all levels!" ); }); QUnit.test( "Circular reference with arrays", function( assert ) { var arr, arrdump, obj, childarr, objdump, childarrdump; // pure array self-ref arr = []; arr.push( arr ); arrdump = QUnit.dump.parse( arr ); assert.equal( arrdump, "[\n recursion(-1)\n]" ); assert.equal( arr, arr[ 0 ], "no endless stack when trying to dump arrays with circular ref" ); // mix obj-arr circular ref obj = {}; childarr = [ obj ]; obj.childarr = childarr; objdump = QUnit.dump.parse( obj ); childarrdump = QUnit.dump.parse( childarr ); assert.equal( objdump, "{\n \"childarr\": [\n recursion(-2)\n ]\n}" ); assert.equal( childarrdump, "[\n {\n \"childarr\": recursion(-2)\n }\n]" ); assert.equal( obj.childarr, childarr, "no endless stack when trying to dump array/object mix with circular ref" ); assert.equal( childarr[ 0 ], obj, "no endless stack when trying to dump array/object mix with circular ref" ); }); QUnit.test( "Circular reference - test reported by soniciq in #105", function( assert ) { var a, b, barr, MyObject = function() {}; MyObject.prototype.parent = function( obj ) { if ( obj === undefined ) { return this._parent; } this._parent = obj; }; MyObject.prototype.children = function( obj ) { if ( obj === undefined ) { return this._children; } this._children = obj; }; a = new MyObject(); b = new MyObject(); barr = [ b ]; a.children( barr ); b.parent( a ); assert.equal( a.children(), barr ); assert.deepEqual( a.children(), [ b ] ); }); qunit-1.22.0/test/main/globals.js000066400000000000000000000030251266310060400166160ustar00rootroot00000000000000/*global ok: false, equal: false, throws: false, raises: false */ (function( window ) { QUnit.module( "globals" ); function checkExported( assert, methods, isAssertion ) { var i, l, method; for ( i = 0, l = methods.length; i < l; i++ ) { method = methods[ i ]; assert.strictEqual( typeof( window[ method ] ), "function", "global " + method ); assert.strictEqual( window[ method ], QUnit[ method ], "QUnit exports QUnit." + method + " to the global scope" ); if ( isAssertion ) { assert.strictEqual( window[ method ], assert[ method ], "Global " + method + " is the same of assert." + method ); } } } QUnit.test( "QUnit exported methods", function( assert ) { var globals = [ "test", "asyncTest", "module", "start", "stop" ]; // 2 assertions per item on checkExported assert.expect( globals.length * 2 ); checkExported( assert, globals ); }); // Test deprecated exported Assert methods QUnit.test( "Exported assertions", function() { QUnit.expect( 12 ); QUnit.ok( true ); QUnit.equal( 2, 2 ); QUnit.throws(function() { throw "error"; }); QUnit.raises(function() { throw "error"; }); ok( true ); equal( 2, 2 ); throws(function() { throw "error"; }); raises(function() { throw "error"; }); QUnit.assert.ok( true ); QUnit.assert.equal( 2, 2 ); QUnit.assert.throws(function() { throw "error"; }); QUnit.assert.raises(function() { throw "error"; }); }); // Get a reference to the global object, like window in browsers }( (function() { return this; }.call()) )); qunit-1.22.0/test/main/modules.js000066400000000000000000000161641266310060400166530ustar00rootroot00000000000000QUnit.module( "beforeEach/afterEach", { beforeEach: function() { this.lastHook = "module-beforeEach"; }, afterEach: function( assert ) { if ( this.hooksTest ) { assert.strictEqual( this.lastHook, "test-block", "Module's afterEach runs after current test block" ); this.lastHook = "module-afterEach"; } } }); QUnit.test( "hooks order", function( assert ) { assert.expect( 2 ); // This will trigger an assertion on the global and one on the module's afterEach this.hooksTest = true; assert.strictEqual( this.lastHook, "module-beforeEach", "Module's beforeEach runs before current test block" ); this.lastHook = "test-block"; }); QUnit.module( "Test context object", { beforeEach: function( assert ) { var key, keys = []; for ( key in this ) { keys.push( key ); } assert.deepEqual( keys, [ "helper" ] ); }, afterEach: function() {}, helper: function() {} }); QUnit.test( "keys", function( assert ) { assert.expect( 1 ); this.contextTest = true; }); QUnit.module( "afterEach and QUnit.stop", { beforeEach: function() { this.state = false; }, afterEach: function( assert ) { assert.strictEqual( this.state, true, "Test afterEach." ); } }); QUnit.test( "afterEach must be called after test ended", function( assert ) { var testContext = this; assert.expect( 1 ); QUnit.stop(); setTimeout(function() { testContext.state = true; QUnit.start(); }); }); QUnit.test( "parameter passed to stop increments semaphore n times", function( assert ) { var testContext = this; assert.expect( 1 ); QUnit.stop( 3 ); setTimeout(function() { QUnit.start(); QUnit.start(); }); setTimeout(function() { testContext.state = true; QUnit.start(); }, 1 ); }); QUnit.test( "parameter passed to start decrements semaphore n times", function( assert ) { var testContext = this; assert.expect( 1 ); QUnit.stop(); QUnit.stop(); QUnit.stop(); setTimeout(function() { testContext.state = true; QUnit.start( 3 ); }); }); QUnit.module( "async beforeEach test", { beforeEach: function( assert ) { QUnit.stop(); setTimeout(function() { assert.ok( true ); QUnit.start(); }); } }); QUnit.asyncTest( "module with async beforeEach", function( assert ) { assert.expect( 2 ); assert.ok( true ); QUnit.start(); }); QUnit.module( "async afterEach test", { afterEach: function( assert ) { QUnit.stop(); setTimeout(function() { assert.ok( true ); QUnit.start(); }); } }); QUnit.asyncTest( "module with async afterEach", function( assert ) { assert.expect( 2 ); assert.ok( true ); QUnit.start(); }); QUnit.module( "save scope", { foo: "foo", beforeEach: function( assert ) { assert.deepEqual( this.foo, "foo" ); this.foo = "bar"; }, afterEach: function( assert ) { assert.deepEqual( this.foo, "foobar" ); } }); QUnit.test( "scope check", function( assert ) { assert.expect( 3 ); assert.deepEqual( this.foo, "bar" ); this.foo = "foobar"; }); QUnit.module( "Deprecated setup/teardown", { setup: function() { this.deprecatedSetup = true; }, teardown: function( assert ) { assert.ok( this.deprecatedSetup ); } }); QUnit.test( "before/after order", function( assert ) { assert.expect( 1 ); }); QUnit.module( "pre-nested modules"); QUnit.module( "nested modules", function() { QUnit.module( "first outer", { afterEach: function( assert ) { assert.ok( true, "first outer module afterEach called" ); }, beforeEach: function( assert ) { assert.ok( true, "first outer beforeEach called" ); } }, function() { QUnit.module( "first inner", { afterEach: function( assert ) { assert.ok( true, "first inner module afterEach called" ); }, beforeEach: function( assert ) { assert.ok( true, "first inner module beforeEach called" ); } }, function() { QUnit.test( "in module, before- and afterEach called in out-in-out " + "order", function( assert ) { var module = assert.test.module; assert.equal( module.name, "nested modules > first outer > first inner" ); assert.expect( 5 ); }); }); QUnit.test( "test after nested module is processed", function( assert ) { var module = assert.test.module; assert.equal( module.name, "nested modules > first outer" ); assert.expect( 3 ); }); QUnit.module( "second inner" ); QUnit.test( "test after non-nesting module declared", function( assert ) { var module = assert.test.module; assert.equal( module.name, "nested modules > first outer > second inner" ); assert.expect( 3 ); }); }); QUnit.module( "second outer" ); QUnit.test( "test after all nesting modules processed and new module declared", function( assert ) { var module = assert.test.module; assert.equal( module.name, "nested modules > second outer" ); }); }); QUnit.test( "modules with nested functions does not spread beyond", function( assert ) { assert.equal( assert.test.module.name, "pre-nested modules" ); }); QUnit.module( "contained suite arguments", function( hooks ) { QUnit.test( "hook functions", function( assert ) { assert.strictEqual( typeof hooks.beforeEach, "function" ); assert.strictEqual( typeof hooks.afterEach, "function" ); } ); QUnit.module( "outer hooks", function( hooks ) { var beforeEach = hooks.beforeEach; var afterEach = hooks.afterEach; beforeEach( function( assert ) { assert.ok( true, "beforeEach called" ); } ); afterEach( function( assert ) { assert.ok( true, "afterEach called" ); } ); QUnit.test( "call hooks", function( assert ) { assert.expect( 2 ); } ); QUnit.module( "stacked inner hooks", function( hooks ) { var beforeEach = hooks.beforeEach; var afterEach = hooks.afterEach; beforeEach( function( assert ) { assert.ok( true, "nested beforeEach called" ); } ); afterEach( function( assert ) { assert.ok( true, "nested afterEach called" ); } ); QUnit.test( "call hooks", function( assert ) { assert.expect( 4 ); } ); } ); } ); } ); QUnit.module( "contained suite `this`", function( hooks ) { this.outer = 1; hooks.beforeEach( function() { this.outer++; } ); hooks.afterEach( function( assert ) { assert.equal( this.outer, 42, "in-test environment modifications are visible by afterEach callbacks" ); } ); QUnit.test( "`this` is shared from modules to the tests", function( assert ) { assert.equal( this.outer, 2 ); this.outer = 42; } ); QUnit.test( "sibling tests don't share environments", function( assert ) { assert.equal( this.outer, 2 ); this.outer = 42; } ); QUnit.module( "nested suite `this`", function( hooks ) { this.inner = true; hooks.beforeEach( function( assert ) { assert.ok( this.outer ); assert.ok( this.inner ); } ); hooks.afterEach( function( assert ) { assert.ok( this.outer ); assert.ok( this.inner ); // This change affects the outermodule afterEach assertion. this.outer = 42; } ); QUnit.test( "inner modules share outer environments", function( assert ) { assert.ok( this.outer ); assert.ok( this.inner ); } ); } ); QUnit.test( "tests can't see environments from nested modules", function( assert ) { assert.strictEqual( this.inner, undefined ); this.outer = 42; } ); } ); qunit-1.22.0/test/main/promise.js000066400000000000000000000035331266310060400166550ustar00rootroot00000000000000// NOTE: Adds 1 assertion function createMockPromise( assert ) { // Return a mock self-fulfilling Promise ("thenable") var thenable = { then: function( fulfilledCallback /*, rejectedCallback */ ) { assert.strictEqual( this, thenable, "`then` was invoked with the Promise as the " + "context" ); setTimeout( function() { return fulfilledCallback.call( thenable, {} ); }, 13 ); } }; return thenable; } QUnit.module( "Module with Promise-aware beforeEach", { beforeEach: function( assert ) { assert.ok( true ); return {}; } }); QUnit.test( "non-Promise", function( assert ) { assert.expect( 1 ); }); QUnit.module( "Module with Promise-aware beforeEach", { beforeEach: function( assert ) { // Adds 1 assertion return createMockPromise( assert ); } }); QUnit.test( "fulfilled Promise", function( assert ) { assert.expect( 1 ); }); QUnit.module( "Module with Promise-aware afterEach", { afterEach: function( assert ) { assert.ok( true ); return {}; } }); QUnit.test( "non-Promise", function( assert ) { assert.expect( 1 ); }); QUnit.module( "Module with Promise-aware afterEach", { afterEach: function( assert ) { // Adds 1 assertion return createMockPromise( assert ); } }); QUnit.test( "fulfilled Promise", function( assert ) { assert.expect( 1 ); }); QUnit.module( "Promise-aware return values without beforeEach/afterEach" ); QUnit.test( "non-Promise", function( assert ) { assert.expect( 0 ); return {}; }); QUnit.test( "fulfilled Promise", function( assert ) { assert.expect( 1 ); // Adds 1 assertion return createMockPromise( assert ); }); QUnit.test( "fulfilled Promise with non-Promise async assertion", function( assert ) { assert.expect( 2 ); var done = assert.async(); setTimeout( function() { assert.ok( true ); done(); }, 100 ); // Adds 1 assertion return createMockPromise( assert ); }); qunit-1.22.0/test/main/stack.js000066400000000000000000000010031266310060400162720ustar00rootroot00000000000000( function() { var stack = QUnit.stack(); QUnit.module( "QUnit.stack" ); // Flag this test as skipped on browsers that doesn't support stack trace QUnit[ stack ? "test" : "skip" ]( "returns the proper stack line", function( assert ) { assert.ok( /\/test\/main\/stack\.js/.test( stack ) ); stack = QUnit.stack( 2 ); assert.ok( stack, "can use offset argument to return a different stacktrace line" ); assert.notOk( /\/test\/main\/stack\.js/.test( stack ), "stack with offset argument" ); } ); } )(); qunit-1.22.0/test/main/test.js000066400000000000000000000037441266310060400161620ustar00rootroot00000000000000QUnit.test( "expect query and multiple issue", function( assert ) { assert.expect( 2 ); assert.ok( true ); var expected = assert.expect(); assert.equal( expected, 2 ); assert.expect( expected + 1 ); assert.ok( true ); }); if ( typeof document !== "undefined" ) { QUnit.module( "fixture" ); QUnit.test( "setup", function( assert ) { assert.expect( 0 ); document.getElementById( "qunit-fixture" ).innerHTML = "foobar"; }); QUnit.test( "basics", function( assert ) { assert.equal( document.getElementById( "qunit-fixture" ).innerHTML, "test markup", "automatically reset" ); }); } QUnit.module( "custom assertions" ); QUnit.assert.mod2 = function( value, expected, message ) { var actual = value % 2; this.pushResult( { result: actual === expected, actual: actual, expected: expected, message: message } ); }; QUnit.assert.testForPush = function( value, expected, message ) { this.push( true, value, expected, message, false ); }; QUnit.test( "mod2", function( assert ) { assert.expect( 2 ); assert.mod2( 2, 0, "2 % 2 == 0" ); assert.mod2( 3, 1, "3 % 2 == 1" ); }); QUnit.test( "testForPush", function( assert ) { assert.expect( 6 ); QUnit.log( function( detail ) { if ( detail.message === "should be call pushResult" ) { assert.equal( detail.result, true ); assert.equal( detail.actual, 1 ); assert.equal( detail.expected, 1 ); assert.equal( detail.message, "should be call pushResult" ); assert.equal( detail.negative, false ); } } ); assert.testForPush( 1, 1, "should be call pushResult" ); }); QUnit.module( "QUnit.skip", { beforeEach: function( assert ) { // skip test hooks for skipped tests assert.ok( false, "skipped function" ); throw "Error"; }, afterEach: function( assert ) { assert.ok( false, "skipped function" ); throw "Error"; } }); QUnit.skip( "test blocks are skipped", function( assert ) { // this test callback won't run, even with broken code assert.expect( 1000 ); throw "Error"; }); QUnit.skip( "no function" ); qunit-1.22.0/test/only.html000066400000000000000000000004321266310060400155570ustar00rootroot00000000000000 QUnit Only Test Suite
          qunit-1.22.0/test/only.js000066400000000000000000000007641266310060400152370ustar00rootroot00000000000000QUnit.module( "QUnit.only" ); QUnit.test( "implicitly skipped test", function( assert ) { assert.ok( false, "test should be skipped" ); }); QUnit.only( "only run this test", function( assert ) { assert.ok( true, "only this test should run" ); }); QUnit.test( "another implicitly skipped test", function( assert ) { assert.ok( false, "test should be skipped" ); }); QUnit.only( "ignore the subsequent calls to only", function( assert ) { assert.ok( false, "this test should be skipped" ); }); qunit-1.22.0/test/regex-exclude-filter.html000066400000000000000000000005731266310060400206300ustar00rootroot00000000000000 QUnit Regex Exclude Filtering Test Suite
          qunit-1.22.0/test/regex-exclude-filter.js000066400000000000000000000010431266310060400202710ustar00rootroot00000000000000QUnit.module( "QUnit.config.filter with excluding, case-sensitive regular expression" ); QUnit.test( "foo test should be run", function( assert ) { assert.ok( true, "foo test should be run" ); }); QUnit.test( "Foo test should not be run", function( assert ) { assert.ok( false, "Foo test should not be run" ); }); QUnit.test( "Bar test should be run", function( assert ) { assert.ok( true, "Bar test should be run" ); }); QUnit.test( "bar test should not be run", function( assert ) { assert.ok( false, "bar test should not be run" ); }); qunit-1.22.0/test/regex-filter.html000066400000000000000000000005531266310060400171770ustar00rootroot00000000000000 QUnit Regex Filtering Test Suite
          qunit-1.22.0/test/regex-filter.js000066400000000000000000000010321266310060400166400ustar00rootroot00000000000000QUnit.module( "QUnit.config.filter with case-insensitive regular expression" ); QUnit.test( "foo test should be run", function( assert ) { assert.ok( true, "foo test should be run" ); }); QUnit.test( "boo test should not be run", function( assert ) { assert.ok( false, "boo test should not be run" ); }); QUnit.test( "bar test should be run", function( assert ) { assert.ok( true, "bar test should be run" ); }); QUnit.test( "baz test should not be run", function( assert ) { assert.ok( false, "baz test should not be run" ); }); qunit-1.22.0/test/reorderError1.html000066400000000000000000000031051266310060400173330ustar00rootroot00000000000000 QUnit - Asserts it does not skip tests after reordering
          qunit-1.22.0/test/reorderError1.js000066400000000000000000000003171266310060400170050ustar00rootroot00000000000000QUnit.module( "Test call count - first case" ); QUnit[ window.sessionStorage ? "test" : "skip" ]( "does not skip tests after reordering", function( assert ) { assert.equal( window.totalCount, 3 ); } ); qunit-1.22.0/test/reorderError2.html000066400000000000000000000027431266310060400173430ustar00rootroot00000000000000 QUnit - Asserts it does not skip tests after reordering
          qunit-1.22.0/test/reorderError2.js000066400000000000000000000003201266310060400170000ustar00rootroot00000000000000QUnit.module( "Test call count - second case" ); QUnit[ window.sessionStorage ? "test" : "skip" ]( "does not skip tests after reordering", function( assert ) { assert.equal( window.totalCount, 2 ); } ); qunit-1.22.0/test/reporter-html/000077500000000000000000000000001266310060400165155ustar00rootroot00000000000000qunit-1.22.0/test/reporter-html/diff.js000066400000000000000000000101611266310060400177620ustar00rootroot00000000000000QUnit.module( "diff" ); QUnit.test( "throws if arguments are not strings", function( assert ) { assert.throws(function() { QUnit.diff( {}, "" ); }); assert.throws(function() { QUnit.diff( "", {} ); }); }); QUnit.test( "different strings", function( assert ) { var a = "abcd"; var b = "xkcd"; assert.equal( QUnit.diff( a, b ), "abxkcd", "QUnit.diff( 'abcd', 'xkcd' )" ); assert.equal( QUnit.diff( b, a ), "xkabcd", "QUnit.diff( 'xkcd', 'abcd' )" ); assert.equal( QUnit.diff( a, "" ), "abcd", "QUnit.diff( 'abcd', '' )" ); assert.equal( QUnit.diff( "", a ), "abcd", "QUnit.diff( '', 'abcd' )" ); assert.equal( QUnit.diff( "false", "true" ), "falstrue", "QUnit.diff( 'false', 'true' )" ); assert.equal( QUnit.diff( "true", "false" ), "trufalse", "QUnit.diff( 'true', 'false' )" ); }); QUnit.test( "additions", function( assert ) { var a = "do less!"; var b = "do less, write more!"; assert.equal( QUnit.diff( a, b ), "do less, write more!", "QUnit.diff( 'do less!', 'do less, write more!' )" ); }); QUnit.test( "removals", function( assert ) { var a = "do less, write more!"; var b = "do less!"; assert.equal( QUnit.diff( a, b ), "do less, write more!", "QUnit.diff( 'do less, write more!', 'do less!' )" ); }); QUnit.test( "equality shifts", function( assert ) { // ABAC -> ABAC var a = "AC"; var b = "ABAC"; assert.equal( QUnit.diff( a, b ), "ABAC" ); }); QUnit.test( "test with line mode on long strings", function( assert ) { var a = "QUnit is a powerful, easy-to-use JavaScript unit testing framework. " + "It's used by the jQuery, jQuery UI and jQuery Mobile projects and is " + "capable of testing any generic JavaScript code, including itself!"; var b = "QUnit is a very powerful, easy-to-use JavaScript unit testing framework. " + "It's used by the jQuery Core, jQuery UI and jQuery Mobile projects and is " + "capable of testing any JavaScript code, including itself!" + "QUnit was originally developed by John Resig as part of jQuery. In 2008 " + "it got its own home, name and API documentation, allowing others to use it " + "for their unit testing as well. At the time it still depended on jQuery. " + "A rewrite in 2009 fixed that, and now QUnit runs completely standalone. "; assert.equal( QUnit.diff( a, b ), "QUnit is a very powerful, easy-to-use " + "JavaScript unit testing framework. It's used by the jQuery " + "Core, jQuery UI and jQuery Mobile projects and is capable of" + " testing any generic JavaScript code, including " + "itself!" + "QUnit was originally developed by John Resig as part of jQuery. In " + "2008 it got its own home, name and API documentation, allowing others to" + " use it for their unit testing as well. At the time it still depended on" + " jQuery. A rewrite in 2009 fixed that, and now QUnit runs completely " + "standalone. " ); }); QUnit.test( "simplified diffs", function( assert ) { assert.equal( QUnit.diff( "BXYD", "AXYC" ), "BXYDAXYC", "return is not BAXYDC" ); assert.equal( QUnit.diff( "XD", "AXC" ), "XDAXC", "return is not AXDC" ); assert.equal( QUnit.diff( "A BC ", " B" ), "A BC ", "Swap insertions for deletions if diff is reversed" ); assert.equal( QUnit.diff( "abcxxx", "xxxdef" ), "abcxxxdef" ); assert.equal( QUnit.diff( "xxxabc", "defxxx" ), "defxxxabc" ); }); QUnit.test( "equal values", function( assert ) { assert.equal( QUnit.diff( "abc", "abc" ), "abc" ); assert.equal( QUnit.diff( "", "" ), "" ); }); qunit-1.22.0/test/reporter-html/legacy-markup.html000066400000000000000000000006731266310060400221520ustar00rootroot00000000000000 QUnit HTML Reporter - Legacy Markup

          Tests

            qunit-1.22.0/test/reporter-html/no-qunit-element.html000066400000000000000000000010731266310060400226050ustar00rootroot00000000000000 QUnit HTML Reporter - No Markup qunit-1.22.0/test/reporter-html/reporter-html.js000066400000000000000000000074621266310060400216700ustar00rootroot00000000000000// The following tests need to run on their respective order QUnit.config.reorder = false; QUnit.module( "", { beforeEach: function() { }, afterEach: function( assert ) { // We can't use ok(false) inside script tags since some browsers // don't evaluate script tags inserted through innerHTML after domready. // Counting them before/after doesn't cover everything either as qunit-modulefilter // is created before any test is ran. So use ids instead. if ( document.getElementById( "qunit-unescaped-module" ) ) { // This can either be from in #qunit-modulefilter or #qunit-testresult assert.ok( false, "Unescaped module name" ); } if ( document.getElementById( "qunit-unescaped-test" ) ) { assert.ok( false, "Unescaped test name" ); } if ( document.getElementById( "qunit-unescaped-assertion" ) ) { assert.ok( false, "Unescaped test name" ); } } }); QUnit.test( "", function( assert ) { assert.expect( 1 ); assert.ok( true, "" ); }); QUnit.module( "display test info" ); QUnit.test( "running test name displayed", function( assert ) { assert.expect( 2 ); var displaying = document.getElementById( "qunit-testresult" ); assert.ok( /running test name displayed/.test( displaying.innerHTML ), "Expect test name to be found in displayed text" ); assert.ok( /display test info/.test( displaying.innerHTML ), "Expect module name to be found in displayed text" ); }); QUnit.module( "timing", { getPreviousTest: function( assert ) { return document.getElementById( "qunit-test-output-" + assert.test.testId ) .previousSibling; }, filterClass: function( elements ) { var i; for ( i = 0; i < elements.length; i++ ) { if ( /(^| )runtime( |$)/.test( elements[ i ].className ) ) { return elements[ i ]; } } }, afterEach: function( assert ) { var done; if ( this.delayNextSetup ) { this.delayNextSetup = false; done = assert.async(); setTimeout(function() { done(); }, 101 ); } } }); QUnit.test( "setup", function( assert ) { assert.expect( 0 ); this.delayNextSetup = true; }); QUnit.test( "basics", function( assert ) { assert.expect( 1 ); var previous = this.getPreviousTest( assert ), runtime = this.filterClass( previous.getElementsByTagName( "span" ) ); assert.ok( /^\d+ ms$/.test( runtime.innerHTML ), "Runtime reported in ms" ); }); QUnit.test( "values", function( assert ) { assert.expect( 2 ); var basics = this.getPreviousTest( assert ), setup = basics.previousSibling; basics = this.filterClass( basics.getElementsByTagName( "span" ) ); setup = this.filterClass( setup.getElementsByTagName( "span" ) ); assert.ok( parseInt( basics.innerHTML, 10 ) < 100, "Fast runtime for trivial test" ); assert.ok( parseInt( setup.innerHTML, 10 ) > 100, "Runtime includes beforeEach" ); }); QUnit.module( "source" ); QUnit.test( "setup", function( assert ) { assert.expect( 0 ); }); QUnit.test( "logs location", function( assert ) { var previous = document.getElementById( "qunit-test-output-" + assert.test.testId ) .previousSibling; var source = previous.lastChild; var stack = QUnit.stack(); // Verify QUnit supported stack trace if ( !stack ) { assert.equal( /(^| )qunit-source( |$)/.test( source.className ), false, "Don't add source information on unsupported environments" ); return; } assert.ok( /(^| )qunit-source( |$)/.test( source.className ), "Source element exists" ); assert.equal( source.firstChild.innerHTML, "Source: " ); // test/reporter-html/reporter-html.js is a direct reference to this test file assert.ok( /\/test\/reporter-html\/reporter-html\.js\:\d+/.test( source.innerHTML ), "Source references to the current file and line number" ); }); qunit-1.22.0/test/reporter-html/single-testid.html000066400000000000000000000004371266310060400221620ustar00rootroot00000000000000 QUnit Main Test Suite
            qunit-1.22.0/test/reporter-html/single-testid.js000066400000000000000000000007611266310060400216320ustar00rootroot00000000000000QUnit.config.testId = [ "2e48c6fa", "9ccf6855" ]; QUnit.test( "Check for changed header after running filtered test", function( assert ) { var html = document.getElementById( "qunit-filteredTest" ).innerHTML; var result = html.match( /Rerunning selected tests\: 2e48c6fa\, 9ccf6855/ ); assert.ok( result ); }); QUnit.test( "Check for link to clear filter", function( assert ) { var html = document.getElementById( "qunit-clearFilter" ).innerHTML; assert.equal( html, "Run all tests" ); }); qunit-1.22.0/test/setTimeout.html000066400000000000000000000004531266310060400167430ustar00rootroot00000000000000 QUnit Fake setTimeout Test Suite
            qunit-1.22.0/test/setTimeout.js000066400000000000000000000006631266310060400164160ustar00rootroot00000000000000(function( window ) { QUnit.module( "Module that mucks with time", { beforeEach: function() { this.setTimeout = window.setTimeout; window.setTimeout = function() {}; }, afterEach: function() { window.setTimeout = this.setTimeout; } }); QUnit.test( "just a test", function( assert ) { assert.ok( true ); }); QUnit.test( "just a test", function( assert ) { assert.ok( true ); }); }( (function() { return this; })() )); qunit-1.22.0/test/stack-errors.html000066400000000000000000000004211266310060400172130ustar00rootroot00000000000000 QUnit Main Test Suite
            qunit-1.22.0/test/stack-errors.js000066400000000000000000000042671266310060400166770ustar00rootroot00000000000000/* globals polluteGlobal: true */ // No pollution QUnit.test( "globals", function( assert ) { QUnit.config.noglobals = true; polluteGlobal = 1; assert.expect( 0 ); }); // Failing test QUnit.test( "failing", function( assert ) { assert.equal( "foo", "bar" ); }); // No assertions fail QUnit.test( "no assertions", function() { // nothing }); // start error inside of a test context QUnit.test( "QUnit.start()", function() { QUnit.start(); }); // Died on test QUnit.test( "dies on test", function() { throw new Error( "foo" ); }); // beforeEach die QUnit.module( "beforeEach fail", { beforeEach: function() { throw new Error( "foo" ); } }); QUnit.test( "module fails", function() { // ... }); // afterEach die QUnit.module( "afterEach fail", { afterEach: function() { throw new Error( "bar" ); } }); QUnit.test( "module fails", function() { // ... }); // assert.async post-resolution assertions fail QUnit.module( "assertions fail after assert.async flows are resolved" ); QUnit.test( "assert.ok", function( assert ) { assert.async()(); assert.ok( true, "This assertion should pass but have a failure logged before it" ); }); QUnit.test( "assert.equal", function( assert ) { assert.async()(); assert.equal( 1, 1, "This assertion should pass but have a failure logged before it" ); }); QUnit.test( "assert.throws", function( assert ) { assert.async()(); assert.throws(function() { throw new Error( "foo" ); }, "This assertion should pass but have a failure logged before it" ); }); QUnit.module( "globals" ); // start error outside of a test context setTimeout(function() { QUnit.start(); }, 0 ); // pushFailure outside of a test context setTimeout(function() { QUnit.pushFailure( true ); }, 0 ); // Assertion outside of a test context setTimeout(function() { QUnit.ok( true ); }, 0 ); // Trigger window.onerror setTimeout(function() { throw new Error( "foo" ); }, 0 ); // DEPRECATED: To be removed in QUnit 2.0.0 // Trigger warnings by replacing the logging callbacks QUnit.begin = function() {}; QUnit.done = function() {}; QUnit.log = function() {}; QUnit.testStart = function() {}; QUnit.testDone = function() {}; QUnit.moduleStart = function() {}; QUnit.moduleDone = function() {}; qunit-1.22.0/test/startError.html000066400000000000000000000012311266310060400167430ustar00rootroot00000000000000 QUnit Start Error Test Suite
            qunit-1.22.0/test/startError.js000066400000000000000000000011151266310060400164140ustar00rootroot00000000000000/*global autostartStartError, tooManyStartsError */ QUnit.module( "global start unrecoverable errors" ); QUnit.test( "start() throws when QUnit.config.autostart === true", function( assert ) { assert.expect( 1 ); assert.equal( autostartStartError.message, "Called start() outside of a test context when QUnit.config.autostart was true" ); }); QUnit.test( "Throws after calling start() too many times outside of a test context", function( assert ) { assert.expect( 1 ); assert.equal( tooManyStartsError.message, "Called start() outside of a test context too many times" ); }); qunit-1.22.0/test/string-filter.html000066400000000000000000000005531266310060400173730ustar00rootroot00000000000000 QUnit String Filtering Test Suite
            qunit-1.22.0/test/string-filter.js000066400000000000000000000005761266310060400170500ustar00rootroot00000000000000QUnit.module( "QUnit.config.filter" ); QUnit.test( "foo test should be run", function( assert ) { assert.ok( true, "foo test should be run" ); }); QUnit.test( "bar test should be run", function( assert ) { assert.ok( true, "bar test should be run" ); }); QUnit.test( "foo|bar test should not be run", function( assert ) { assert.ok( false, "baz test should not be run" ); });