netzke-testing 0.12.3 → 1.0.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. data/CHANGELOG.md +1 -0
  2. data/Gemfile +2 -3
  3. data/LICENSE.txt +2 -21
  4. data/README.md +8 -7
  5. data/app/assets/javascripts/netzke/testing/helpers/actions.js.coffee +3 -0
  6. data/app/assets/javascripts/netzke/testing/helpers/grid.js.coffee +16 -10
  7. data/app/assets/javascripts/netzke/testing/helpers/queries.js.coffee +16 -10
  8. data/app/assets/javascripts/netzke/testing/mocha/mocha.js +1948 -723
  9. data/app/assets/stylesheets/netzke/testing/mocha/mocha.css +53 -14
  10. data/lib/netzke/testing/version.rb +1 -1
  11. data/spec/rails_app/app/assets/javascripts/application.js +0 -3
  12. data/spec/rails_app/log/test.log +1699 -0
  13. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/-o-YaVl0T7eDJ9OrOV9RGwqVsIWZFHy_ANOiYAzd5xs.cache +2 -0
  14. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/0ytTgUbUdaNC4f2HwTF9KPoNfEPoelbdxEEonmXcFY8.cache +1 -0
  15. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/2N-wUxBH2S0I0Vdtr2WyUGI2-QP0sx654c316Tle0Y8.cache +1 -0
  16. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/2zr8-0Qy8xKJVKL6YDLMlxXVM3UR6y6LsSiMHIzTzZk.cache +108 -0
  17. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/4iunyDf9YIjBbUNGIWAUzBvRnjmDZHzub-ySK64tNiM.cache +1 -0
  18. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/5TvecBl1M5rB_WmUw9C6hq0IQscGnYdoEo54Nu7v-ro.cache +2 -0
  19. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/5hZUq2VTJHMVrffS80-YXBfIY0KRFcqIczHYxwEq1O4.cache +1 -0
  20. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/65vMYBj8h-G6ncIkZb0XGK0AkccVIg3nRZ49oVQD5pk.cache +1 -0
  21. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/6QkXlYVD0H86cOJuY4YsQwTsh9R-Xuei5H9g4AI6qLc.cache +0 -0
  22. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/6oNBMh4vlr3nAg_zScBsRwoNfCUCwuMfxEsMExGEZ58.cache +1 -0
  23. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/7AdXoQpyUKCU1NajQOdwfICU7NNx66_CjHVZaEPMie8.cache +0 -0
  24. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/7SehKiu3k-7sWqyekPGAAlRRuG6zi4zImkGSsFZY5Hg.cache +1 -0
  25. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/8hCQLzcxr1YiyVw4yQnLRkyjUeJPrBL68jaO0PBTVlc.cache +1 -0
  26. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/8jJcLw-iylJTyRci7ICaZfve0wKGZehtTiFdrRGRSWY.cache +1 -0
  27. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/9tmtKoc6UHxqorCSuhwhFQiEoEH8cz4oCJxJRhz6pyg.cache +0 -0
  28. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/BOuRxAKchk00bl_QQch7_NnI7jvFEq1o3uicHp8nTlA.cache +0 -0
  29. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/BT0NlH5e-SMXSFoONZqKAfPuxkCaZq9t03GG-tbqf2A.cache +1 -0
  30. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/C3sjqQ7IyyEmT_fAJspAqXDgyZTcglD-YbgpfYb0zb0.cache +0 -0
  31. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/CD3FvmKB9PfopODzH8rXDZqEJfXsuXpcIezK4WAZaBE.cache +3 -0
  32. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/CeuWmVqZWhwboK9KGiK-VGwtZYLrqCRI93rvJQIBTXs.cache +2 -0
  33. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/DBSkqZmCLWpPoAEFi-Reo4ICD1LTRmbh9XIbohHM47A.cache +1 -0
  34. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/DYPq9tb1XNCaTXOoFjisXo-wigP-JudWtJeUfiqW0m8.cache +0 -0
  35. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/EvRmssKzs-UegvrZuPi_Lk3cjNfMNSpr9xx0EshSNJo.cache +1 -0
  36. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/GGgLpQ8oW-77EMBuKwruBXwHesf4NZu1Y3HiI1WMWDI.cache +1 -0
  37. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Gg7p64pI2umKw5BpAglJ9RimpnYcL34FWDWFaKOz62A.cache +106 -0
  38. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Hdq71Baun4hNkGv_d0fbdq2p26likuSnCMUFkyDCFws.cache +0 -0
  39. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/HsEibo-dY2kOc-nAvEmkUrm5PWK9cU-uNKra6itU_zw.cache +1 -0
  40. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/I-Yu0SmONUDD_GaeBx_r3qR4yHoQ3aqFWAFmyHRL-zg.cache +3 -0
  41. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/JCrLlkFPuQLUZyQYKL6_MMNgNGfz2-E323acSOSrt5k.cache +0 -0
  42. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/JVTgVQi9d98c1SokUVnZ3aomHqJQbXZioi7g3ahm5Qc.cache +1 -0
  43. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Jeh27aEr_ZWMJZqyCSIczaLGHt_DhIWwdukzqrGWa9w.cache +1 -0
  44. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Jl60NqnWqOdABtuYSE5ZzrcmVseJlSZF2HNFS59FpY0.cache +0 -0
  45. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/LRGtOZMxHuSVunCvJ63sVbxqDeAajlRaa6KCmyrAIqA.cache +1 -0
  46. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/MBeoy0q5Gyw0x-mJzpHAPysisACug1l0TL2vMkIyrOw.cache +2 -0
  47. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/NOfTwd49eb86ANBlpdBndMO4ImhsuP1GRpxDAgwAfx4.cache +3 -0
  48. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/N_20zEdBHyIIxuOVGv8q7BS0FFrNQ39k3CPwpMrSSYo.cache +1 -0
  49. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/NvWH8wh5mUg78k87c85rP3NlI7nTtwzUxyYJOS5qiQI.cache +3 -0
  50. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/OUo2VS0xOErRnbS1XuvOarZVKs-rq2NnztsajD5Fj10.cache +1 -0
  51. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/OcWPwVd4Kv47Guuuu80JnFKoHk_okLZ7iRvgwzoZUHA.cache +0 -0
  52. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Oyz8XfIMJQ3g6O0KjipLd4Kuem3B6xG5oyaHeeGmt-M.cache +1 -0
  53. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/P8_qz5Ye3lrk5y6ujvXaW6SDLuGDLb262IusJX4dIVE.cache +0 -0
  54. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/QW--4tkWOk_XF1gyoYZDRCyDehMpmnuVayrZKMVqZ7M.cache +2 -0
  55. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/RLcVI-Rt6DzaGkFHDbXe0tx4wEBGaCRvnY_EycIsnuU.cache +0 -0
  56. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Sw3U88tBBMOJoXaXESCBCsrzso8r9YSJvEKU8JZ0BNo.cache +2 -0
  57. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/TITv6kKDIwWXMb3nMX-OmOM1MM1mmsPg1LcooNVGG9Y.cache +0 -0
  58. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/U4FMxMcT2pVuJgZ0D8A0QRXYk9HkjcVtq5UUkSZo4T8.cache +3 -0
  59. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Ue75Stcow3jQUURIsInN3UD7RoV7zwQ9QJPNgjMhbxA.cache +1 -0
  60. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/VQZge3nKTLKNH6_2qlFSj9_U6tszzMCCb3Dt8zlJLEs.cache +3 -0
  61. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/WobYryqMWrJJ1FO5WUyND9wkonpQ-k0TFRSdlnJxQpA.cache +2 -0
  62. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/WukfMkB8j3K5Frrr4G-QucfwskKB-pt4ZVVOF9ctkWc.cache +1 -0
  63. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Xf7wDNUuYgkKRSiNrAXnd202ySmPUYH7Bz1uh7NkQ3E.cache +1 -0
  64. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/Z-Roe_JsgiZpkKE_8qoVGx3TMCSMOoPhPovT2mkAz80.cache +1 -0
  65. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/ZdXCd9BmMKvjHoZpHQB-YyFuGeaVXUMrcAu2LxlrUD8.cache +1 -0
  66. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/ZyKlKxGLZqtOZ1B5xqNJAy5I17yM_tX3zg_pk0fthZI.cache +1 -0
  67. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/_DDbKzjQQKg6BAaLR73pixBhcBdqyJWKjrqiRBqz2eg.cache +2 -0
  68. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/aEe8dZBMoJN_L7dQ9O90etuJwhYBZaxRLXPLkI4lwXU.cache +3 -0
  69. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/apG5zQKUGPau7gI490daUVsPkALVV2jmMhHowsEv_uc.cache +0 -0
  70. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/bigAjWfxtrTpxpryFTjOzuZDpQN1TQn2LgyC2UX608A.cache +3 -0
  71. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/cC4aVV6WxpZQ1JV9tiqJ7c1SHial-X4fXVKIL_0cSNk.cache +1 -0
  72. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/eCXlP45Wc2ERhH-FkBXtTDoS_WnSRhTr0gswBg-RRfY.cache +0 -0
  73. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/eHpCOzyMXB_heVVMB3p9CZ1SETj9OoVhd_4vmYxVjKs.cache +1 -0
  74. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/ez37HtozJjtuxDJ7zpMQKEA8mmmXbeo_53mQclPL4aw.cache +0 -0
  75. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/hAQjVJYrPwre3Y-BQAhTqMEnxoGw0-4b7qhLyD52dAg.cache +0 -0
  76. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/hhhcBN5dBU2-wGGwdUrlOVzAEf9gVcyGUcbFgHPMDpw.cache +1 -0
  77. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/j0GxKFTTXVLhgLP0leVrUzi9J5Vk0iv5I3Etnvf1hKo.cache +3 -0
  78. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/jJtmBEK1VRWyzZxlArUpOZ_eLVOhOnVYeZqq0bIsyEc.cache +0 -0
  79. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/jOCRCYDoaF271HXVDnj1U6FBFjKDD49spXEobwxFHoY.cache +1 -0
  80. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/jUcv_4GPi-D5jFNvcEkNKl60eXmJV3voiuFymwpMEZc.cache +2 -0
  81. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/jbxonAySrT1xyOeSqtRWBQz1vraED9_QfuVtGgPSPgw.cache +0 -0
  82. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/kAGXQElMVnAPNM2Gp_-yrWrZ53cEVOJg4rSrqelTa74.cache +1 -0
  83. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/kAtTT8eQbqVAPn3r0CLbsK1K84vbYpAJO-cDeriRN_4.cache +1 -0
  84. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/ll8jtsHipPGjByuvi5Cfqa4nz7g5si6q5arg9iK3hB8.cache +1 -0
  85. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/m7qNudRV2a_8us3h_WQ0MenSTjES5wu_R4CiBsCFbIQ.cache +1 -0
  86. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/nONU3x7H75eQ2Xaw3GMC0pKEWPyMtRxMJ9Y-l0Ayq6M.cache +33 -0
  87. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/nY3J4dkHhQje-2nXPqoqWH-9rzW2pK2HwaMfQbsfoIM.cache +1 -0
  88. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/oKqCIaSUcvj99uxAW-9dg1RT6kfhkaQXnWbKgR05uXs.cache +1 -0
  89. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/pOy3DFQaPfNEszqVjOsUM2d4fMorRrlDjUZCBLXfdWY.cache +1 -0
  90. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/qA9aqXy3Z7iNVnH_S00EnHsObKjU4gBjfAi0LsK31ec.cache +1 -0
  91. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/qoifXlMbps0buakSToSMKGUzijDll20f2phfUWvTveI.cache +0 -0
  92. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/qp_LekwrmrrQbsDBfH1bncgh22iq3wpSny6qolOBxrg.cache +1 -0
  93. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/rAhxfb71OMNOOASBBxbuJxVZWEHebArv3N7bzdiPqmk.cache +0 -0
  94. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/rED6iTVwl15Rj1k2LO38e0eCQitK-lMbvwpdz3G4mkU.cache +0 -0
  95. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/rqAXfU5_qJvYOuvQFsv4-Bsr993COgaOPKutmISI9Ac.cache +3 -0
  96. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/sbkgYxjKRuP8wk525y6VBNm00JCRW3rELt3mRvcfhUo.cache +13 -0
  97. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/srA1w0iCmsgYQvL_8ETHApAWdkqP7mRv3yRsts_8oks.cache +3 -0
  98. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/t3yuTQREe-_jlUIsoNfmFjrCncCUapXxX196qXhdtHM.cache +0 -0
  99. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/uytA8PznWSrTlIdRDkZ0cbhA72VDTsEeaj1MEK3XS18.cache +2 -0
  100. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/vWTf1CyFrHGKFakqn3HWhHKawzFnpTRXP0NaqPKXZj8.cache +2 -0
  101. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/vhuMIsvEbEL-zaLp1RZEIWd3sZDg4gCw6Am1tFfZEhY.cache +0 -0
  102. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/ynpdaUV6dj37pI5aP5Ki9BLQf9UEcqkc_yL9fCeoOU0.cache +0 -0
  103. data/spec/rails_app/tmp/cache/assets/test/sprockets/v3.0/yr5lGfFwzSXWHCuM8LahKsYDRGqJt4eMSPcc7WU1gkE.cache +0 -0
  104. metadata +213 -28
  105. checksums.yaml +0 -7
  106. data/netzke-testing.gemspec +0 -21
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ * Fix `wait` hanging at server exception
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '4.2.0'
5
+ gem 'rails', '~>4.2.0'
6
6
  gem 'sqlite3'
7
7
  gem 'yard'
8
8
  gem 'rake'
@@ -15,8 +15,7 @@ group :test do
15
15
  end
16
16
 
17
17
  group :development, :test do
18
- gem 'byebug'
19
18
  gem 'web-console', '~> 2.0'
20
19
  gem 'pry-rails'
21
- gem 'netzke-core', github: 'netzke/netzke-core'
20
+ gem 'netzke-core', github: 'netzke/netzke-core', branch: 'master'
22
21
  end
data/LICENSE.txt CHANGED
@@ -1,22 +1,3 @@
1
- Copyright (c) 2015 Max Gorin
1
+ Copyright (c) 2009-2015 Good Bit Labs Limited
2
2
 
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3
+ GPLv3 License: http://www.gnu.org/licenses/gpl-3.0.en.html
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Netzke Testing [![Gem Version](https://fury-badge.herokuapp.com/rb/netzke-testing.png)](http://badge.fury.io/rb/netzke-testing)
1
+ # Netzke Testing [![Build Status](https://travis-ci.org/netzke/netzke-testing.png?branch=master)](https://travis-ci.org/netzke/netzke-testing) [![Gem Version](https://fury-badge.herokuapp.com/rb/netzke-testing.png)](http://badge.fury.io/rb/netzke-testing)
2
2
 
3
3
  This gem helps with development and testing of Netzke components. In parcticular, it helps you with:
4
4
 
@@ -7,7 +7,7 @@ This gem helps with development and testing of Netzke components. In parcticular
7
7
 
8
8
  Usage:
9
9
 
10
- gem 'netzke_testing'
10
+ gem 'netzke-testing'
11
11
 
12
12
  ## Isolated component development
13
13
 
@@ -157,14 +157,15 @@ Asynchronous helpers like `wait` can either call the provided callback function,
157
157
  .then
158
158
  doSomeMore()
159
159
 
160
+ ## Note on headless browser testing
161
+
162
+ No headless browsers like PhantomJS or WebKit were used for testing because our tests rely heavily on HTMLElement's `click` event, which is only properly implemented in "real" browsers like Firefox (which is what we use).
163
+
160
164
  ## Requirements
161
165
 
162
- * Ruby >= 2.0.0
166
+ * Ruby >= 1.9.3
163
167
  * Rails ~> 4.2.0
164
168
  * Ext JS = 5.1.0
165
169
 
166
170
  ---
167
- © 2015 [Max Gorin](https://twitter.com/mxgrn), released under the MIT license (see LICENSE).
168
-
169
- **Note** that Ext JS is licensed [differently](http://www.sencha.com/products/extjs/license/), and you may need to
170
- purchase a commercial license in order to use it in your projects!
171
+ Copyright (c) 2009-2015 [Good Bit Labs Limited](http://goodbitlabs.com/), released under the GPLv3 license
@@ -5,6 +5,9 @@ Ext.Ajax.on 'beforerequest', ->
5
5
  Ext.Ajax.on 'requestcomplete', ->
6
6
  Netzke.ajaxCount -= 1
7
7
 
8
+ Ext.Ajax.on 'requestexception', ->
9
+ Netzke.ajaxCount -= 1
10
+
8
11
  Ext.apply window,
9
12
  # Waits for all AJAX activity to stop, then calls the optional callback. If no callback was specified, returns a
10
13
  # promise. The first argument can be a number of milliseconds to wait before starting to listen to the AJAX activity
@@ -57,12 +57,7 @@ Ext.apply window,
57
57
 
58
58
  valuesInColumn: (name, params = {}) ->
59
59
  grid = params.in || @grid()
60
- out = []
61
- i = 0
62
- grid.getStore().each (r) ->
63
- out.push valueInCell(name, i++, params)
64
-
65
- out
60
+ valueInCell(name, i, params) for i in [0..grid.getStore().getCount()-1]
66
61
 
67
62
  # Example:
68
63
  # valueInCell 'author__name', 2
@@ -78,7 +73,7 @@ Ext.apply window,
78
73
  selectAllRows: (params) ->
79
74
  params ?= {}
80
75
  grid = params.in || @grid()
81
- grid.getSelectionModel().selectAll()
76
+ grid.getSelectionModel().selectRange(0, grid.getStore().getCount() - 1)
82
77
 
83
78
  # rowDisplayValues in: grid('Books'), of: grid('Books').getStore().last()
84
79
  # Without parameters, assumes the first found grid and the selected row
@@ -94,7 +89,7 @@ Ext.apply window,
94
89
  i = -1
95
90
  return Ext.Array.map(Ext.DomQuery.select('table[data-recordid="'+record.internalId+'"] tbody tr td div'), (cell) ->
96
91
  i++
97
- if visibleColumns[i].attrType == 'boolean'
92
+ if visibleColumns[i].type == 'boolean'
98
93
  record.get(visibleColumns[i].name)
99
94
  else
100
95
  cell.innerHTML
@@ -106,7 +101,7 @@ Ext.apply window,
106
101
  selectLastRow: (params) ->
107
102
  params ?= {}
108
103
  grid = params.in || @grid()
109
- grid.getSelectionModel().select(grid.getStore().last())
104
+ grid.getSelectionModel().select(grid.getStore().getCount() - 1)
110
105
 
111
106
  # Examples:
112
107
  # selectFirstRow()
@@ -114,7 +109,7 @@ Ext.apply window,
114
109
  selectFirstRow: (params) ->
115
110
  params ?= {}
116
111
  grid = params.in || @grid()
117
- grid.getSelectionModel().select(grid.getStore().first())
112
+ grid.getSelectionModel().select(0)
118
113
 
119
114
  # Examples:
120
115
  # selectRow 5
@@ -138,3 +133,14 @@ Ext.apply window,
138
133
  g = g || @grid()
139
134
  e = g.getPlugin('celleditor')
140
135
  e.completeEdit()
136
+
137
+ # Double clicks currently selected row
138
+ dblclickRow: (params = {}) ->
139
+ grid = params.in || @grid()
140
+ record = grid.getSelectionModel().getSelection()[0]
141
+ rowEl = Ext.DomQuery.select('table[data-recordid="'+record.internalId+'"] tbody tr td div')[0]
142
+ event = new MouseEvent 'dblclick',
143
+ view: window
144
+ bubbles: true
145
+ cancelable: true
146
+ rowEl.dispatchEvent(event)
@@ -10,11 +10,16 @@ Ext.apply window,
10
10
  panelWithContent: (text) ->
11
11
  Ext.DomQuery.select("div.x-panel-body:contains(" + text + ")")[0] || 'panel with content ' + text
12
12
 
13
- button: (text) ->
14
- button = Ext.ComponentQuery.query("button{isVisible(true)}[text='"+text+"']")[0]
15
- button ||= Ext.ComponentQuery.query("button{isVisible(true)}[tooltip='"+text+"']")[0]
13
+ button: (text, params = {}) ->
14
+ context = params.within || Ext.ComponentQuery
15
+
16
+ button = context.query("button{isVisible(true)}[text='"+text+"']")[0]
17
+ button ||= context.query("button{isVisible(true)}[tooltip='"+text+"']")[0]
16
18
  button || "button " + text
17
19
 
20
+ panel: (name) ->
21
+ Ext.getCmp(name)
22
+
18
23
  tool: (type) ->
19
24
  Ext.ComponentQuery.query("tool{isVisible(true)}[type='"+type+"']")[0] || 'tool ' + type
20
25
 
@@ -32,26 +37,26 @@ Ext.apply window,
32
37
 
33
38
  combobox: (name) ->
34
39
  Ext.ComponentQuery.query("combo{isVisible(true)}[name='"+name+"']")[0] ||
35
- 'combobox ' + name
40
+ "combobox '#{name}'"
36
41
 
37
42
  icon: (tooltip) ->
38
43
  Ext.DomQuery.select('img[data-qtip="'+tooltip+'"]')[0] || 'icon ' + tooltip
39
44
 
40
45
  textfield: (name) ->
41
46
  Ext.ComponentQuery.query("textfield{isVisible(true)}[name='"+name+"']")[0] ||
42
- 'textfield ' + name
47
+ "textfield '#{name}'"
43
48
 
44
49
  numberfield: (name) ->
45
50
  Ext.ComponentQuery.query("numberfield{isVisible(true)}[name='"+name+"']")[0] ||
46
- 'numberfield ' + name
51
+ "numberfield '#{name}'"
47
52
 
48
53
  datefield: (name) ->
49
54
  Ext.ComponentQuery.query("datefield{isVisible(true)}[name='"+name+"']")[0] ||
50
- 'datefield ' + name
55
+ "datefield '#{name}'"
51
56
 
52
57
  xdatetime: (name) ->
53
58
  Ext.ComponentQuery.query("xdatetime{isVisible(true)}[name='"+name+"']")[0] ||
54
- 'xdatetime ' + name
59
+ "xdatetime '#{name}'"
55
60
 
56
61
  textFieldWith: (text) ->
57
62
  _componentLike "textfield", "value", text
@@ -69,7 +74,7 @@ Ext.apply window,
69
74
  Ext.WindowMgr.getActive()
70
75
 
71
76
  dateTimeFieldWith: (value) ->
72
- res = 'xdatetime with value ' + value
77
+ res = "xdatetime with value '#{value}'"
73
78
  Ext.each Ext.ComponentQuery.query('xdatetime'), (item) ->
74
79
  if item.getValue().toString() == (new Date(value)).toString()
75
80
  res = item
@@ -77,7 +82,7 @@ Ext.apply window,
77
82
  res
78
83
 
79
84
  dateFieldWith: (value) ->
80
- res = 'datefield with value ' + value
85
+ res = "datefield with value '#{value}'"
81
86
  Ext.each Ext.ComponentQuery.query('datefield'), (item) ->
82
87
  if item.getValue().toString() == (new Date(value)).toString()
83
88
  res = item
@@ -98,5 +103,6 @@ Ext.apply window,
98
103
 
99
104
  _componentLike:(type,attr,value)->
100
105
  Ext.ComponentQuery.query(type+'['+attr+'='+value+']')[0] || type + " with " + attr + " '" + value + "'"
106
+
101
107
  # alias
102
108
  window.anywhere = window.somewhere
@@ -1,6 +1,5 @@
1
1
  ;(function(){
2
2
 
3
-
4
3
  // CommonJS require()
5
4
 
6
5
  function require(p){
@@ -49,16 +48,15 @@ require.relative = function (parent) {
49
48
 
50
49
 
51
50
  require.register("browser/debug.js", function(module, exports, require){
52
-
53
51
  module.exports = function(type){
54
52
  return function(){
55
-
56
53
  }
57
54
  };
55
+
58
56
  }); // module: browser/debug.js
59
57
 
60
58
  require.register("browser/diff.js", function(module, exports, require){
61
- /* See license.txt for terms of usage */
59
+ /* See LICENSE file for terms of use */
62
60
 
63
61
  /*
64
62
  * Text diff implementation.
@@ -75,6 +73,7 @@ require.register("browser/diff.js", function(module, exports, require){
75
73
  * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
76
74
  */
77
75
  var JsDiff = (function() {
76
+ /*jshint maxparams: 5*/
78
77
  function clonePath(path) {
79
78
  return { newPos: path.newPos, components: path.components.slice(0) };
80
79
  }
@@ -89,22 +88,21 @@ var JsDiff = (function() {
89
88
  }
90
89
  function escapeHTML(s) {
91
90
  var n = s;
92
- n = n.replace(/&/g, "&");
93
- n = n.replace(/</g, "&lt;");
94
- n = n.replace(/>/g, "&gt;");
95
- n = n.replace(/"/g, "&quot;");
91
+ n = n.replace(/&/g, '&amp;');
92
+ n = n.replace(/</g, '&lt;');
93
+ n = n.replace(/>/g, '&gt;');
94
+ n = n.replace(/"/g, '&quot;');
96
95
 
97
96
  return n;
98
97
  }
99
98
 
100
-
101
- var fbDiff = function(ignoreWhitespace) {
99
+ var Diff = function(ignoreWhitespace) {
102
100
  this.ignoreWhitespace = ignoreWhitespace;
103
101
  };
104
- fbDiff.prototype = {
102
+ Diff.prototype = {
105
103
  diff: function(oldString, newString) {
106
104
  // Handle the identity case (this is due to unrolling editLength == 0
107
- if (newString == oldString) {
105
+ if (newString === oldString) {
108
106
  return [{ value: newString }];
109
107
  }
110
108
  if (!newString) {
@@ -199,7 +197,7 @@ var JsDiff = (function() {
199
197
  if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
200
198
  return true;
201
199
  } else {
202
- return left == right;
200
+ return left === right;
203
201
  }
204
202
  },
205
203
  join: function(left, right) {
@@ -210,26 +208,45 @@ var JsDiff = (function() {
210
208
  }
211
209
  };
212
210
 
213
- var CharDiff = new fbDiff();
211
+ var CharDiff = new Diff();
214
212
 
215
- var WordDiff = new fbDiff(true);
216
- WordDiff.tokenize = function(value) {
213
+ var WordDiff = new Diff(true);
214
+ var WordWithSpaceDiff = new Diff();
215
+ WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) {
217
216
  return removeEmpty(value.split(/(\s+|\b)/));
218
217
  };
219
218
 
220
- var CssDiff = new fbDiff(true);
219
+ var CssDiff = new Diff(true);
221
220
  CssDiff.tokenize = function(value) {
222
221
  return removeEmpty(value.split(/([{}:;,]|\s+)/));
223
222
  };
224
223
 
225
- var LineDiff = new fbDiff();
224
+ var LineDiff = new Diff();
226
225
  LineDiff.tokenize = function(value) {
227
- return value.split(/^/m);
226
+ var retLines = [],
227
+ lines = value.split(/^/m);
228
+
229
+ for(var i = 0; i < lines.length; i++) {
230
+ var line = lines[i],
231
+ lastLine = lines[i - 1];
232
+
233
+ // Merge lines that may contain windows new lines
234
+ if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
235
+ retLines[retLines.length - 1] += '\n';
236
+ } else if (line) {
237
+ retLines.push(line);
238
+ }
239
+ }
240
+
241
+ return retLines;
228
242
  };
229
243
 
230
244
  return {
245
+ Diff: Diff,
246
+
231
247
  diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
232
248
  diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
249
+ diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
233
250
  diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
234
251
 
235
252
  diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
@@ -237,16 +254,16 @@ var JsDiff = (function() {
237
254
  createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
238
255
  var ret = [];
239
256
 
240
- ret.push("Index: " + fileName);
241
- ret.push("===================================================================");
242
- ret.push("--- " + fileName + (typeof oldHeader === "undefined" ? "" : "\t" + oldHeader));
243
- ret.push("+++ " + fileName + (typeof newHeader === "undefined" ? "" : "\t" + newHeader));
257
+ ret.push('Index: ' + fileName);
258
+ ret.push('===================================================================');
259
+ ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
260
+ ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));
244
261
 
245
262
  var diff = LineDiff.diff(oldStr, newStr);
246
263
  if (!diff[diff.length-1].value) {
247
264
  diff.pop(); // Remove trailing newline add
248
265
  }
249
- diff.push({value: "", lines: []}); // Append an empty value to make cleanup easier
266
+ diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier
250
267
 
251
268
  function contextLines(lines) {
252
269
  return lines.map(function(entry) { return ' ' + entry; });
@@ -254,7 +271,7 @@ var JsDiff = (function() {
254
271
  function eofNL(curRange, i, current) {
255
272
  var last = diff[diff.length-2],
256
273
  isLast = i === diff.length-2,
257
- isLastOfType = i === diff.length-3 && (current.added === !last.added || current.removed === !last.removed);
274
+ isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed);
258
275
 
259
276
  // Figure out if this is the last line for the given file and missing NL
260
277
  if (!/\n$/.test(current.value) && (isLast || isLastOfType)) {
@@ -266,7 +283,7 @@ var JsDiff = (function() {
266
283
  oldLine = 1, newLine = 1;
267
284
  for (var i = 0; i < diff.length; i++) {
268
285
  var current = diff[i],
269
- lines = current.lines || current.value.replace(/\n$/, "").split("\n");
286
+ lines = current.lines || current.value.replace(/\n$/, '').split('\n');
270
287
  current.lines = lines;
271
288
 
272
289
  if (current.added || current.removed) {
@@ -281,7 +298,7 @@ var JsDiff = (function() {
281
298
  newRangeStart -= curRange.length;
282
299
  }
283
300
  }
284
- curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?"+":"-") + entry; }));
301
+ curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; }));
285
302
  eofNL(curRange, i, current);
286
303
 
287
304
  if (current.added) {
@@ -299,9 +316,9 @@ var JsDiff = (function() {
299
316
  // end the range and output
300
317
  var contextSize = Math.min(lines.length, 4);
301
318
  ret.push(
302
- "@@ -" + oldRangeStart + "," + (oldLine-oldRangeStart+contextSize)
303
- + " +" + newRangeStart + "," + (newLine-newRangeStart+contextSize)
304
- + " @@");
319
+ '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize)
320
+ + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize)
321
+ + ' @@');
305
322
  ret.push.apply(ret, curRange);
306
323
  ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
307
324
  if (lines.length <= 4) {
@@ -319,37 +336,114 @@ var JsDiff = (function() {
319
336
  return ret.join('\n') + '\n';
320
337
  },
321
338
 
339
+ applyPatch: function(oldStr, uniDiff) {
340
+ var diffstr = uniDiff.split('\n');
341
+ var diff = [];
342
+ var remEOFNL = false,
343
+ addEOFNL = false;
344
+
345
+ for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) {
346
+ if(diffstr[i][0] === '@') {
347
+ var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
348
+ diff.unshift({
349
+ start:meh[3],
350
+ oldlength:meh[2],
351
+ oldlines:[],
352
+ newlength:meh[4],
353
+ newlines:[]
354
+ });
355
+ } else if(diffstr[i][0] === '+') {
356
+ diff[0].newlines.push(diffstr[i].substr(1));
357
+ } else if(diffstr[i][0] === '-') {
358
+ diff[0].oldlines.push(diffstr[i].substr(1));
359
+ } else if(diffstr[i][0] === ' ') {
360
+ diff[0].newlines.push(diffstr[i].substr(1));
361
+ diff[0].oldlines.push(diffstr[i].substr(1));
362
+ } else if(diffstr[i][0] === '\\') {
363
+ if (diffstr[i-1][0] === '+') {
364
+ remEOFNL = true;
365
+ } else if(diffstr[i-1][0] === '-') {
366
+ addEOFNL = true;
367
+ }
368
+ }
369
+ }
370
+
371
+ var str = oldStr.split('\n');
372
+ for (var i = diff.length - 1; i >= 0; i--) {
373
+ var d = diff[i];
374
+ for (var j = 0; j < d.oldlength; j++) {
375
+ if(str[d.start-1+j] !== d.oldlines[j]) {
376
+ return false;
377
+ }
378
+ }
379
+ Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines));
380
+ }
381
+
382
+ if (remEOFNL) {
383
+ while (!str[str.length-1]) {
384
+ str.pop();
385
+ }
386
+ } else if (addEOFNL) {
387
+ str.push('');
388
+ }
389
+ return str.join('\n');
390
+ },
391
+
322
392
  convertChangesToXML: function(changes){
323
393
  var ret = [];
324
394
  for ( var i = 0; i < changes.length; i++) {
325
395
  var change = changes[i];
326
396
  if (change.added) {
327
- ret.push("<ins>");
397
+ ret.push('<ins>');
328
398
  } else if (change.removed) {
329
- ret.push("<del>");
399
+ ret.push('<del>');
330
400
  }
331
401
 
332
402
  ret.push(escapeHTML(change.value));
333
403
 
334
404
  if (change.added) {
335
- ret.push("</ins>");
405
+ ret.push('</ins>');
336
406
  } else if (change.removed) {
337
- ret.push("</del>");
407
+ ret.push('</del>');
338
408
  }
339
409
  }
340
- return ret.join("");
410
+ return ret.join('');
411
+ },
412
+
413
+ // See: http://code.google.com/p/google-diff-match-patch/wiki/API
414
+ convertChangesToDMP: function(changes){
415
+ var ret = [], change;
416
+ for ( var i = 0; i < changes.length; i++) {
417
+ change = changes[i];
418
+ ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
419
+ }
420
+ return ret;
341
421
  }
342
422
  };
343
423
  })();
344
424
 
345
- if (typeof module !== "undefined") {
425
+ if (typeof module !== 'undefined') {
346
426
  module.exports = JsDiff;
347
427
  }
348
428
 
349
429
  }); // module: browser/diff.js
350
430
 
351
- require.register("browser/events.js", function(module, exports, require){
431
+ require.register("browser/escape-string-regexp.js", function(module, exports, require){
432
+ 'use strict';
433
+
434
+ var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
435
+
436
+ module.exports = function (str) {
437
+ if (typeof str !== 'string') {
438
+ throw new TypeError('Expected a string');
439
+ }
440
+
441
+ return str.replace(matchOperatorsRe, '\\$&');
442
+ };
443
+
444
+ }); // module: browser/escape-string-regexp.js
352
445
 
446
+ require.register("browser/events.js", function(module, exports, require){
353
447
  /**
354
448
  * Module exports.
355
449
  */
@@ -527,18 +621,22 @@ EventEmitter.prototype.emit = function (name) {
527
621
 
528
622
  return true;
529
623
  };
624
+
530
625
  }); // module: browser/events.js
531
626
 
532
627
  require.register("browser/fs.js", function(module, exports, require){
533
628
 
534
629
  }); // module: browser/fs.js
535
630
 
631
+ require.register("browser/glob.js", function(module, exports, require){
632
+
633
+ }); // module: browser/glob.js
634
+
536
635
  require.register("browser/path.js", function(module, exports, require){
537
636
 
538
637
  }); // module: browser/path.js
539
638
 
540
639
  require.register("browser/progress.js", function(module, exports, require){
541
-
542
640
  /**
543
641
  * Expose `Progress`.
544
642
  */
@@ -627,58 +725,63 @@ Progress.prototype.update = function(n){
627
725
  */
628
726
 
629
727
  Progress.prototype.draw = function(ctx){
630
- var percent = Math.min(this.percent, 100)
631
- , size = this._size
632
- , half = size / 2
633
- , x = half
634
- , y = half
635
- , rad = half - 1
636
- , fontSize = this._fontSize;
637
-
638
- ctx.font = fontSize + 'px ' + this._font;
639
-
640
- var angle = Math.PI * 2 * (percent / 100);
641
- ctx.clearRect(0, 0, size, size);
642
-
643
- // outer circle
644
- ctx.strokeStyle = '#9f9f9f';
645
- ctx.beginPath();
646
- ctx.arc(x, y, rad, 0, angle, false);
647
- ctx.stroke();
648
-
649
- // inner circle
650
- ctx.strokeStyle = '#eee';
651
- ctx.beginPath();
652
- ctx.arc(x, y, rad - 1, 0, angle, true);
653
- ctx.stroke();
654
-
655
- // text
656
- var text = this._text || (percent | 0) + '%'
657
- , w = ctx.measureText(text).width;
658
-
659
- ctx.fillText(
660
- text
661
- , x - w / 2 + 1
662
- , y + fontSize / 2 - 1);
663
-
728
+ try {
729
+ var percent = Math.min(this.percent, 100)
730
+ , size = this._size
731
+ , half = size / 2
732
+ , x = half
733
+ , y = half
734
+ , rad = half - 1
735
+ , fontSize = this._fontSize;
736
+
737
+ ctx.font = fontSize + 'px ' + this._font;
738
+
739
+ var angle = Math.PI * 2 * (percent / 100);
740
+ ctx.clearRect(0, 0, size, size);
741
+
742
+ // outer circle
743
+ ctx.strokeStyle = '#9f9f9f';
744
+ ctx.beginPath();
745
+ ctx.arc(x, y, rad, 0, angle, false);
746
+ ctx.stroke();
747
+
748
+ // inner circle
749
+ ctx.strokeStyle = '#eee';
750
+ ctx.beginPath();
751
+ ctx.arc(x, y, rad - 1, 0, angle, true);
752
+ ctx.stroke();
753
+
754
+ // text
755
+ var text = this._text || (percent | 0) + '%'
756
+ , w = ctx.measureText(text).width;
757
+
758
+ ctx.fillText(
759
+ text
760
+ , x - w / 2 + 1
761
+ , y + fontSize / 2 - 1);
762
+ } catch (ex) {} //don't fail if we can't render progress
664
763
  return this;
665
764
  };
666
765
 
667
766
  }); // module: browser/progress.js
668
767
 
669
768
  require.register("browser/tty.js", function(module, exports, require){
670
-
671
769
  exports.isatty = function(){
672
770
  return true;
673
771
  };
674
772
 
675
773
  exports.getWindowSize = function(){
676
- return [window.innerHeight, window.innerWidth];
774
+ if ('innerHeight' in global) {
775
+ return [global.innerHeight, global.innerWidth];
776
+ } else {
777
+ // In a Web Worker, the DOM Window is not available.
778
+ return [640, 480];
779
+ }
677
780
  };
781
+
678
782
  }); // module: browser/tty.js
679
783
 
680
784
  require.register("context.js", function(module, exports, require){
681
-
682
785
  /**
683
786
  * Expose `Context`.
684
787
  */
@@ -716,10 +819,25 @@ Context.prototype.runnable = function(runnable){
716
819
  */
717
820
 
718
821
  Context.prototype.timeout = function(ms){
822
+ if (arguments.length === 0) return this.runnable().timeout();
719
823
  this.runnable().timeout(ms);
720
824
  return this;
721
825
  };
722
826
 
827
+ /**
828
+ * Set test timeout `enabled`.
829
+ *
830
+ * @param {Boolean} enabled
831
+ * @return {Context} self
832
+ * @api private
833
+ */
834
+
835
+ Context.prototype.enableTimeouts = function (enabled) {
836
+ this.runnable().enableTimeouts(enabled);
837
+ return this;
838
+ };
839
+
840
+
723
841
  /**
724
842
  * Set test slowness threshold `ms`.
725
843
  *
@@ -733,6 +851,18 @@ Context.prototype.slow = function(ms){
733
851
  return this;
734
852
  };
735
853
 
854
+ /**
855
+ * Mark a test as skipped.
856
+ *
857
+ * @return {Context} self
858
+ * @api private
859
+ */
860
+
861
+ Context.prototype.skip = function(){
862
+ this.runnable().skip();
863
+ return this;
864
+ };
865
+
736
866
  /**
737
867
  * Inspect the context void of `._runnable`.
738
868
  *
@@ -751,7 +881,6 @@ Context.prototype.inspect = function(){
751
881
  }); // module: context.js
752
882
 
753
883
  require.register("hook.js", function(module, exports, require){
754
-
755
884
  /**
756
885
  * Module dependencies.
757
886
  */
@@ -805,17 +934,17 @@ Hook.prototype.error = function(err){
805
934
  this._error = err;
806
935
  };
807
936
 
808
-
809
937
  }); // module: hook.js
810
938
 
811
939
  require.register("interfaces/bdd.js", function(module, exports, require){
812
-
813
940
  /**
814
941
  * Module dependencies.
815
942
  */
816
943
 
817
944
  var Suite = require('../suite')
818
- , Test = require('../test');
945
+ , Test = require('../test')
946
+ , utils = require('../utils')
947
+ , escapeRe = require('browser/escape-string-regexp');
819
948
 
820
949
  /**
821
950
  * BDD-style interface:
@@ -839,38 +968,13 @@ module.exports = function(suite){
839
968
 
840
969
  suite.on('pre-require', function(context, file, mocha){
841
970
 
842
- /**
843
- * Execute before running tests.
844
- */
845
-
846
- context.before = function(fn){
847
- suites[0].beforeAll(fn);
848
- };
849
-
850
- /**
851
- * Execute after running tests.
852
- */
853
-
854
- context.after = function(fn){
855
- suites[0].afterAll(fn);
856
- };
857
-
858
- /**
859
- * Execute before each test case.
860
- */
861
-
862
- context.beforeEach = function(fn){
863
- suites[0].beforeEach(fn);
864
- };
865
-
866
- /**
867
- * Execute after each test case.
868
- */
869
-
870
- context.afterEach = function(fn){
871
- suites[0].afterEach(fn);
872
- };
971
+ var common = require('./common')(suites, context);
873
972
 
973
+ context.before = common.before;
974
+ context.after = common.after;
975
+ context.beforeEach = common.beforeEach;
976
+ context.afterEach = common.afterEach;
977
+ context.run = mocha.options.delay && common.runWithSuite(suite);
874
978
  /**
875
979
  * Describe a "suite" with the given `title`
876
980
  * and callback `fn` containing nested suites
@@ -879,6 +983,7 @@ module.exports = function(suite){
879
983
 
880
984
  context.describe = context.context = function(title, fn){
881
985
  var suite = Suite.create(suites[0], title);
986
+ suite.file = file;
882
987
  suites.unshift(suite);
883
988
  fn.call(suite);
884
989
  suites.shift();
@@ -906,6 +1011,7 @@ module.exports = function(suite){
906
1011
  context.describe.only = function(title, fn){
907
1012
  var suite = context.describe(title, fn);
908
1013
  mocha.grep(suite.fullTitle());
1014
+ return suite;
909
1015
  };
910
1016
 
911
1017
  /**
@@ -916,8 +1022,9 @@ module.exports = function(suite){
916
1022
 
917
1023
  context.it = context.specify = function(title, fn){
918
1024
  var suite = suites[0];
919
- if (suite.pending) var fn = null;
1025
+ if (suite.pending) fn = null;
920
1026
  var test = new Test(title, fn);
1027
+ test.file = file;
921
1028
  suite.addTest(test);
922
1029
  return test;
923
1030
  };
@@ -928,7 +1035,9 @@ module.exports = function(suite){
928
1035
 
929
1036
  context.it.only = function(title, fn){
930
1037
  var test = context.it(title, fn);
931
- mocha.grep(test.fullTitle());
1038
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
1039
+ mocha.grep(new RegExp(reString));
1040
+ return test;
932
1041
  };
933
1042
 
934
1043
  /**
@@ -940,13 +1049,75 @@ module.exports = function(suite){
940
1049
  context.it.skip = function(title){
941
1050
  context.it(title);
942
1051
  };
1052
+
943
1053
  });
944
1054
  };
945
1055
 
946
1056
  }); // module: interfaces/bdd.js
947
1057
 
948
- require.register("interfaces/exports.js", function(module, exports, require){
1058
+ require.register("interfaces/common.js", function(module, exports, require){
1059
+ /**
1060
+ * Functions common to more than one interface
1061
+ * @module lib/interfaces/common
1062
+ */
1063
+
1064
+ 'use strict';
1065
+
1066
+ module.exports = function (suites, context) {
1067
+
1068
+ return {
1069
+ /**
1070
+ * This is only present if flag --delay is passed into Mocha. It triggers
1071
+ * root suite execution. Returns a function which runs the root suite.
1072
+ */
1073
+ runWithSuite: function runWithSuite(suite) {
1074
+ return function run() {
1075
+ suite.run();
1076
+ };
1077
+ },
1078
+
1079
+ /**
1080
+ * Execute before running tests.
1081
+ */
1082
+ before: function (name, fn) {
1083
+ suites[0].beforeAll(name, fn);
1084
+ },
1085
+
1086
+ /**
1087
+ * Execute after running tests.
1088
+ */
1089
+ after: function (name, fn) {
1090
+ suites[0].afterAll(name, fn);
1091
+ },
1092
+
1093
+ /**
1094
+ * Execute before each test case.
1095
+ */
1096
+ beforeEach: function (name, fn) {
1097
+ suites[0].beforeEach(name, fn);
1098
+ },
1099
+
1100
+ /**
1101
+ * Execute after each test case.
1102
+ */
1103
+ afterEach: function (name, fn) {
1104
+ suites[0].afterEach(name, fn);
1105
+ },
1106
+
1107
+ test: {
1108
+ /**
1109
+ * Pending test case.
1110
+ */
1111
+ skip: function (title) {
1112
+ context.test(title);
1113
+ }
1114
+ }
1115
+ }
1116
+ };
1117
+
1118
+ }); // module: interfaces/common.js
949
1119
 
1120
+ require.register("interfaces/exports.js", function(module, exports, require){
950
1121
  /**
951
1122
  * Module dependencies.
952
1123
  */
@@ -976,7 +1147,7 @@ module.exports = function(suite){
976
1147
 
977
1148
  suite.on('require', visit);
978
1149
 
979
- function visit(obj) {
1150
+ function visit(obj, file) {
980
1151
  var suite;
981
1152
  for (var key in obj) {
982
1153
  if ('function' == typeof obj[key]) {
@@ -995,10 +1166,12 @@ module.exports = function(suite){
995
1166
  suites[0].afterEach(fn);
996
1167
  break;
997
1168
  default:
998
- suites[0].addTest(new Test(key, fn));
1169
+ var test = new Test(key, fn);
1170
+ test.file = file;
1171
+ suites[0].addTest(test);
999
1172
  }
1000
1173
  } else {
1001
- var suite = Suite.create(suites[0], key);
1174
+ suite = Suite.create(suites[0], key);
1002
1175
  suites.unshift(suite);
1003
1176
  visit(obj[key]);
1004
1177
  suites.shift();
@@ -1006,10 +1179,10 @@ module.exports = function(suite){
1006
1179
  }
1007
1180
  }
1008
1181
  };
1182
+
1009
1183
  }); // module: interfaces/exports.js
1010
1184
 
1011
1185
  require.register("interfaces/index.js", function(module, exports, require){
1012
-
1013
1186
  exports.bdd = require('./bdd');
1014
1187
  exports.tdd = require('./tdd');
1015
1188
  exports.qunit = require('./qunit');
@@ -1018,13 +1191,14 @@ exports.exports = require('./exports');
1018
1191
  }); // module: interfaces/index.js
1019
1192
 
1020
1193
  require.register("interfaces/qunit.js", function(module, exports, require){
1021
-
1022
1194
  /**
1023
1195
  * Module dependencies.
1024
1196
  */
1025
1197
 
1026
1198
  var Suite = require('../suite')
1027
- , Test = require('../test');
1199
+ , Test = require('../test')
1200
+ , escapeRe = require('browser/escape-string-regexp')
1201
+ , utils = require('../utils');
1028
1202
 
1029
1203
  /**
1030
1204
  * QUnit-style interface:
@@ -1054,72 +1228,75 @@ var Suite = require('../suite')
1054
1228
  module.exports = function(suite){
1055
1229
  var suites = [suite];
1056
1230
 
1057
- suite.on('pre-require', function(context){
1058
-
1059
- /**
1060
- * Execute before running tests.
1061
- */
1231
+ suite.on('pre-require', function(context, file, mocha){
1062
1232
 
1063
- context.before = function(fn){
1064
- suites[0].beforeAll(fn);
1065
- };
1233
+ var common = require('./common')(suites, context);
1066
1234
 
1235
+ context.before = common.before;
1236
+ context.after = common.after;
1237
+ context.beforeEach = common.beforeEach;
1238
+ context.afterEach = common.afterEach;
1239
+ context.run = mocha.options.delay && common.runWithSuite(suite);
1067
1240
  /**
1068
- * Execute after running tests.
1241
+ * Describe a "suite" with the given `title`.
1069
1242
  */
1070
1243
 
1071
- context.after = function(fn){
1072
- suites[0].afterAll(fn);
1244
+ context.suite = function(title){
1245
+ if (suites.length > 1) suites.shift();
1246
+ var suite = Suite.create(suites[0], title);
1247
+ suite.file = file;
1248
+ suites.unshift(suite);
1249
+ return suite;
1073
1250
  };
1074
1251
 
1075
1252
  /**
1076
- * Execute before each test case.
1253
+ * Exclusive test-case.
1077
1254
  */
1078
1255
 
1079
- context.beforeEach = function(fn){
1080
- suites[0].beforeEach(fn);
1256
+ context.suite.only = function(title, fn){
1257
+ var suite = context.suite(title, fn);
1258
+ mocha.grep(suite.fullTitle());
1081
1259
  };
1082
1260
 
1083
1261
  /**
1084
- * Execute after each test case.
1262
+ * Describe a specification or test-case
1263
+ * with the given `title` and callback `fn`
1264
+ * acting as a thunk.
1085
1265
  */
1086
1266
 
1087
- context.afterEach = function(fn){
1088
- suites[0].afterEach(fn);
1267
+ context.test = function(title, fn){
1268
+ var test = new Test(title, fn);
1269
+ test.file = file;
1270
+ suites[0].addTest(test);
1271
+ return test;
1089
1272
  };
1090
1273
 
1091
1274
  /**
1092
- * Describe a "suite" with the given `title`.
1275
+ * Exclusive test-case.
1093
1276
  */
1094
1277
 
1095
- context.suite = function(title){
1096
- if (suites.length > 1) suites.shift();
1097
- var suite = Suite.create(suites[0], title);
1098
- suites.unshift(suite);
1278
+ context.test.only = function(title, fn){
1279
+ var test = context.test(title, fn);
1280
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
1281
+ mocha.grep(new RegExp(reString));
1099
1282
  };
1100
1283
 
1101
- /**
1102
- * Describe a specification or test-case
1103
- * with the given `title` and callback `fn`
1104
- * acting as a thunk.
1105
- */
1284
+ context.test.skip = common.test.skip;
1106
1285
 
1107
- context.test = function(title, fn){
1108
- suites[0].addTest(new Test(title, fn));
1109
- };
1110
1286
  });
1111
1287
  };
1112
1288
 
1113
1289
  }); // module: interfaces/qunit.js
1114
1290
 
1115
1291
  require.register("interfaces/tdd.js", function(module, exports, require){
1116
-
1117
1292
  /**
1118
1293
  * Module dependencies.
1119
1294
  */
1120
1295
 
1121
1296
  var Suite = require('../suite')
1122
- , Test = require('../test');
1297
+ , Test = require('../test')
1298
+ , escapeRe = require('browser/escape-string-regexp')
1299
+ , utils = require('../utils');
1123
1300
 
1124
1301
  /**
1125
1302
  * TDD-style interface:
@@ -1151,38 +1328,13 @@ module.exports = function(suite){
1151
1328
 
1152
1329
  suite.on('pre-require', function(context, file, mocha){
1153
1330
 
1154
- /**
1155
- * Execute before each test case.
1156
- */
1157
-
1158
- context.setup = function(fn){
1159
- suites[0].beforeEach(fn);
1160
- };
1161
-
1162
- /**
1163
- * Execute after each test case.
1164
- */
1165
-
1166
- context.teardown = function(fn){
1167
- suites[0].afterEach(fn);
1168
- };
1169
-
1170
- /**
1171
- * Execute before the suite.
1172
- */
1173
-
1174
- context.suiteSetup = function(fn){
1175
- suites[0].beforeAll(fn);
1176
- };
1177
-
1178
- /**
1179
- * Execute after the suite.
1180
- */
1181
-
1182
- context.suiteTeardown = function(fn){
1183
- suites[0].afterAll(fn);
1184
- };
1331
+ var common = require('./common')(suites, context);
1185
1332
 
1333
+ context.setup = common.beforeEach;
1334
+ context.teardown = common.afterEach;
1335
+ context.suiteSetup = common.before;
1336
+ context.suiteTeardown = common.after;
1337
+ context.run = mocha.options.delay && common.runWithSuite(suite);
1186
1338
  /**
1187
1339
  * Describe a "suite" with the given `title`
1188
1340
  * and callback `fn` containing nested suites
@@ -1191,12 +1343,24 @@ module.exports = function(suite){
1191
1343
 
1192
1344
  context.suite = function(title, fn){
1193
1345
  var suite = Suite.create(suites[0], title);
1346
+ suite.file = file;
1194
1347
  suites.unshift(suite);
1195
1348
  fn.call(suite);
1196
1349
  suites.shift();
1197
1350
  return suite;
1198
1351
  };
1199
1352
 
1353
+ /**
1354
+ * Pending suite.
1355
+ */
1356
+ context.suite.skip = function(title, fn) {
1357
+ var suite = Suite.create(suites[0], title);
1358
+ suite.pending = true;
1359
+ suites.unshift(suite);
1360
+ fn.call(suite);
1361
+ suites.shift();
1362
+ };
1363
+
1200
1364
  /**
1201
1365
  * Exclusive test-case.
1202
1366
  */
@@ -1213,8 +1377,11 @@ module.exports = function(suite){
1213
1377
  */
1214
1378
 
1215
1379
  context.test = function(title, fn){
1380
+ var suite = suites[0];
1381
+ if (suite.pending) fn = null;
1216
1382
  var test = new Test(title, fn);
1217
- suites[0].addTest(test);
1383
+ test.file = file;
1384
+ suite.addTest(test);
1218
1385
  return test;
1219
1386
  };
1220
1387
 
@@ -1224,16 +1391,11 @@ module.exports = function(suite){
1224
1391
 
1225
1392
  context.test.only = function(title, fn){
1226
1393
  var test = context.test(title, fn);
1227
- mocha.grep(test.fullTitle());
1394
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
1395
+ mocha.grep(new RegExp(reString));
1228
1396
  };
1229
1397
 
1230
- /**
1231
- * Pending test case.
1232
- */
1233
-
1234
- context.test.skip = function(title){
1235
- context.test(title);
1236
- };
1398
+ context.test.skip = common.test.skip;
1237
1399
  });
1238
1400
  };
1239
1401
 
@@ -1251,6 +1413,7 @@ require.register("mocha.js", function(module, exports, require){
1251
1413
  */
1252
1414
 
1253
1415
  var path = require('browser/path')
1416
+ , escapeRe = require('browser/escape-string-regexp')
1254
1417
  , utils = require('./utils');
1255
1418
 
1256
1419
  /**
@@ -1259,6 +1422,16 @@ var path = require('browser/path')
1259
1422
 
1260
1423
  exports = module.exports = Mocha;
1261
1424
 
1425
+ /**
1426
+ * To require local UIs and reporters when running in node.
1427
+ */
1428
+
1429
+ if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
1430
+ var join = path.join
1431
+ , cwd = process.cwd();
1432
+ module.paths.push(cwd, join(cwd, 'node_modules'));
1433
+ }
1434
+
1262
1435
  /**
1263
1436
  * Expose internals.
1264
1437
  */
@@ -1291,12 +1464,13 @@ function image(name) {
1291
1464
  * Options:
1292
1465
  *
1293
1466
  * - `ui` name "bdd", "tdd", "exports" etc
1294
- * - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
1467
+ * - `reporter` reporter instance, defaults to `mocha.reporters.spec`
1295
1468
  * - `globals` array of accepted globals
1296
1469
  * - `timeout` timeout in milliseconds
1297
1470
  * - `bail` bail on the first test failure
1298
1471
  * - `slow` milliseconds to wait before considering a test slow
1299
1472
  * - `ignoreLeaks` ignore global leaks
1473
+ * - `fullTrace` display the full stack-trace on failing
1300
1474
  * - `grep` string or regexp to filter tests with
1301
1475
  *
1302
1476
  * @param {Object} options
@@ -1307,13 +1481,32 @@ function Mocha(options) {
1307
1481
  options = options || {};
1308
1482
  this.files = [];
1309
1483
  this.options = options;
1310
- this.grep(options.grep);
1484
+ if (options.grep) this.grep(new RegExp(options.grep));
1485
+ if (options.fgrep) this.grep(options.fgrep);
1311
1486
  this.suite = new exports.Suite('', new exports.Context);
1312
1487
  this.ui(options.ui);
1313
1488
  this.bail(options.bail);
1314
- this.reporter(options.reporter);
1315
- if (options.timeout) this.timeout(options.timeout);
1489
+ this.reporter(options.reporter, options.reporterOptions);
1490
+ if (null != options.timeout) this.timeout(options.timeout);
1491
+ this.useColors(options.useColors);
1492
+ if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
1316
1493
  if (options.slow) this.slow(options.slow);
1494
+
1495
+ this.suite.on('pre-require', function (context) {
1496
+ exports.afterEach = context.afterEach || context.teardown;
1497
+ exports.after = context.after || context.suiteTeardown;
1498
+ exports.beforeEach = context.beforeEach || context.setup;
1499
+ exports.before = context.before || context.suiteSetup;
1500
+ exports.describe = context.describe || context.suite;
1501
+ exports.it = context.it || context.test;
1502
+ exports.setup = context.setup || context.beforeEach;
1503
+ exports.suiteSetup = context.suiteSetup || context.before;
1504
+ exports.suiteTeardown = context.suiteTeardown || context.after;
1505
+ exports.suite = context.suite || context.describe;
1506
+ exports.teardown = context.teardown || context.afterEach;
1507
+ exports.test = context.test || context.it;
1508
+ exports.run = context.run;
1509
+ });
1317
1510
  }
1318
1511
 
1319
1512
  /**
@@ -1342,24 +1535,32 @@ Mocha.prototype.addFile = function(file){
1342
1535
  };
1343
1536
 
1344
1537
  /**
1345
- * Set reporter to `reporter`, defaults to "dot".
1538
+ * Set reporter to `reporter`, defaults to "spec".
1346
1539
  *
1347
1540
  * @param {String|Function} reporter name or constructor
1541
+ * @param {Object} reporterOptions optional options
1348
1542
  * @api public
1349
1543
  */
1350
-
1351
- Mocha.prototype.reporter = function(reporter){
1544
+ Mocha.prototype.reporter = function(reporter, reporterOptions){
1352
1545
  if ('function' == typeof reporter) {
1353
1546
  this._reporter = reporter;
1354
1547
  } else {
1355
- reporter = reporter || 'dot';
1356
- try {
1357
- this._reporter = require('./reporters/' + reporter);
1358
- } catch (err) {
1359
- this._reporter = require(reporter);
1548
+ reporter = reporter || 'spec';
1549
+ var _reporter;
1550
+ try { _reporter = require('./reporters/' + reporter); } catch (err) {}
1551
+ if (!_reporter) try { _reporter = require(reporter); } catch (err) {
1552
+ err.message.indexOf('Cannot find module') !== -1
1553
+ ? console.warn('"' + reporter + '" reporter not found')
1554
+ : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack);
1360
1555
  }
1361
- if (!this._reporter) throw new Error('invalid reporter "' + reporter + '"');
1556
+ if (!_reporter && reporter === 'teamcity')
1557
+ console.warn('The Teamcity reporter was moved to a package named ' +
1558
+ 'mocha-teamcity-reporter ' +
1559
+ '(https://npmjs.org/package/mocha-teamcity-reporter).');
1560
+ if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
1561
+ this._reporter = _reporter;
1362
1562
  }
1563
+ this.options.reporterOptions = reporterOptions;
1363
1564
  return this;
1364
1565
  };
1365
1566
 
@@ -1373,6 +1574,7 @@ Mocha.prototype.reporter = function(reporter){
1373
1574
  Mocha.prototype.ui = function(name){
1374
1575
  name = name || 'bdd';
1375
1576
  this._ui = exports.interfaces[name];
1577
+ if (!this._ui) try { this._ui = require(name); } catch (err) {}
1376
1578
  if (!this._ui) throw new Error('invalid interface "' + name + '"');
1377
1579
  this._ui = this._ui(this.suite);
1378
1580
  return this;
@@ -1431,7 +1633,7 @@ Mocha.prototype._growl = function(runner, reporter) {
1431
1633
 
1432
1634
  Mocha.prototype.grep = function(re){
1433
1635
  this.options.grep = 'string' == typeof re
1434
- ? new RegExp(utils.escapeRegexp(re))
1636
+ ? new RegExp(escapeRe(re))
1435
1637
  : re;
1436
1638
  return this;
1437
1639
  };
@@ -1451,12 +1653,13 @@ Mocha.prototype.invert = function(){
1451
1653
  /**
1452
1654
  * Ignore global leaks.
1453
1655
  *
1656
+ * @param {Boolean} ignore
1454
1657
  * @return {Mocha}
1455
1658
  * @api public
1456
1659
  */
1457
1660
 
1458
- Mocha.prototype.ignoreLeaks = function(){
1459
- this.options.ignoreLeaks = true;
1661
+ Mocha.prototype.ignoreLeaks = function(ignore){
1662
+ this.options.ignoreLeaks = !!ignore;
1460
1663
  return this;
1461
1664
  };
1462
1665
 
@@ -1472,6 +1675,18 @@ Mocha.prototype.checkLeaks = function(){
1472
1675
  return this;
1473
1676
  };
1474
1677
 
1678
+ /**
1679
+ * Display long stack-trace on failing
1680
+ *
1681
+ * @return {Mocha}
1682
+ * @api public
1683
+ */
1684
+
1685
+ Mocha.prototype.fullTrace = function() {
1686
+ this.options.fullStackTrace = true;
1687
+ return this;
1688
+ };
1689
+
1475
1690
  /**
1476
1691
  * Enable growl support.
1477
1692
  *
@@ -1498,69 +1713,145 @@ Mocha.prototype.globals = function(globals){
1498
1713
  };
1499
1714
 
1500
1715
  /**
1501
- * Set the timeout in milliseconds.
1716
+ * Emit color output.
1502
1717
  *
1503
- * @param {Number} timeout
1718
+ * @param {Boolean} colors
1504
1719
  * @return {Mocha}
1505
1720
  * @api public
1506
1721
  */
1507
1722
 
1508
- Mocha.prototype.timeout = function(timeout){
1509
- this.suite.timeout(timeout);
1723
+ Mocha.prototype.useColors = function(colors){
1724
+ if (colors !== undefined) {
1725
+ this.options.useColors = colors;
1726
+ }
1510
1727
  return this;
1511
1728
  };
1512
1729
 
1513
1730
  /**
1514
- * Set slowness threshold in milliseconds.
1731
+ * Use inline diffs rather than +/-.
1515
1732
  *
1516
- * @param {Number} slow
1733
+ * @param {Boolean} inlineDiffs
1517
1734
  * @return {Mocha}
1518
1735
  * @api public
1519
1736
  */
1520
1737
 
1521
- Mocha.prototype.slow = function(slow){
1522
- this.suite.slow(slow);
1738
+ Mocha.prototype.useInlineDiffs = function(inlineDiffs) {
1739
+ this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined
1740
+ ? inlineDiffs
1741
+ : false;
1523
1742
  return this;
1524
1743
  };
1525
1744
 
1526
1745
  /**
1527
- * Makes all tests async (accepting a callback)
1746
+ * Set the timeout in milliseconds.
1528
1747
  *
1748
+ * @param {Number} timeout
1529
1749
  * @return {Mocha}
1530
1750
  * @api public
1531
1751
  */
1532
1752
 
1533
- Mocha.prototype.asyncOnly = function(){
1534
- this.options.asyncOnly = true;
1753
+ Mocha.prototype.timeout = function(timeout){
1754
+ this.suite.timeout(timeout);
1535
1755
  return this;
1536
1756
  };
1537
1757
 
1538
1758
  /**
1539
- * Run tests and invoke `fn()` when complete.
1759
+ * Set slowness threshold in milliseconds.
1540
1760
  *
1541
- * @param {Function} fn
1542
- * @return {Runner}
1761
+ * @param {Number} slow
1762
+ * @return {Mocha}
1763
+ * @api public
1764
+ */
1765
+
1766
+ Mocha.prototype.slow = function(slow){
1767
+ this.suite.slow(slow);
1768
+ return this;
1769
+ };
1770
+
1771
+ /**
1772
+ * Enable timeouts.
1773
+ *
1774
+ * @param {Boolean} enabled
1775
+ * @return {Mocha}
1776
+ * @api public
1777
+ */
1778
+
1779
+ Mocha.prototype.enableTimeouts = function(enabled) {
1780
+ this.suite.enableTimeouts(arguments.length && enabled !== undefined
1781
+ ? enabled
1782
+ : true);
1783
+ return this
1784
+ };
1785
+
1786
+ /**
1787
+ * Makes all tests async (accepting a callback)
1788
+ *
1789
+ * @return {Mocha}
1790
+ * @api public
1791
+ */
1792
+
1793
+ Mocha.prototype.asyncOnly = function(){
1794
+ this.options.asyncOnly = true;
1795
+ return this;
1796
+ };
1797
+
1798
+ /**
1799
+ * Disable syntax highlighting (in browser).
1800
+ * @returns {Mocha}
1543
1801
  * @api public
1544
1802
  */
1803
+ Mocha.prototype.noHighlighting = function() {
1804
+ this.options.noHighlighting = true;
1805
+ return this;
1806
+ };
1807
+
1808
+ /**
1809
+ * Delay root suite execution.
1810
+ * @returns {Mocha}
1811
+ * @api public
1812
+ */
1813
+ Mocha.prototype.delay = function delay() {
1814
+ this.options.delay = true;
1815
+ return this;
1816
+ };
1545
1817
 
1818
+ /**
1819
+ * Run tests and invoke `fn()` when complete.
1820
+ *
1821
+ * @param {Function} fn
1822
+ * @return {Runner}
1823
+ * @api public
1824
+ */
1546
1825
  Mocha.prototype.run = function(fn){
1547
1826
  if (this.files.length) this.loadFiles();
1548
1827
  var suite = this.suite;
1549
1828
  var options = this.options;
1550
- var runner = new exports.Runner(suite);
1551
- var reporter = new this._reporter(runner);
1552
- runner.ignoreLeaks = options.ignoreLeaks;
1829
+ options.files = this.files;
1830
+ var runner = new exports.Runner(suite, options.delay);
1831
+ var reporter = new this._reporter(runner, options);
1832
+ runner.ignoreLeaks = false !== options.ignoreLeaks;
1833
+ runner.fullStackTrace = options.fullStackTrace;
1553
1834
  runner.asyncOnly = options.asyncOnly;
1554
1835
  if (options.grep) runner.grep(options.grep, options.invert);
1555
1836
  if (options.globals) runner.globals(options.globals);
1556
1837
  if (options.growl) this._growl(runner, reporter);
1557
- return runner.run(fn);
1838
+ if (options.useColors !== undefined) {
1839
+ exports.reporters.Base.useColors = options.useColors;
1840
+ }
1841
+ exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
1842
+
1843
+ function done(failures) {
1844
+ if (reporter.done) {
1845
+ reporter.done(failures, fn);
1846
+ } else fn && fn(failures);
1847
+ }
1848
+
1849
+ return runner.run(done);
1558
1850
  };
1559
1851
 
1560
1852
  }); // module: mocha.js
1561
1853
 
1562
1854
  require.register("ms.js", function(module, exports, require){
1563
-
1564
1855
  /**
1565
1856
  * Helpers.
1566
1857
  */
@@ -1569,19 +1860,26 @@ var s = 1000;
1569
1860
  var m = s * 60;
1570
1861
  var h = m * 60;
1571
1862
  var d = h * 24;
1863
+ var y = d * 365.25;
1572
1864
 
1573
1865
  /**
1574
1866
  * Parse or format the given `val`.
1575
1867
  *
1868
+ * Options:
1869
+ *
1870
+ * - `long` verbose formatting [false]
1871
+ *
1576
1872
  * @param {String|Number} val
1873
+ * @param {Object} options
1577
1874
  * @return {String|Number}
1578
1875
  * @api public
1579
1876
  */
1580
1877
 
1581
- module.exports = function(val){
1878
+ module.exports = function(val, options){
1879
+ options = options || {};
1582
1880
  if ('string' == typeof val) return parse(val);
1583
- return format(val);
1584
- }
1881
+ return options['long'] ? longFormat(val) : shortFormat(val);
1882
+ };
1585
1883
 
1586
1884
  /**
1587
1885
  * Parse the given `str` and return milliseconds.
@@ -1592,66 +1890,110 @@ module.exports = function(val){
1592
1890
  */
1593
1891
 
1594
1892
  function parse(str) {
1595
- var m = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
1596
- if (!m) return;
1597
- var n = parseFloat(m[1]);
1598
- var type = (m[2] || 'ms').toLowerCase();
1893
+ var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
1894
+ if (!match) return;
1895
+ var n = parseFloat(match[1]);
1896
+ var type = (match[2] || 'ms').toLowerCase();
1599
1897
  switch (type) {
1600
1898
  case 'years':
1601
1899
  case 'year':
1602
1900
  case 'y':
1603
- return n * 31557600000;
1901
+ return n * y;
1604
1902
  case 'days':
1605
1903
  case 'day':
1606
1904
  case 'd':
1607
- return n * 86400000;
1905
+ return n * d;
1608
1906
  case 'hours':
1609
1907
  case 'hour':
1610
1908
  case 'h':
1611
- return n * 3600000;
1909
+ return n * h;
1612
1910
  case 'minutes':
1613
1911
  case 'minute':
1614
1912
  case 'm':
1615
- return n * 60000;
1913
+ return n * m;
1616
1914
  case 'seconds':
1617
1915
  case 'second':
1618
1916
  case 's':
1619
- return n * 1000;
1917
+ return n * s;
1620
1918
  case 'ms':
1621
1919
  return n;
1622
1920
  }
1623
1921
  }
1624
1922
 
1625
1923
  /**
1626
- * Format the given `ms`.
1924
+ * Short format for `ms`.
1627
1925
  *
1628
1926
  * @param {Number} ms
1629
1927
  * @return {String}
1630
- * @api public
1928
+ * @api private
1929
+ */
1930
+
1931
+ function shortFormat(ms) {
1932
+ if (ms >= d) return Math.round(ms / d) + 'd';
1933
+ if (ms >= h) return Math.round(ms / h) + 'h';
1934
+ if (ms >= m) return Math.round(ms / m) + 'm';
1935
+ if (ms >= s) return Math.round(ms / s) + 's';
1936
+ return ms + 'ms';
1937
+ }
1938
+
1939
+ /**
1940
+ * Long format for `ms`.
1941
+ *
1942
+ * @param {Number} ms
1943
+ * @return {String}
1944
+ * @api private
1945
+ */
1946
+
1947
+ function longFormat(ms) {
1948
+ return plural(ms, d, 'day')
1949
+ || plural(ms, h, 'hour')
1950
+ || plural(ms, m, 'minute')
1951
+ || plural(ms, s, 'second')
1952
+ || ms + ' ms';
1953
+ }
1954
+
1955
+ /**
1956
+ * Pluralization helper.
1631
1957
  */
1632
1958
 
1633
- function format(ms) {
1634
- if (ms == d) return Math.round(ms / d) + ' day';
1635
- if (ms > d) return Math.round(ms / d) + ' days';
1636
- if (ms == h) return Math.round(ms / h) + ' hour';
1637
- if (ms > h) return Math.round(ms / h) + ' hours';
1638
- if (ms == m) return Math.round(ms / m) + ' minute';
1639
- if (ms > m) return Math.round(ms / m) + ' minutes';
1640
- if (ms == s) return Math.round(ms / s) + ' second';
1641
- if (ms > s) return Math.round(ms / s) + ' seconds';
1642
- return ms + ' ms';
1959
+ function plural(ms, n, name) {
1960
+ if (ms < n) return;
1961
+ if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
1962
+ return Math.ceil(ms / n) + ' ' + name + 's';
1643
1963
  }
1964
+
1644
1965
  }); // module: ms.js
1645
1966
 
1646
- require.register("reporters/base.js", function(module, exports, require){
1967
+ require.register("pending.js", function(module, exports, require){
1968
+
1969
+ /**
1970
+ * Expose `Pending`.
1971
+ */
1647
1972
 
1973
+ module.exports = Pending;
1974
+
1975
+ /**
1976
+ * Initialize a new `Pending` error with the given message.
1977
+ *
1978
+ * @param {String} message
1979
+ */
1980
+
1981
+ function Pending(message) {
1982
+ this.message = message;
1983
+ }
1984
+
1985
+ }); // module: pending.js
1986
+
1987
+ require.register("reporters/base.js", function(module, exports, require){
1648
1988
  /**
1649
1989
  * Module dependencies.
1650
1990
  */
1651
1991
 
1652
1992
  var tty = require('browser/tty')
1653
1993
  , diff = require('browser/diff')
1654
- , ms = require('../ms');
1994
+ , ms = require('../ms')
1995
+ , utils = require('../utils')
1996
+ , supportsColor = process.env ? require('supports-color') : null;
1655
1997
 
1656
1998
  /**
1657
1999
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -1676,10 +2018,18 @@ var isatty = tty.isatty(1) && tty.isatty(2);
1676
2018
  exports = module.exports = Base;
1677
2019
 
1678
2020
  /**
1679
- * Enable coloring by default.
2021
+ * Enable coloring by default, except in the browser interface.
2022
+ */
2023
+
2024
+ exports.useColors = process.env
2025
+ ? (supportsColor || (process.env.MOCHA_COLORS !== undefined))
2026
+ : false;
2027
+
2028
+ /**
2029
+ * Inline diffs instead of +/-
1680
2030
  */
1681
2031
 
1682
- exports.useColors = isatty;
2032
+ exports.inlineDiffs = false;
1683
2033
 
1684
2034
  /**
1685
2035
  * Default color map.
@@ -1737,7 +2087,7 @@ if ('win32' == process.platform) {
1737
2087
  */
1738
2088
 
1739
2089
  var color = exports.color = function(type, str) {
1740
- if (!exports.useColors) return str;
2090
+ if (!exports.useColors) return String(str);
1741
2091
  return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
1742
2092
  };
1743
2093
 
@@ -1761,24 +2111,28 @@ exports.window = {
1761
2111
 
1762
2112
  exports.cursor = {
1763
2113
  hide: function(){
1764
- process.stdout.write('\u001b[?25l');
2114
+ isatty && process.stdout.write('\u001b[?25l');
1765
2115
  },
1766
2116
 
1767
2117
  show: function(){
1768
- process.stdout.write('\u001b[?25h');
2118
+ isatty && process.stdout.write('\u001b[?25h');
1769
2119
  },
1770
2120
 
1771
2121
  deleteLine: function(){
1772
- process.stdout.write('\u001b[2K');
2122
+ isatty && process.stdout.write('\u001b[2K');
1773
2123
  },
1774
2124
 
1775
2125
  beginningOfLine: function(){
1776
- process.stdout.write('\u001b[0G');
2126
+ isatty && process.stdout.write('\u001b[0G');
1777
2127
  },
1778
2128
 
1779
2129
  CR: function(){
1780
- exports.cursor.deleteLine();
1781
- exports.cursor.beginningOfLine();
2130
+ if (isatty) {
2131
+ exports.cursor.deleteLine();
2132
+ exports.cursor.beginningOfLine();
2133
+ } else {
2134
+ process.stdout.write('\r');
2135
+ }
1782
2136
  }
1783
2137
  };
1784
2138
 
@@ -1790,8 +2144,9 @@ exports.cursor = {
1790
2144
  */
1791
2145
 
1792
2146
  exports.list = function(failures){
1793
- console.error();
2147
+ console.log();
1794
2148
  failures.forEach(function(test, i){
2149
+ console.log("test ", test);
1795
2150
  // format
1796
2151
  var fmt = color('error title', ' %s) %s:\n')
1797
2152
  + color('error message', ' %s')
@@ -1801,56 +2156,48 @@ exports.list = function(failures){
1801
2156
  var err = test.err
1802
2157
  , message = err.message || ''
1803
2158
  , stack = err.stack || message
1804
- , index = stack.indexOf(message) + message.length
1805
- , msg = stack.slice(0, index)
2159
+ , index = stack.indexOf(message)
1806
2160
  , actual = err.actual
1807
2161
  , expected = err.expected
1808
2162
  , escape = true;
1809
-
1810
- // explicitly show diff
1811
- if (err.showDiff) {
1812
- escape = false;
1813
- err.actual = actual = JSON.stringify(actual, null, 2);
1814
- err.expected = expected = JSON.stringify(expected, null, 2);
2163
+ if (index === -1) {
2164
+ msg = message;
2165
+ } else {
2166
+ index += message.length;
2167
+ msg = stack.slice(0, index);
2168
+ // remove msg from stack
2169
+ stack = stack.slice(index + 1);
1815
2170
  }
1816
2171
 
1817
- // actual / expected diff
1818
- if ('string' == typeof actual && 'string' == typeof expected) {
1819
- var len = Math.max(actual.length, expected.length);
1820
-
1821
- if (len < 20) msg = errorDiff(err, 'Chars', escape);
1822
- else msg = errorDiff(err, 'Words', escape);
2172
+ // uncaught
2173
+ if (err.uncaught) {
2174
+ msg = 'Uncaught ' + msg;
2175
+ }
2176
+ // explicitly show diff
2177
+ if (err.showDiff !== false && sameType(actual, expected)
2178
+ && expected !== undefined) {
1823
2179
 
1824
- // linenos
1825
- var lines = msg.split('\n');
1826
- if (lines.length > 4) {
1827
- var width = String(lines.length).length;
1828
- msg = lines.map(function(str, i){
1829
- return pad(++i, width) + ' |' + ' ' + str;
1830
- }).join('\n');
2180
+ if ('string' !== typeof actual) {
2181
+ escape = false;
2182
+ err.actual = actual = utils.stringify(actual);
2183
+ err.expected = expected = utils.stringify(expected);
1831
2184
  }
1832
2185
 
1833
- // legend
1834
- msg = '\n'
1835
- + color('diff removed', 'actual')
1836
- + ' '
1837
- + color('diff added', 'expected')
1838
- + '\n\n'
1839
- + msg
1840
- + '\n';
1841
-
1842
- // indent
1843
- msg = msg.replace(/^/gm, ' ');
2186
+ fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
2187
+ var match = message.match(/^([^:]+): expected/);
2188
+ msg = '\n ' + color('error message', match ? match[1] : msg);
1844
2189
 
1845
- fmt = color('error title', ' %s) %s:\n%s')
1846
- + color('error stack', '\n%s\n');
2190
+ if (exports.inlineDiffs) {
2191
+ msg += inlineDiff(err, escape);
2192
+ } else {
2193
+ msg += unifiedDiff(err, escape);
2194
+ }
1847
2195
  }
1848
2196
 
1849
- // indent stack trace without msg
1850
- stack = stack.slice(index ? index + 1 : index)
1851
- .replace(/^/gm, ' ');
2197
+ // indent stack trace
2198
+ stack = stack.replace(/^/gm, ' ');
1852
2199
 
1853
- console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
2200
+ console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
1854
2201
  });
1855
2202
  };
1856
2203
 
@@ -1928,48 +2275,37 @@ function Base(runner) {
1928
2275
  */
1929
2276
 
1930
2277
  Base.prototype.epilogue = function(){
1931
- var stats = this.stats
1932
- , fmt
1933
- , tests;
2278
+ var stats = this.stats;
2279
+ var tests;
2280
+ var fmt;
1934
2281
 
1935
2282
  console.log();
1936
2283
 
1937
- function pluralize(n) {
1938
- return 1 == n ? 'test' : 'tests';
1939
- }
1940
-
1941
- // failure
1942
- if (stats.failures) {
1943
- fmt = color('bright fail', ' ' + exports.symbols.err)
1944
- + color('fail', ' %d of %d %s failed')
1945
- + color('light', ':')
1946
-
1947
- console.error(fmt,
1948
- stats.failures,
1949
- this.runner.total,
1950
- pluralize(this.runner.total));
1951
-
1952
- Base.list(this.failures);
1953
- console.error();
1954
- return;
1955
- }
1956
-
1957
- // pass
2284
+ // passes
1958
2285
  fmt = color('bright pass', ' ')
1959
- + color('green', ' %d %s complete')
2286
+ + color('green', ' %d passing')
1960
2287
  + color('light', ' (%s)');
1961
2288
 
1962
2289
  console.log(fmt,
1963
- stats.tests || 0,
1964
- pluralize(stats.tests),
2290
+ stats.passes || 0,
1965
2291
  ms(stats.duration));
1966
2292
 
1967
2293
  // pending
1968
2294
  if (stats.pending) {
1969
2295
  fmt = color('pending', ' ')
1970
- + color('pending', ' %d %s pending');
2296
+ + color('pending', ' %d pending');
2297
+
2298
+ console.log(fmt, stats.pending);
2299
+ }
2300
+
2301
+ // failures
2302
+ if (stats.failures) {
2303
+ fmt = color('fail', ' %d failing');
2304
+
2305
+ console.log(fmt, stats.failures);
1971
2306
 
1972
- console.log(fmt, stats.pending, pluralize(stats.pending));
2307
+ Base.list(this.failures);
2308
+ console.log();
1973
2309
  }
1974
2310
 
1975
2311
  console.log();
@@ -1989,6 +2325,73 @@ function pad(str, len) {
1989
2325
  return Array(len - str.length + 1).join(' ') + str;
1990
2326
  }
1991
2327
 
2328
+
2329
+ /**
2330
+ * Returns an inline diff between 2 strings with coloured ANSI output
2331
+ *
2332
+ * @param {Error} Error with actual/expected
2333
+ * @return {String} Diff
2334
+ * @api private
2335
+ */
2336
+
2337
+ function inlineDiff(err, escape) {
2338
+ var msg = errorDiff(err, 'WordsWithSpace', escape);
2339
+
2340
+ // linenos
2341
+ var lines = msg.split('\n');
2342
+ if (lines.length > 4) {
2343
+ var width = String(lines.length).length;
2344
+ msg = lines.map(function(str, i){
2345
+ return pad(++i, width) + ' |' + ' ' + str;
2346
+ }).join('\n');
2347
+ }
2348
+
2349
+ // legend
2350
+ msg = '\n'
2351
+ + color('diff removed', 'actual')
2352
+ + ' '
2353
+ + color('diff added', 'expected')
2354
+ + '\n\n'
2355
+ + msg
2356
+ + '\n';
2357
+
2358
+ // indent
2359
+ msg = msg.replace(/^/gm, ' ');
2360
+ return msg;
2361
+ }
2362
+
2363
+ /**
2364
+ * Returns a unified diff between 2 strings
2365
+ *
2366
+ * @param {Error} Error with actual/expected
2367
+ * @return {String} Diff
2368
+ * @api private
2369
+ */
2370
+
2371
+ function unifiedDiff(err, escape) {
2372
+ var indent = ' ';
2373
+ function cleanUp(line) {
2374
+ if (escape) {
2375
+ line = escapeInvisibles(line);
2376
+ }
2377
+ if (line[0] === '+') return indent + colorLines('diff added', line);
2378
+ if (line[0] === '-') return indent + colorLines('diff removed', line);
2379
+ if (line.match(/\@\@/)) return null;
2380
+ if (line.match(/\\ No newline/)) return null;
2381
+ else return indent + line;
2382
+ }
2383
+ function notBlank(line) {
2384
+ return line != null;
2385
+ }
2386
+ var msg = diff.createPatch('string', err.actual, err.expected);
2387
+ var lines = msg.split('\n').splice(4);
2388
+ return '\n '
2389
+ + colorLines('diff added', '+ expected') + ' '
2390
+ + colorLines('diff removed', '- actual')
2391
+ + '\n\n'
2392
+ + lines.map(cleanUp).filter(notBlank).join('\n');
2393
+ }
2394
+
1992
2395
  /**
1993
2396
  * Return a character diff for `err`.
1994
2397
  *
@@ -1998,19 +2401,28 @@ function pad(str, len) {
1998
2401
  */
1999
2402
 
2000
2403
  function errorDiff(err, type, escape) {
2001
- return diff['diff' + type](err.actual, err.expected).map(function(str){
2002
- if (escape) {
2003
- str.value = str.value
2004
- .replace(/\t/g, '<tab>')
2005
- .replace(/\r/g, '<CR>')
2006
- .replace(/\n/g, '<LF>\n');
2007
- }
2404
+ var actual = escape ? escapeInvisibles(err.actual) : err.actual;
2405
+ var expected = escape ? escapeInvisibles(err.expected) : err.expected;
2406
+ return diff['diff' + type](actual, expected).map(function(str){
2008
2407
  if (str.added) return colorLines('diff added', str.value);
2009
2408
  if (str.removed) return colorLines('diff removed', str.value);
2010
2409
  return str.value;
2011
2410
  }).join('');
2012
2411
  }
2013
2412
 
2413
+ /**
2414
+ * Returns a string with all invisible characters in plain text
2415
+ *
2416
+ * @param {String} line
2417
+ * @return {String}
2418
+ * @api private
2419
+ */
2420
+ function escapeInvisibles(line) {
2421
+ return line.replace(/\t/g, '<tab>')
2422
+ .replace(/\r/g, '<CR>')
2423
+ .replace(/\n/g, '<LF>\n');
2424
+ }
2425
+
2014
2426
  /**
2015
2427
  * Color lines for `str`, using the color `name`.
2016
2428
  *
@@ -2026,10 +2438,24 @@ function colorLines(name, str) {
2026
2438
  }).join('\n');
2027
2439
  }
2028
2440
 
2441
+ /**
2442
+ * Check that a / b have the same type.
2443
+ *
2444
+ * @param {Object} a
2445
+ * @param {Object} b
2446
+ * @return {Boolean}
2447
+ * @api private
2448
+ */
2449
+
2450
+ function sameType(a, b) {
2451
+ a = Object.prototype.toString.call(a);
2452
+ b = Object.prototype.toString.call(b);
2453
+ return a == b;
2454
+ }
2455
+
2029
2456
  }); // module: reporters/base.js
2030
2457
 
2031
2458
  require.register("reporters/doc.js", function(module, exports, require){
2032
-
2033
2459
  /**
2034
2460
  * Module dependencies.
2035
2461
  */
@@ -2084,12 +2510,18 @@ function Doc(runner) {
2084
2510
  var code = utils.escape(utils.clean(test.fn.toString()));
2085
2511
  console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
2086
2512
  });
2513
+
2514
+ runner.on('fail', function(test, err){
2515
+ console.log('%s <dt class="error">%s</dt>', indent(), utils.escape(test.title));
2516
+ var code = utils.escape(utils.clean(test.fn.toString()));
2517
+ console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
2518
+ console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
2519
+ });
2087
2520
  }
2088
2521
 
2089
2522
  }); // module: reporters/doc.js
2090
2523
 
2091
2524
  require.register("reporters/dot.js", function(module, exports, require){
2092
-
2093
2525
  /**
2094
2526
  * Module dependencies.
2095
2527
  */
@@ -2116,13 +2548,14 @@ function Dot(runner) {
2116
2548
  var self = this
2117
2549
  , stats = this.stats
2118
2550
  , width = Base.window.width * .75 | 0
2119
- , n = 0;
2551
+ , n = -1;
2120
2552
 
2121
2553
  runner.on('start', function(){
2122
- process.stdout.write('\n ');
2554
+ process.stdout.write('\n');
2123
2555
  });
2124
2556
 
2125
2557
  runner.on('pending', function(test){
2558
+ if (++n % width == 0) process.stdout.write('\n ');
2126
2559
  process.stdout.write(color('pending', Base.symbols.dot));
2127
2560
  });
2128
2561
 
@@ -2155,10 +2588,10 @@ F.prototype = Base.prototype;
2155
2588
  Dot.prototype = new F;
2156
2589
  Dot.prototype.constructor = Dot;
2157
2590
 
2591
+
2158
2592
  }); // module: reporters/dot.js
2159
2593
 
2160
2594
  require.register("reporters/html-cov.js", function(module, exports, require){
2161
-
2162
2595
  /**
2163
2596
  * Module dependencies.
2164
2597
  */
@@ -2209,10 +2642,10 @@ function coverageClass(n) {
2209
2642
  if (n >= 25) return 'low';
2210
2643
  return 'terrible';
2211
2644
  }
2645
+
2212
2646
  }); // module: reporters/html-cov.js
2213
2647
 
2214
2648
  require.register("reporters/html.js", function(module, exports, require){
2215
-
2216
2649
  /**
2217
2650
  * Module dependencies.
2218
2651
  */
@@ -2233,7 +2666,7 @@ var Date = global.Date
2233
2666
  , clearInterval = global.clearInterval;
2234
2667
 
2235
2668
  /**
2236
- * Expose `Doc`.
2669
+ * Expose `HTML`.
2237
2670
  */
2238
2671
 
2239
2672
  exports = module.exports = HTML;
@@ -2250,13 +2683,13 @@ var statsTemplate = '<ul id="mocha-stats">'
2250
2683
  + '</ul>';
2251
2684
 
2252
2685
  /**
2253
- * Initialize a new `Doc` reporter.
2686
+ * Initialize a new `HTML` reporter.
2254
2687
  *
2255
2688
  * @param {Runner} runner
2256
2689
  * @api public
2257
2690
  */
2258
2691
 
2259
- function HTML(runner, root) {
2692
+ function HTML(runner) {
2260
2693
  Base.call(this, runner);
2261
2694
 
2262
2695
  var self = this
@@ -2274,8 +2707,7 @@ function HTML(runner, root) {
2274
2707
  , stack = [report]
2275
2708
  , progress
2276
2709
  , ctx
2277
-
2278
- root = root || document.getElementById('mocha');
2710
+ , root = document.getElementById('mocha');
2279
2711
 
2280
2712
  if (canvas.getContext) {
2281
2713
  var ratio = window.devicePixelRatio || 1;
@@ -2315,7 +2747,7 @@ function HTML(runner, root) {
2315
2747
  if (suite.root) return;
2316
2748
 
2317
2749
  // suite
2318
- var url = '?grep=' + encodeURIComponent(suite.fullTitle());
2750
+ var url = self.suiteURL(suite);
2319
2751
  var el = fragment('<li class="suite"><h1><a href="%s">%s</a></h1></li>', url, escape(suite.title));
2320
2752
 
2321
2753
  // container
@@ -2334,8 +2766,6 @@ function HTML(runner, root) {
2334
2766
  });
2335
2767
 
2336
2768
  runner.on('test end', function(test){
2337
- window.scrollTo(0, document.body.scrollHeight);
2338
-
2339
2769
  // TODO: add to stats
2340
2770
  var percent = stats.tests / this.total * 100 | 0;
2341
2771
  if (progress) progress.update(percent).draw(ctx);
@@ -2348,11 +2778,12 @@ function HTML(runner, root) {
2348
2778
 
2349
2779
  // test
2350
2780
  if ('passed' == test.state) {
2351
- var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="?grep=%e" class="replay">‣</a></h2></li>', test.speed, test.title, test.duration, encodeURIComponent(test.fullTitle()));
2781
+ var url = self.testURL(test);
2782
+ var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="%s" class="replay">‣</a></h2></li>', test.speed, test.title, test.duration, url);
2352
2783
  } else if (test.pending) {
2353
2784
  var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
2354
2785
  } else {
2355
- var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
2786
+ var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
2356
2787
  var str = test.err.stack || test.err.toString();
2357
2788
 
2358
2789
  // FF / Opera do not add the message
@@ -2393,6 +2824,41 @@ function HTML(runner, root) {
2393
2824
  });
2394
2825
  }
2395
2826
 
2827
+ /**
2828
+ * Makes a URL, preserving querystring ("search") parameters.
2829
+ * @param {string} s
2830
+ * @returns {string} your new URL
2831
+ */
2832
+ var makeUrl = function makeUrl(s) {
2833
+ var search = window.location.search;
2834
+
2835
+ // Remove previous grep query parameter if present
2836
+ if (search) {
2837
+ search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
2838
+ }
2839
+
2840
+ return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
2841
+ };
2842
+
2843
+ /**
2844
+ * Provide suite URL
2845
+ *
2846
+ * @param {Object} [suite]
2847
+ */
2848
+ HTML.prototype.suiteURL = function(suite){
2849
+ return makeUrl(suite.fullTitle());
2850
+ };
2851
+
2852
+ /**
2853
+ * Provide test URL
2854
+ *
2855
+ * @param {Object} [test]
2856
+ */
2857
+
2858
+ HTML.prototype.testURL = function(test){
2859
+ return makeUrl(test.fullTitle());
2860
+ };
2861
+
2396
2862
  /**
2397
2863
  * Display error `msg`.
2398
2864
  */
@@ -2471,7 +2937,6 @@ function on(el, event, fn) {
2471
2937
  }); // module: reporters/html.js
2472
2938
 
2473
2939
  require.register("reporters/index.js", function(module, exports, require){
2474
-
2475
2940
  exports.Base = require('./base');
2476
2941
  exports.Dot = require('./dot');
2477
2942
  exports.Doc = require('./doc');
@@ -2489,12 +2954,10 @@ exports.Landing = require('./landing');
2489
2954
  exports.JSONCov = require('./json-cov');
2490
2955
  exports.HTMLCov = require('./html-cov');
2491
2956
  exports.JSONStream = require('./json-stream');
2492
- exports.Teamcity = require('./teamcity');
2493
2957
 
2494
2958
  }); // module: reporters/index.js
2495
2959
 
2496
2960
  require.register("reporters/json-cov.js", function(module, exports, require){
2497
-
2498
2961
  /**
2499
2962
  * Module dependencies.
2500
2963
  */
@@ -2585,7 +3048,7 @@ function map(cov) {
2585
3048
  }
2586
3049
 
2587
3050
  return ret;
2588
- };
3051
+ }
2589
3052
 
2590
3053
  /**
2591
3054
  * Map jscoverage data for a single source file
@@ -2651,7 +3114,6 @@ function clean(test) {
2651
3114
  }); // module: reporters/json-cov.js
2652
3115
 
2653
3116
  require.register("reporters/json-stream.js", function(module, exports, require){
2654
-
2655
3117
  /**
2656
3118
  * Module dependencies.
2657
3119
  */
@@ -2688,7 +3150,9 @@ function List(runner) {
2688
3150
  });
2689
3151
 
2690
3152
  runner.on('fail', function(test, err){
2691
- console.log(JSON.stringify(['fail', clean(test)]));
3153
+ test = clean(test);
3154
+ test.err = err.message;
3155
+ console.log(JSON.stringify(['fail', test]));
2692
3156
  });
2693
3157
 
2694
3158
  runner.on('end', function(){
@@ -2712,10 +3176,10 @@ function clean(test) {
2712
3176
  , duration: test.duration
2713
3177
  }
2714
3178
  }
3179
+
2715
3180
  }); // module: reporters/json-stream.js
2716
3181
 
2717
3182
  require.register("reporters/json.js", function(module, exports, require){
2718
-
2719
3183
  /**
2720
3184
  * Module dependencies.
2721
3185
  */
@@ -2742,6 +3206,7 @@ function JSONReporter(runner) {
2742
3206
  Base.call(this, runner);
2743
3207
 
2744
3208
  var tests = []
3209
+ , pending = []
2745
3210
  , failures = []
2746
3211
  , passes = [];
2747
3212
 
@@ -2757,14 +3222,21 @@ function JSONReporter(runner) {
2757
3222
  failures.push(test);
2758
3223
  });
2759
3224
 
3225
+ runner.on('pending', function(test){
3226
+ pending.push(test);
3227
+ });
3228
+
2760
3229
  runner.on('end', function(){
2761
3230
  var obj = {
2762
- stats: self.stats
2763
- , tests: tests.map(clean)
2764
- , failures: failures.map(clean)
2765
- , passes: passes.map(clean)
3231
+ stats: self.stats,
3232
+ tests: tests.map(clean),
3233
+ pending: pending.map(clean),
3234
+ failures: failures.map(clean),
3235
+ passes: passes.map(clean)
2766
3236
  };
2767
3237
 
3238
+ runner.testResults = obj;
3239
+
2768
3240
  process.stdout.write(JSON.stringify(obj, null, 2));
2769
3241
  });
2770
3242
  }
@@ -2780,15 +3252,30 @@ function JSONReporter(runner) {
2780
3252
 
2781
3253
  function clean(test) {
2782
3254
  return {
2783
- title: test.title
2784
- , fullTitle: test.fullTitle()
2785
- , duration: test.duration
3255
+ title: test.title,
3256
+ fullTitle: test.fullTitle(),
3257
+ duration: test.duration,
3258
+ err: errorJSON(test.err || {})
2786
3259
  }
2787
3260
  }
3261
+
3262
+ /**
3263
+ * Transform `error` into a JSON object.
3264
+ * @param {Error} err
3265
+ * @return {Object}
3266
+ */
3267
+
3268
+ function errorJSON(err) {
3269
+ var res = {};
3270
+ Object.getOwnPropertyNames(err).forEach(function(key) {
3271
+ res[key] = err[key];
3272
+ }, err);
3273
+ return res;
3274
+ }
3275
+
2788
3276
  }); // module: reporters/json.js
2789
3277
 
2790
3278
  require.register("reporters/landing.js", function(module, exports, require){
2791
-
2792
3279
  /**
2793
3280
  * Module dependencies.
2794
3281
  */
@@ -2846,7 +3333,7 @@ function Landing(runner) {
2846
3333
  }
2847
3334
 
2848
3335
  runner.on('start', function(){
2849
- stream.write('\n ');
3336
+ stream.write('\n\n\n ');
2850
3337
  cursor.hide();
2851
3338
  });
2852
3339
 
@@ -2863,7 +3350,7 @@ function Landing(runner) {
2863
3350
  }
2864
3351
 
2865
3352
  // render landing strip
2866
- stream.write('\u001b[4F\n\n');
3353
+ stream.write('\u001b['+(width+1)+'D\u001b[2A');
2867
3354
  stream.write(runway());
2868
3355
  stream.write('\n ');
2869
3356
  stream.write(color('runway', Array(col).join('⋅')));
@@ -2889,10 +3376,10 @@ F.prototype = Base.prototype;
2889
3376
  Landing.prototype = new F;
2890
3377
  Landing.prototype.constructor = Landing;
2891
3378
 
3379
+
2892
3380
  }); // module: reporters/landing.js
2893
3381
 
2894
3382
  require.register("reporters/list.js", function(module, exports, require){
2895
-
2896
3383
  /**
2897
3384
  * Module dependencies.
2898
3385
  */
@@ -2971,6 +3458,12 @@ require.register("reporters/markdown.js", function(module, exports, require){
2971
3458
  var Base = require('./base')
2972
3459
  , utils = require('../utils');
2973
3460
 
3461
+ /**
3462
+ * Constants
3463
+ */
3464
+
3465
+ var SUITE_PREFIX = '$';
3466
+
2974
3467
  /**
2975
3468
  * Expose `Markdown`.
2976
3469
  */
@@ -3001,8 +3494,9 @@ function Markdown(runner) {
3001
3494
  }
3002
3495
 
3003
3496
  function mapTOC(suite, obj) {
3004
- var ret = obj;
3005
- obj = obj[suite.title] = obj[suite.title] || { suite: suite };
3497
+ var ret = obj,
3498
+ key = SUITE_PREFIX + suite.title;
3499
+ obj = obj[key] = obj[key] || { suite: suite };
3006
3500
  suite.suites.forEach(function(suite){
3007
3501
  mapTOC(suite, obj);
3008
3502
  });
@@ -3015,11 +3509,13 @@ function Markdown(runner) {
3015
3509
  var link;
3016
3510
  for (var key in obj) {
3017
3511
  if ('suite' == key) continue;
3018
- if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
3019
- if (key) buf += Array(level).join(' ') + link;
3512
+ if (key !== SUITE_PREFIX) {
3513
+ link = ' - [' + key.substring(1) + ']';
3514
+ link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
3515
+ buf += Array(level).join(' ') + link;
3516
+ }
3020
3517
  buf += stringifyTOC(obj[key], level);
3021
3518
  }
3022
- --level;
3023
3519
  return buf;
3024
3520
  }
3025
3521
 
@@ -3055,10 +3551,10 @@ function Markdown(runner) {
3055
3551
  process.stdout.write(buf);
3056
3552
  });
3057
3553
  }
3554
+
3058
3555
  }); // module: reporters/markdown.js
3059
3556
 
3060
3557
  require.register("reporters/min.js", function(module, exports, require){
3061
-
3062
3558
  /**
3063
3559
  * Module dependencies.
3064
3560
  */
@@ -3100,16 +3596,15 @@ F.prototype = Base.prototype;
3100
3596
  Min.prototype = new F;
3101
3597
  Min.prototype.constructor = Min;
3102
3598
 
3599
+
3103
3600
  }); // module: reporters/min.js
3104
3601
 
3105
3602
  require.register("reporters/nyan.js", function(module, exports, require){
3106
-
3107
3603
  /**
3108
3604
  * Module dependencies.
3109
3605
  */
3110
3606
 
3111
- var Base = require('./base')
3112
- , color = Base.color;
3607
+ var Base = require('./base');
3113
3608
 
3114
3609
  /**
3115
3610
  * Expose `Dot`.
@@ -3126,7 +3621,6 @@ exports = module.exports = NyanCat;
3126
3621
 
3127
3622
  function NyanCat(runner) {
3128
3623
  Base.call(this, runner);
3129
-
3130
3624
  var self = this
3131
3625
  , stats = this.stats
3132
3626
  , width = Base.window.width * .75 | 0
@@ -3142,19 +3636,19 @@ function NyanCat(runner) {
3142
3636
 
3143
3637
  runner.on('start', function(){
3144
3638
  Base.cursor.hide();
3145
- self.draw('start');
3639
+ self.draw();
3146
3640
  });
3147
3641
 
3148
3642
  runner.on('pending', function(test){
3149
- self.draw('pending');
3643
+ self.draw();
3150
3644
  });
3151
3645
 
3152
3646
  runner.on('pass', function(test){
3153
- self.draw('pass');
3647
+ self.draw();
3154
3648
  });
3155
3649
 
3156
3650
  runner.on('fail', function(test, err){
3157
- self.draw('fail');
3651
+ self.draw();
3158
3652
  });
3159
3653
 
3160
3654
  runner.on('end', function(){
@@ -3165,17 +3659,16 @@ function NyanCat(runner) {
3165
3659
  }
3166
3660
 
3167
3661
  /**
3168
- * Draw the nyan cat with runner `status`.
3662
+ * Draw the nyan cat
3169
3663
  *
3170
- * @param {String} status
3171
3664
  * @api private
3172
3665
  */
3173
3666
 
3174
- NyanCat.prototype.draw = function(status){
3667
+ NyanCat.prototype.draw = function(){
3175
3668
  this.appendRainbow();
3176
3669
  this.drawScoreboard();
3177
3670
  this.drawRainbow();
3178
- this.drawNyanCat(status);
3671
+ this.drawNyanCat();
3179
3672
  this.tick = !this.tick;
3180
3673
  };
3181
3674
 
@@ -3188,17 +3681,16 @@ NyanCat.prototype.draw = function(status){
3188
3681
 
3189
3682
  NyanCat.prototype.drawScoreboard = function(){
3190
3683
  var stats = this.stats;
3191
- var colors = Base.colors;
3192
3684
 
3193
- function draw(color, n) {
3685
+ function draw(type, n) {
3194
3686
  write(' ');
3195
- write('\u001b[' + color + 'm' + n + '\u001b[0m');
3687
+ write(Base.color(type, n));
3196
3688
  write('\n');
3197
3689
  }
3198
3690
 
3199
- draw(colors.green, stats.passes);
3200
- draw(colors.fail, stats.failures);
3201
- draw(colors.pending, stats.pending);
3691
+ draw('green', stats.passes);
3692
+ draw('fail', stats.failures);
3693
+ draw('pending', stats.pending);
3202
3694
  write('\n');
3203
3695
 
3204
3696
  this.cursorUp(this.numberOfLines);
@@ -3240,65 +3732,69 @@ NyanCat.prototype.drawRainbow = function(){
3240
3732
  };
3241
3733
 
3242
3734
  /**
3243
- * Draw the nyan cat with `status`.
3735
+ * Draw the nyan cat
3244
3736
  *
3245
- * @param {String} status
3246
3737
  * @api private
3247
3738
  */
3248
3739
 
3249
- NyanCat.prototype.drawNyanCat = function(status) {
3740
+ NyanCat.prototype.drawNyanCat = function() {
3250
3741
  var self = this;
3251
3742
  var startWidth = this.scoreboardWidth + this.trajectories[0].length;
3743
+ var dist = '\u001b[' + startWidth + 'C';
3744
+ var padding = '';
3252
3745
 
3253
- [0, 1, 2, 3].forEach(function(index) {
3254
- write('\u001b[' + startWidth + 'C');
3746
+ write(dist);
3747
+ write('_,------,');
3748
+ write('\n');
3255
3749
 
3256
- switch (index) {
3257
- case 0:
3258
- write('_,------,');
3259
- write('\n');
3260
- break;
3261
- case 1:
3262
- var padding = self.tick ? ' ' : ' ';
3263
- write('_|' + padding + '/\\_/\\ ');
3264
- write('\n');
3265
- break;
3266
- case 2:
3267
- var padding = self.tick ? '_' : '__';
3268
- var tail = self.tick ? '~' : '^';
3269
- var face;
3270
- switch (status) {
3271
- case 'pass':
3272
- face = '( ^ .^)';
3273
- break;
3274
- case 'fail':
3275
- face = '( o .o)';
3276
- break;
3277
- default:
3278
- face = '( - .-)';
3279
- }
3280
- write(tail + '|' + padding + face + ' ');
3281
- write('\n');
3282
- break;
3283
- case 3:
3284
- var padding = self.tick ? ' ' : ' ';
3285
- write(padding + '"" "" ');
3286
- write('\n');
3287
- break;
3288
- }
3289
- });
3750
+ write(dist);
3751
+ padding = self.tick ? ' ' : ' ';
3752
+ write('_|' + padding + '/\\_/\\ ');
3753
+ write('\n');
3754
+
3755
+ write(dist);
3756
+ padding = self.tick ? '_' : '__';
3757
+ var tail = self.tick ? '~' : '^';
3758
+ var face;
3759
+ write(tail + '|' + padding + this.face() + ' ');
3760
+ write('\n');
3761
+
3762
+ write(dist);
3763
+ padding = self.tick ? ' ' : ' ';
3764
+ write(padding + '"" "" ');
3765
+ write('\n');
3290
3766
 
3291
3767
  this.cursorUp(this.numberOfLines);
3292
3768
  };
3293
3769
 
3294
3770
  /**
3295
- * Move cursor up `n`.
3771
+ * Draw nyan cat face.
3296
3772
  *
3297
- * @param {Number} n
3773
+ * @return {String}
3298
3774
  * @api private
3299
3775
  */
3300
3776
 
3301
- NyanCat.prototype.cursorUp = function(n) {
3777
+ NyanCat.prototype.face = function() {
3778
+ var stats = this.stats;
3779
+ if (stats.failures) {
3780
+ return '( x .x)';
3781
+ } else if (stats.pending) {
3782
+ return '( o .o)';
3783
+ } else if(stats.passes) {
3784
+ return '( ^ .^)';
3785
+ } else {
3786
+ return '( - .-)';
3787
+ }
3788
+ };
3789
+
3790
+ /**
3791
+ * Move cursor up `n`.
3792
+ *
3793
+ * @param {Number} n
3794
+ * @api private
3795
+ */
3796
+
3797
+ NyanCat.prototype.cursorUp = function(n) {
3302
3798
  write('\u001b[' + n + 'A');
3303
3799
  };
3304
3800
 
@@ -3344,6 +3840,8 @@ NyanCat.prototype.generateColors = function(){
3344
3840
  */
3345
3841
 
3346
3842
  NyanCat.prototype.rainbowify = function(str){
3843
+ if (!Base.useColors)
3844
+ return str;
3347
3845
  var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
3348
3846
  this.colorIndex += 1;
3349
3847
  return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
@@ -3370,7 +3868,6 @@ NyanCat.prototype.constructor = NyanCat;
3370
3868
  }); // module: reporters/nyan.js
3371
3869
 
3372
3870
  require.register("reporters/progress.js", function(module, exports, require){
3373
-
3374
3871
  /**
3375
3872
  * Module dependencies.
3376
3873
  */
@@ -3408,7 +3905,8 @@ function Progress(runner, options) {
3408
3905
  , width = Base.window.width * .50 | 0
3409
3906
  , total = runner.total
3410
3907
  , complete = 0
3411
- , max = Math.max;
3908
+ , max = Math.max
3909
+ , lastN = -1;
3412
3910
 
3413
3911
  // default chars
3414
3912
  options.open = options.open || '[';
@@ -3431,6 +3929,12 @@ function Progress(runner, options) {
3431
3929
  , n = width * percent | 0
3432
3930
  , i = width - n;
3433
3931
 
3932
+ if (lastN === n && !options.verbose) {
3933
+ // Don't re-render the line if it hasn't changed
3934
+ return;
3935
+ }
3936
+ lastN = n;
3937
+
3434
3938
  cursor.CR();
3435
3939
  process.stdout.write('\u001b[J');
3436
3940
  process.stdout.write(color('progress', ' ' + options.open));
@@ -3464,7 +3968,6 @@ Progress.prototype.constructor = Progress;
3464
3968
  }); // module: reporters/progress.js
3465
3969
 
3466
3970
  require.register("reporters/spec.js", function(module, exports, require){
3467
-
3468
3971
  /**
3469
3972
  * Module dependencies.
3470
3973
  */
@@ -3512,10 +4015,6 @@ function Spec(runner) {
3512
4015
  if (1 == indents) console.log();
3513
4016
  });
3514
4017
 
3515
- runner.on('test', function(test){
3516
- process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': '));
3517
- });
3518
-
3519
4018
  runner.on('pending', function(test){
3520
4019
  var fmt = indent() + color('pending', ' - %s');
3521
4020
  console.log(fmt, test.title);
@@ -3525,14 +4024,14 @@ function Spec(runner) {
3525
4024
  if ('fast' == test.speed) {
3526
4025
  var fmt = indent()
3527
4026
  + color('checkmark', ' ' + Base.symbols.ok)
3528
- + color('pass', ' %s ');
4027
+ + color('pass', ' %s');
3529
4028
  cursor.CR();
3530
4029
  console.log(fmt, test.title);
3531
4030
  } else {
3532
4031
  var fmt = indent()
3533
4032
  + color('checkmark', ' ' + Base.symbols.ok)
3534
- + color('pass', ' %s ')
3535
- + color(test.speed, '(%dms)');
4033
+ + color('pass', ' %s')
4034
+ + color(test.speed, ' (%dms)');
3536
4035
  cursor.CR();
3537
4036
  console.log(fmt, test.title, test.duration);
3538
4037
  }
@@ -3559,7 +4058,6 @@ Spec.prototype.constructor = Spec;
3559
4058
  }); // module: reporters/spec.js
3560
4059
 
3561
4060
  require.register("reporters/tap.js", function(module, exports, require){
3562
-
3563
4061
  /**
3564
4062
  * Module dependencies.
3565
4063
  */
@@ -3635,83 +4133,14 @@ function title(test) {
3635
4133
 
3636
4134
  }); // module: reporters/tap.js
3637
4135
 
3638
- require.register("reporters/teamcity.js", function(module, exports, require){
3639
-
3640
- /**
3641
- * Module dependencies.
3642
- */
3643
-
3644
- var Base = require('./base');
3645
-
3646
- /**
3647
- * Expose `Teamcity`.
3648
- */
3649
-
3650
- exports = module.exports = Teamcity;
3651
-
3652
- /**
3653
- * Initialize a new `Teamcity` reporter.
3654
- *
3655
- * @param {Runner} runner
3656
- * @api public
3657
- */
3658
-
3659
- function Teamcity(runner) {
3660
- Base.call(this, runner);
3661
- var stats = this.stats;
3662
-
3663
- runner.on('start', function() {
3664
- console.log("##teamcity[testSuiteStarted name='mocha.suite']");
3665
- });
3666
-
3667
- runner.on('test', function(test) {
3668
- console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']");
3669
- });
3670
-
3671
- runner.on('fail', function(test, err) {
3672
- console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']");
3673
- });
3674
-
3675
- runner.on('pending', function(test) {
3676
- console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']");
3677
- });
3678
-
3679
- runner.on('test end', function(test) {
3680
- console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']");
3681
- });
3682
-
3683
- runner.on('end', function() {
3684
- console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']");
3685
- });
3686
- }
3687
-
3688
- /**
3689
- * Escape the given `str`.
3690
- */
3691
-
3692
- function escape(str) {
3693
- return str
3694
- .replace(/\|/g, "||")
3695
- .replace(/\n/g, "|n")
3696
- .replace(/\r/g, "|r")
3697
- .replace(/\[/g, "|[")
3698
- .replace(/\]/g, "|]")
3699
- .replace(/\u0085/g, "|x")
3700
- .replace(/\u2028/g, "|l")
3701
- .replace(/\u2029/g, "|p")
3702
- .replace(/'/g, "|'");
3703
- }
3704
-
3705
- }); // module: reporters/teamcity.js
3706
-
3707
4136
  require.register("reporters/xunit.js", function(module, exports, require){
3708
-
3709
4137
  /**
3710
4138
  * Module dependencies.
3711
4139
  */
3712
4140
 
3713
4141
  var Base = require('./base')
3714
4142
  , utils = require('../utils')
4143
+ , fs = require('browser/fs')
3715
4144
  , escape = utils.escape;
3716
4145
 
3717
4146
  /**
@@ -3737,12 +4166,23 @@ exports = module.exports = XUnit;
3737
4166
  * @api public
3738
4167
  */
3739
4168
 
3740
- function XUnit(runner) {
4169
+ function XUnit(runner, options) {
3741
4170
  Base.call(this, runner);
3742
4171
  var stats = this.stats
3743
4172
  , tests = []
3744
4173
  , self = this;
3745
4174
 
4175
+ if (options.reporterOptions && options.reporterOptions.output) {
4176
+ if (! fs.createWriteStream) {
4177
+ throw new Error('file output not supported in browser');
4178
+ }
4179
+ self.fileStream = fs.createWriteStream(options.reporterOptions.output);
4180
+ }
4181
+
4182
+ runner.on('pending', function(test){
4183
+ tests.push(test);
4184
+ });
4185
+
3746
4186
  runner.on('pass', function(test){
3747
4187
  tests.push(test);
3748
4188
  });
@@ -3752,21 +4192,34 @@ function XUnit(runner) {
3752
4192
  });
3753
4193
 
3754
4194
  runner.on('end', function(){
3755
- console.log(tag('testsuite', {
4195
+ self.write(tag('testsuite', {
3756
4196
  name: 'Mocha Tests'
3757
4197
  , tests: stats.tests
3758
4198
  , failures: stats.failures
3759
4199
  , errors: stats.failures
3760
- , skip: stats.tests - stats.failures - stats.passes
4200
+ , skipped: stats.tests - stats.failures - stats.passes
3761
4201
  , timestamp: (new Date).toUTCString()
3762
- , time: stats.duration / 1000
4202
+ , time: (stats.duration / 1000) || 0
3763
4203
  }, false));
3764
4204
 
3765
- tests.forEach(test);
3766
- console.log('</testsuite>');
4205
+ tests.forEach(function(t) { self.test(t); });
4206
+ self.write('</testsuite>');
3767
4207
  });
3768
4208
  }
3769
4209
 
4210
+ /**
4211
+ * Override done to close the stream (if it's a file).
4212
+ */
4213
+ XUnit.prototype.done = function(failures, fn) {
4214
+ if (this.fileStream) {
4215
+ this.fileStream.end(function() {
4216
+ fn(failures);
4217
+ });
4218
+ } else {
4219
+ fn(failures);
4220
+ }
4221
+ };
4222
+
3770
4223
  /**
3771
4224
  * Inherit from `Base.prototype`.
3772
4225
  */
@@ -3777,27 +4230,37 @@ XUnit.prototype = new F;
3777
4230
  XUnit.prototype.constructor = XUnit;
3778
4231
 
3779
4232
 
4233
+ /**
4234
+ * Write out the given line
4235
+ */
4236
+ XUnit.prototype.write = function(line) {
4237
+ if (this.fileStream) {
4238
+ this.fileStream.write(line + '\n');
4239
+ } else {
4240
+ console.log(line);
4241
+ }
4242
+ };
4243
+
3780
4244
  /**
3781
4245
  * Output tag for the given `test.`
3782
4246
  */
3783
4247
 
3784
- function test(test) {
4248
+ XUnit.prototype.test = function(test, ostream) {
3785
4249
  var attrs = {
3786
4250
  classname: test.parent.fullTitle()
3787
4251
  , name: test.title
3788
- , time: test.duration / 1000
4252
+ , time: (test.duration / 1000) || 0
3789
4253
  };
3790
4254
 
3791
4255
  if ('failed' == test.state) {
3792
4256
  var err = test.err;
3793
- attrs.message = escape(err.message);
3794
- console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
4257
+ this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
3795
4258
  } else if (test.pending) {
3796
- console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
4259
+ this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
3797
4260
  } else {
3798
- console.log(tag('testcase', attrs, true) );
4261
+ this.write(tag('testcase', attrs, true) );
3799
4262
  }
3800
- }
4263
+ };
3801
4264
 
3802
4265
  /**
3803
4266
  * HTML tag helper.
@@ -3828,14 +4291,15 @@ function cdata(str) {
3828
4291
  }); // module: reporters/xunit.js
3829
4292
 
3830
4293
  require.register("runnable.js", function(module, exports, require){
3831
-
3832
4294
  /**
3833
4295
  * Module dependencies.
3834
4296
  */
3835
4297
 
3836
4298
  var EventEmitter = require('browser/events').EventEmitter
3837
4299
  , debug = require('browser/debug')('mocha:runnable')
3838
- , milliseconds = require('./ms');
4300
+ , Pending = require('./pending')
4301
+ , milliseconds = require('./ms')
4302
+ , utils = require('./utils');
3839
4303
 
3840
4304
  /**
3841
4305
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -3874,7 +4338,9 @@ function Runnable(title, fn) {
3874
4338
  this.sync = ! this.async;
3875
4339
  this._timeout = 2000;
3876
4340
  this._slow = 75;
4341
+ this._enableTimeouts = true;
3877
4342
  this.timedOut = false;
4343
+ this._trace = new Error('done() called multiple times')
3878
4344
  }
3879
4345
 
3880
4346
  /**
@@ -3897,6 +4363,7 @@ Runnable.prototype.constructor = Runnable;
3897
4363
 
3898
4364
  Runnable.prototype.timeout = function(ms){
3899
4365
  if (0 == arguments.length) return this._timeout;
4366
+ if (ms === 0) this._enableTimeouts = false;
3900
4367
  if ('string' == typeof ms) ms = milliseconds(ms);
3901
4368
  debug('timeout %d', ms);
3902
4369
  this._timeout = ms;
@@ -3920,6 +4387,31 @@ Runnable.prototype.slow = function(ms){
3920
4387
  return this;
3921
4388
  };
3922
4389
 
4390
+ /**
4391
+ * Set and & get timeout `enabled`.
4392
+ *
4393
+ * @param {Boolean} enabled
4394
+ * @return {Runnable|Boolean} enabled or self
4395
+ * @api private
4396
+ */
4397
+
4398
+ Runnable.prototype.enableTimeouts = function(enabled){
4399
+ if (arguments.length === 0) return this._enableTimeouts;
4400
+ debug('enableTimeouts %s', enabled);
4401
+ this._enableTimeouts = enabled;
4402
+ return this;
4403
+ };
4404
+
4405
+ /**
4406
+ * Halt and mark as pending.
4407
+ *
4408
+ * @api private
4409
+ */
4410
+
4411
+ Runnable.prototype.skip = function(){
4412
+ throw new Pending();
4413
+ };
4414
+
3923
4415
  /**
3924
4416
  * Return the full title generated by recursively
3925
4417
  * concatenating the parent's full title.
@@ -3965,16 +4457,26 @@ Runnable.prototype.inspect = function(){
3965
4457
  */
3966
4458
 
3967
4459
  Runnable.prototype.resetTimeout = function(){
3968
- var self = this
3969
- , ms = this.timeout();
4460
+ var self = this;
4461
+ var ms = this.timeout() || 1e9;
3970
4462
 
4463
+ if (!this._enableTimeouts) return;
3971
4464
  this.clearTimeout();
3972
- if (ms) {
3973
- this.timer = setTimeout(function(){
3974
- self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
3975
- self.timedOut = true;
3976
- }, ms);
3977
- }
4465
+ this.timer = setTimeout(function(){
4466
+ if (!self._enableTimeouts) return;
4467
+ self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'));
4468
+ self.timedOut = true;
4469
+ }, ms);
4470
+ };
4471
+
4472
+ /**
4473
+ * Whitelist these globals for this test run
4474
+ *
4475
+ * @api private
4476
+ */
4477
+ Runnable.prototype.globals = function(arr){
4478
+ var self = this;
4479
+ this._allowedGlobals = arr;
3978
4480
  };
3979
4481
 
3980
4482
  /**
@@ -3986,54 +4488,58 @@ Runnable.prototype.resetTimeout = function(){
3986
4488
 
3987
4489
  Runnable.prototype.run = function(fn){
3988
4490
  var self = this
3989
- , ms = this.timeout()
3990
4491
  , start = new Date
3991
4492
  , ctx = this.ctx
3992
4493
  , finished
3993
4494
  , emitted;
3994
4495
 
3995
- if (ctx) ctx.runnable(this);
3996
-
3997
- // timeout
3998
- if (this.async) {
3999
- if (ms) {
4000
- this.timer = setTimeout(function(){
4001
- done(new Error('timeout of ' + ms + 'ms exceeded'));
4002
- self.timedOut = true;
4003
- }, ms);
4004
- }
4005
- }
4496
+ // Some times the ctx exists but it is not runnable
4497
+ if (ctx && ctx.runnable) ctx.runnable(this);
4006
4498
 
4007
4499
  // called multiple times
4008
4500
  function multiple(err) {
4009
4501
  if (emitted) return;
4010
4502
  emitted = true;
4011
- self.emit('error', err || new Error('done() called multiple times'));
4503
+ self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
4012
4504
  }
4013
4505
 
4014
4506
  // finished
4015
4507
  function done(err) {
4508
+ var ms = self.timeout();
4016
4509
  if (self.timedOut) return;
4017
- if (finished) return multiple(err);
4510
+ if (finished) return multiple(err || self._trace);
4511
+
4512
+ // Discard the resolution if this test has already failed asynchronously
4513
+ if (self.state) return;
4514
+
4018
4515
  self.clearTimeout();
4019
4516
  self.duration = new Date - start;
4020
4517
  finished = true;
4518
+ if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.');
4021
4519
  fn(err);
4022
4520
  }
4023
4521
 
4024
4522
  // for .resetTimeout()
4025
4523
  this.callback = done;
4026
4524
 
4027
- // async
4525
+ // explicit async with `done` argument
4028
4526
  if (this.async) {
4527
+ this.resetTimeout();
4528
+
4029
4529
  try {
4030
4530
  this.fn.call(ctx, function(err){
4031
4531
  if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
4032
- if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
4532
+ if (null != err) {
4533
+ if (Object.prototype.toString.call(err) === '[object Object]') {
4534
+ return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
4535
+ } else {
4536
+ return done(new Error('done() invoked with non-Error: ' + err));
4537
+ }
4538
+ }
4033
4539
  done();
4034
4540
  });
4035
4541
  } catch (err) {
4036
- done(err);
4542
+ done(utils.getError(err));
4037
4543
  }
4038
4544
  return;
4039
4545
  }
@@ -4042,31 +4548,51 @@ Runnable.prototype.run = function(fn){
4042
4548
  return done(new Error('--async-only option in use without declaring `done()`'));
4043
4549
  }
4044
4550
 
4045
- // sync
4551
+ // sync or promise-returning
4046
4552
  try {
4047
- if (!this.pending) this.fn.call(ctx);
4048
- this.duration = new Date - start;
4049
- fn();
4553
+ if (this.pending) {
4554
+ done();
4555
+ } else {
4556
+ callFn(this.fn);
4557
+ }
4050
4558
  } catch (err) {
4051
- fn(err);
4559
+ done(utils.getError(err));
4560
+ }
4561
+
4562
+ function callFn(fn) {
4563
+ var result = fn.call(ctx);
4564
+ if (result && typeof result.then === 'function') {
4565
+ self.resetTimeout();
4566
+ result
4567
+ .then(function() {
4568
+ done()
4569
+ },
4570
+ function(reason) {
4571
+ done(reason || new Error('Promise rejected with no or falsy reason'))
4572
+ });
4573
+ } else {
4574
+ done();
4575
+ }
4052
4576
  }
4053
4577
  };
4054
4578
 
4055
4579
  }); // module: runnable.js
4056
4580
 
4057
4581
  require.register("runner.js", function(module, exports, require){
4058
-
4059
4582
  /**
4060
4583
  * Module dependencies.
4061
4584
  */
4062
4585
 
4063
4586
  var EventEmitter = require('browser/events').EventEmitter
4064
4587
  , debug = require('browser/debug')('mocha:runner')
4588
+ , Pending = require('./pending')
4065
4589
  , Test = require('./test')
4066
4590
  , utils = require('./utils')
4067
4591
  , filter = utils.filter
4068
4592
  , keys = utils.keys
4069
- , noop = function(){};
4593
+ , type = utils.type
4594
+ , stringify = utils.stringify
4595
+ , stackFilter = utils.stackTraceFilter();
4070
4596
 
4071
4597
  /**
4072
4598
  * Non-enumerable globals.
@@ -4078,7 +4604,9 @@ var globals = [
4078
4604
  'setInterval',
4079
4605
  'clearInterval',
4080
4606
  'XMLHttpRequest',
4081
- 'Date'
4607
+ 'Date',
4608
+ 'setImmediate',
4609
+ 'clearImmediate'
4082
4610
  ];
4083
4611
 
4084
4612
  /**
@@ -4102,22 +4630,37 @@ module.exports = Runner;
4102
4630
  * - `hook end` (hook) hook complete
4103
4631
  * - `pass` (test) test passed
4104
4632
  * - `fail` (test, err) test failed
4633
+ * - `pending` (test) test pending
4105
4634
  *
4635
+ * @param {Suite} suite Root suite
4636
+ * @param {boolean} [delay] Whether or not to delay execution of root suite
4637
+ * until ready.
4106
4638
  * @api public
4107
4639
  */
4108
4640
 
4109
- function Runner(suite) {
4641
+ function Runner(suite, delay) {
4110
4642
  var self = this;
4111
4643
  this._globals = [];
4644
+ this._abort = false;
4645
+ this._delay = delay;
4112
4646
  this.suite = suite;
4113
4647
  this.total = suite.total();
4114
4648
  this.failures = 0;
4115
4649
  this.on('test end', function(test){ self.checkGlobals(test); });
4116
4650
  this.on('hook end', function(hook){ self.checkGlobals(hook); });
4117
4651
  this.grep(/.*/);
4118
- this.globals(this.globalProps().concat(['errno']));
4652
+ this.globals(this.globalProps().concat(extraGlobals()));
4119
4653
  }
4120
4654
 
4655
+ /**
4656
+ * Wrapper for setImmediate, process.nextTick, or browser polyfill.
4657
+ *
4658
+ * @param {Function} fn
4659
+ * @api private
4660
+ */
4661
+
4662
+ Runner.immediately = global.setImmediate || process.nextTick;
4663
+
4121
4664
  /**
4122
4665
  * Inherit from `EventEmitter.prototype`.
4123
4666
  */
@@ -4198,9 +4741,7 @@ Runner.prototype.globalProps = function() {
4198
4741
  Runner.prototype.globals = function(arr){
4199
4742
  if (0 == arguments.length) return this._globals;
4200
4743
  debug('globals %j', arr);
4201
- utils.forEach(arr, function(arr){
4202
- this._globals.push(arr);
4203
- }, this);
4744
+ this._globals = this._globals.concat(arr);
4204
4745
  return this;
4205
4746
  };
4206
4747
 
@@ -4213,13 +4754,16 @@ Runner.prototype.globals = function(arr){
4213
4754
  Runner.prototype.checkGlobals = function(test){
4214
4755
  if (this.ignoreLeaks) return;
4215
4756
  var ok = this._globals;
4757
+
4216
4758
  var globals = this.globalProps();
4217
- var isNode = process.kill;
4218
4759
  var leaks;
4219
4760
 
4220
- // check length - 2 ('errno' and 'location' globals)
4221
- if (isNode && 1 == ok.length - globals.length) return
4222
- else if (2 == ok.length - globals.length) return;
4761
+ if (test) {
4762
+ ok = ok.concat(test._allowedGlobals || []);
4763
+ }
4764
+
4765
+ if(this.prevGlobalsLength == globals.length) return;
4766
+ this.prevGlobalsLength = globals.length;
4223
4767
 
4224
4768
  leaks = filterLeaks(ok, globals);
4225
4769
  this._globals = this._globals.concat(leaks);
@@ -4239,24 +4783,36 @@ Runner.prototype.checkGlobals = function(test){
4239
4783
  * @api private
4240
4784
  */
4241
4785
 
4242
- Runner.prototype.fail = function(test, err){
4786
+ Runner.prototype.fail = function(test, err) {
4243
4787
  ++this.failures;
4244
4788
  test.state = 'failed';
4245
4789
 
4246
- if ('string' == typeof err) {
4247
- err = new Error('the string "' + err + '" was thrown, throw an Error :)');
4790
+ if (!(err instanceof Error)) {
4791
+ err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)');
4248
4792
  }
4249
4793
 
4794
+ err.stack = (this.fullStackTrace || !err.stack)
4795
+ ? err.stack
4796
+ : stackFilter(err.stack);
4797
+
4250
4798
  this.emit('fail', test, err);
4251
4799
  };
4252
4800
 
4253
4801
  /**
4254
4802
  * Fail the given `hook` with `err`.
4255
4803
  *
4256
- * Hook failures (currently) hard-end due
4257
- * to that fact that a failing hook will
4258
- * surely cause subsequent tests to fail,
4259
- * causing jumbled reporting.
4804
+ * Hook failures work in the following pattern:
4805
+ * - If bail, then exit
4806
+ * - Failed `before` hook skips all tests in a suite and subsuites,
4807
+ * but jumps to corresponding `after` hook
4808
+ * - Failed `before each` hook skips remaining tests in a
4809
+ * suite and jumps to corresponding `after each` hook,
4810
+ * which is run only once
4811
+ * - Failed `after` hook does not alter
4812
+ * execution order
4813
+ * - Failed `after each` hook skips remaining tests in a
4814
+ * suite and subsuites, but executes other `after each`
4815
+ * hooks
4260
4816
  *
4261
4817
  * @param {Hook} hook
4262
4818
  * @param {Error} err
@@ -4265,7 +4821,9 @@ Runner.prototype.fail = function(test, err){
4265
4821
 
4266
4822
  Runner.prototype.failHook = function(hook, err){
4267
4823
  this.fail(hook, err);
4268
- this.emit('end');
4824
+ if (this.suite.bail()) {
4825
+ this.emit('end');
4826
+ }
4269
4827
  };
4270
4828
 
4271
4829
  /**
@@ -4287,6 +4845,8 @@ Runner.prototype.hook = function(name, fn){
4287
4845
  if (!hook) return fn();
4288
4846
  self.currentRunnable = hook;
4289
4847
 
4848
+ hook.ctx.currentTest = self.test;
4849
+
4290
4850
  self.emit('hook', hook);
4291
4851
 
4292
4852
  hook.on('error', function(err){
@@ -4297,20 +4857,30 @@ Runner.prototype.hook = function(name, fn){
4297
4857
  hook.removeAllListeners('error');
4298
4858
  var testError = hook.error();
4299
4859
  if (testError) self.fail(self.test, testError);
4300
- if (err) return self.failHook(hook, err);
4860
+ if (err) {
4861
+ if (err instanceof Pending) {
4862
+ suite.pending = true;
4863
+ } else {
4864
+ self.failHook(hook, err);
4865
+
4866
+ // stop executing hooks, notify callee of hook err
4867
+ return fn(err);
4868
+ }
4869
+ }
4301
4870
  self.emit('hook end', hook);
4871
+ delete hook.ctx.currentTest;
4302
4872
  next(++i);
4303
4873
  });
4304
4874
  }
4305
4875
 
4306
- process.nextTick(function(){
4876
+ Runner.immediately(function(){
4307
4877
  next(0);
4308
4878
  });
4309
4879
  };
4310
4880
 
4311
4881
  /**
4312
4882
  * Run hook `name` for the given array of `suites`
4313
- * in order, and callback `fn(err)`.
4883
+ * in order, and callback `fn(err, errSuite)`.
4314
4884
  *
4315
4885
  * @param {String} name
4316
4886
  * @param {Array} suites
@@ -4332,8 +4902,9 @@ Runner.prototype.hooks = function(name, suites, fn){
4332
4902
 
4333
4903
  self.hook(name, function(err){
4334
4904
  if (err) {
4905
+ var errSuite = self.suite;
4335
4906
  self.suite = orig;
4336
- return fn(err);
4907
+ return fn(err, errSuite);
4337
4908
  }
4338
4909
 
4339
4910
  next(suites.pop());
@@ -4421,10 +4992,39 @@ Runner.prototype.runTests = function(suite, fn){
4421
4992
  , tests = suite.tests.slice()
4422
4993
  , test;
4423
4994
 
4424
- function next(err) {
4995
+
4996
+ function hookErr(err, errSuite, after) {
4997
+ // before/after Each hook for errSuite failed:
4998
+ var orig = self.suite;
4999
+
5000
+ // for failed 'after each' hook start from errSuite parent,
5001
+ // otherwise start from errSuite itself
5002
+ self.suite = after ? errSuite.parent : errSuite;
5003
+
5004
+ if (self.suite) {
5005
+ // call hookUp afterEach
5006
+ self.hookUp('afterEach', function(err2, errSuite2) {
5007
+ self.suite = orig;
5008
+ // some hooks may fail even now
5009
+ if (err2) return hookErr(err2, errSuite2, true);
5010
+ // report error suite
5011
+ fn(errSuite);
5012
+ });
5013
+ } else {
5014
+ // there is no need calling other 'after each' hooks
5015
+ self.suite = orig;
5016
+ fn(errSuite);
5017
+ }
5018
+ }
5019
+
5020
+ function next(err, errSuite) {
4425
5021
  // if we bail after first err
4426
5022
  if (self.failures && suite._bail) return fn();
4427
5023
 
5024
+ if (self._abort) return fn();
5025
+
5026
+ if (err) return hookErr(err, errSuite, true);
5027
+
4428
5028
  // next test
4429
5029
  test = tests.shift();
4430
5030
 
@@ -4445,14 +5045,31 @@ Runner.prototype.runTests = function(suite, fn){
4445
5045
 
4446
5046
  // execute test and hook(s)
4447
5047
  self.emit('test', self.test = test);
4448
- self.hookDown('beforeEach', function(){
5048
+ self.hookDown('beforeEach', function(err, errSuite){
5049
+
5050
+ if (suite.pending) {
5051
+ self.emit('pending', test);
5052
+ self.emit('test end', test);
5053
+ return next();
5054
+ }
5055
+ if (err) return hookErr(err, errSuite, false);
5056
+
4449
5057
  self.currentRunnable = self.test;
4450
5058
  self.runTest(function(err){
4451
5059
  test = self.test;
4452
5060
 
4453
5061
  if (err) {
4454
- self.fail(test, err);
5062
+ if (err instanceof Pending) {
5063
+ self.emit('pending', test);
5064
+ } else {
5065
+ self.fail(test, err);
5066
+ }
4455
5067
  self.emit('test end', test);
5068
+
5069
+ if (err instanceof Pending) {
5070
+ return next();
5071
+ }
5072
+
4456
5073
  return self.hookUp('afterEach', next);
4457
5074
  }
4458
5075
 
@@ -4488,21 +5105,37 @@ Runner.prototype.runSuite = function(suite, fn){
4488
5105
 
4489
5106
  this.emit('suite', this.suite = suite);
4490
5107
 
4491
- function next() {
5108
+ function next(errSuite) {
5109
+ if (errSuite) {
5110
+ // current suite failed on a hook from errSuite
5111
+ if (errSuite == suite) {
5112
+ // if errSuite is current suite
5113
+ // continue to the next sibling suite
5114
+ return done();
5115
+ } else {
5116
+ // errSuite is among the parents of current suite
5117
+ // stop execution of errSuite and all sub-suites
5118
+ return done(errSuite);
5119
+ }
5120
+ }
5121
+
5122
+ if (self._abort) return done();
5123
+
4492
5124
  var curr = suite.suites[i++];
4493
5125
  if (!curr) return done();
4494
5126
  self.runSuite(curr, next);
4495
5127
  }
4496
5128
 
4497
- function done() {
5129
+ function done(errSuite) {
4498
5130
  self.suite = suite;
4499
5131
  self.hook('afterAll', function(){
4500
5132
  self.emit('suite end', suite);
4501
- fn();
5133
+ fn(errSuite);
4502
5134
  });
4503
5135
  }
4504
5136
 
4505
- this.hook('beforeAll', function(){
5137
+ this.hook('beforeAll', function(err){
5138
+ if (err) return done();
4506
5139
  self.runTests(suite, next);
4507
5140
  });
4508
5141
  };
@@ -4515,11 +5148,23 @@ Runner.prototype.runSuite = function(suite, fn){
4515
5148
  */
4516
5149
 
4517
5150
  Runner.prototype.uncaught = function(err){
4518
- debug('uncaught exception %s', err.message);
5151
+ if (err) {
5152
+ debug('uncaught exception %s', err !== function () {
5153
+ return this;
5154
+ }.call(err) ? err : ( err.message || err ));
5155
+ } else {
5156
+ debug('uncaught undefined exception');
5157
+ err = utils.undefinedError();
5158
+ }
5159
+ err.uncaught = true;
5160
+
4519
5161
  var runnable = this.currentRunnable;
4520
- if (!runnable || 'failed' == runnable.state) return;
5162
+ if (!runnable) return;
5163
+
4521
5164
  runnable.clearTimeout();
4522
- err.uncaught = true;
5165
+
5166
+ // Ignore errors if complete
5167
+ if (runnable.state) return;
4523
5168
  this.fail(runnable, err);
4524
5169
 
4525
5170
  // recover from test
@@ -4543,35 +5188,59 @@ Runner.prototype.uncaught = function(err){
4543
5188
  */
4544
5189
 
4545
5190
  Runner.prototype.run = function(fn){
4546
- var self = this
4547
- , fn = fn || function(){};
5191
+ var self = this,
5192
+ rootSuite = this.suite;
5193
+
5194
+ fn = fn || function(){};
5195
+
5196
+ function uncaught(err){
5197
+ self.uncaught(err);
5198
+ }
5199
+
5200
+ function start() {
5201
+ self.emit('start');
5202
+ self.runSuite(rootSuite, function(){
5203
+ debug('finished running');
5204
+ self.emit('end');
5205
+ });
5206
+ }
4548
5207
 
4549
5208
  debug('start');
4550
5209
 
4551
5210
  // callback
4552
5211
  this.on('end', function(){
4553
5212
  debug('end');
4554
- process.removeListener('uncaughtException', function(err){
4555
- self.uncaught(err);
4556
- });
5213
+ process.removeListener('uncaughtException', uncaught);
4557
5214
  fn(self.failures);
4558
5215
  });
4559
5216
 
4560
- // run suites
4561
- this.emit('start');
4562
- this.runSuite(this.suite, function(){
4563
- debug('finished running');
4564
- self.emit('end');
4565
- });
4566
-
4567
5217
  // uncaught exception
4568
- process.on('uncaughtException', function(err){
4569
- self.uncaught(err);
4570
- });
5218
+ process.on('uncaughtException', uncaught);
5219
+
5220
+ if (this._delay) {
5221
+ // for reporters, I guess.
5222
+ // might be nice to debounce some dots while we wait.
5223
+ this.emit('waiting', rootSuite);
5224
+ rootSuite.once('run', start);
5225
+ }
5226
+ else {
5227
+ start();
5228
+ }
4571
5229
 
4572
5230
  return this;
4573
5231
  };
4574
5232
 
5233
+ /**
5234
+ * Cleanly abort execution
5235
+ *
5236
+ * @return {Runner} for chaining
5237
+ * @api public
5238
+ */
5239
+ Runner.prototype.abort = function(){
5240
+ debug('aborting');
5241
+ this._abort = true;
5242
+ };
5243
+
4575
5244
  /**
4576
5245
  * Filter leaks with the given globals flagged as `ok`.
4577
5246
  *
@@ -4583,20 +5252,57 @@ Runner.prototype.run = function(fn){
4583
5252
 
4584
5253
  function filterLeaks(ok, globals) {
4585
5254
  return filter(globals, function(key){
5255
+ // Firefox and Chrome exposes iframes as index inside the window object
5256
+ if (/^d+/.test(key)) return false;
5257
+
5258
+ // in firefox
5259
+ // if runner runs in an iframe, this iframe's window.getInterface method not init at first
5260
+ // it is assigned in some seconds
5261
+ if (global.navigator && /^getInterface/.test(key)) return false;
5262
+
5263
+ // an iframe could be approached by window[iframeIndex]
5264
+ // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
5265
+ if (global.navigator && /^\d+/.test(key)) return false;
5266
+
5267
+ // Opera and IE expose global variables for HTML element IDs (issue #243)
5268
+ if (/^mocha-/.test(key)) return false;
5269
+
4586
5270
  var matched = filter(ok, function(ok){
4587
5271
  if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]);
4588
- // Opera and IE expose global variables for HTML element IDs (issue #243)
4589
- if (/^mocha-/.test(key)) return true;
4590
5272
  return key == ok;
4591
5273
  });
4592
5274
  return matched.length == 0 && (!global.navigator || 'onerror' !== key);
4593
5275
  });
4594
5276
  }
4595
5277
 
5278
+ /**
5279
+ * Array of globals dependent on the environment.
5280
+ *
5281
+ * @return {Array}
5282
+ * @api private
5283
+ */
5284
+
5285
+ function extraGlobals() {
5286
+ if (typeof(process) === 'object' &&
5287
+ typeof(process.version) === 'string') {
5288
+
5289
+ var nodeVersion = process.version.split('.').reduce(function(a, v) {
5290
+ return a << 8 | v;
5291
+ });
5292
+
5293
+ // 'errno' was renamed to process._errno in v0.9.11.
5294
+
5295
+ if (nodeVersion < 0x00090B) {
5296
+ return ['errno'];
5297
+ }
5298
+ }
5299
+
5300
+ return [];
5301
+ }
5302
+
4596
5303
  }); // module: runner.js
4597
5304
 
4598
5305
  require.register("suite.js", function(module, exports, require){
4599
-
4600
5306
  /**
4601
5307
  * Module dependencies.
4602
5308
  */
@@ -4644,9 +5350,11 @@ exports.create = function(parent, title){
4644
5350
  * @api private
4645
5351
  */
4646
5352
 
4647
- function Suite(title, ctx) {
5353
+ function Suite(title, parentContext) {
4648
5354
  this.title = title;
4649
- this.ctx = ctx;
5355
+ var context = function() {};
5356
+ context.prototype = parentContext;
5357
+ this.ctx = new context();
4650
5358
  this.suites = [];
4651
5359
  this.tests = [];
4652
5360
  this.pending = false;
@@ -4656,8 +5364,10 @@ function Suite(title, ctx) {
4656
5364
  this._afterAll = [];
4657
5365
  this.root = !title;
4658
5366
  this._timeout = 2000;
5367
+ this._enableTimeouts = true;
4659
5368
  this._slow = 75;
4660
5369
  this._bail = false;
5370
+ this.delayed = false;
4661
5371
  }
4662
5372
 
4663
5373
  /**
@@ -4682,6 +5392,7 @@ Suite.prototype.clone = function(){
4682
5392
  debug('clone');
4683
5393
  suite.ctx = this.ctx;
4684
5394
  suite.timeout(this.timeout());
5395
+ suite.enableTimeouts(this.enableTimeouts());
4685
5396
  suite.slow(this.slow());
4686
5397
  suite.bail(this.bail());
4687
5398
  return suite;
@@ -4697,12 +5408,28 @@ Suite.prototype.clone = function(){
4697
5408
 
4698
5409
  Suite.prototype.timeout = function(ms){
4699
5410
  if (0 == arguments.length) return this._timeout;
5411
+ if (ms.toString() === '0') this._enableTimeouts = false;
4700
5412
  if ('string' == typeof ms) ms = milliseconds(ms);
4701
5413
  debug('timeout %d', ms);
4702
5414
  this._timeout = parseInt(ms, 10);
4703
5415
  return this;
4704
5416
  };
4705
5417
 
5418
+ /**
5419
+ * Set timeout `enabled`.
5420
+ *
5421
+ * @param {Boolean} enabled
5422
+ * @return {Suite|Boolean} self or enabled
5423
+ * @api private
5424
+ */
5425
+
5426
+ Suite.prototype.enableTimeouts = function(enabled){
5427
+ if (arguments.length === 0) return this._enableTimeouts;
5428
+ debug('enableTimeouts %s', enabled);
5429
+ this._enableTimeouts = enabled;
5430
+ return this;
5431
+ };
5432
+
4706
5433
  /**
4707
5434
  * Set slow `ms` or short-hand such as "2s".
4708
5435
  *
@@ -4722,7 +5449,7 @@ Suite.prototype.slow = function(ms){
4722
5449
  /**
4723
5450
  * Sets whether to bail after first error.
4724
5451
  *
4725
- * @parma {Boolean} bail
5452
+ * @param {Boolean} bail
4726
5453
  * @return {Suite|Number} for chaining
4727
5454
  * @api private
4728
5455
  */
@@ -4742,11 +5469,18 @@ Suite.prototype.bail = function(bail){
4742
5469
  * @api private
4743
5470
  */
4744
5471
 
4745
- Suite.prototype.beforeAll = function(fn){
5472
+ Suite.prototype.beforeAll = function(title, fn){
4746
5473
  if (this.pending) return this;
4747
- var hook = new Hook('"before all" hook', fn);
5474
+ if ('function' === typeof title) {
5475
+ fn = title;
5476
+ title = fn.name;
5477
+ }
5478
+ title = '"before all" hook' + (title ? ': ' + title : '');
5479
+
5480
+ var hook = new Hook(title, fn);
4748
5481
  hook.parent = this;
4749
5482
  hook.timeout(this.timeout());
5483
+ hook.enableTimeouts(this.enableTimeouts());
4750
5484
  hook.slow(this.slow());
4751
5485
  hook.ctx = this.ctx;
4752
5486
  this._beforeAll.push(hook);
@@ -4762,11 +5496,18 @@ Suite.prototype.beforeAll = function(fn){
4762
5496
  * @api private
4763
5497
  */
4764
5498
 
4765
- Suite.prototype.afterAll = function(fn){
5499
+ Suite.prototype.afterAll = function(title, fn){
4766
5500
  if (this.pending) return this;
4767
- var hook = new Hook('"after all" hook', fn);
5501
+ if ('function' === typeof title) {
5502
+ fn = title;
5503
+ title = fn.name;
5504
+ }
5505
+ title = '"after all" hook' + (title ? ': ' + title : '');
5506
+
5507
+ var hook = new Hook(title, fn);
4768
5508
  hook.parent = this;
4769
5509
  hook.timeout(this.timeout());
5510
+ hook.enableTimeouts(this.enableTimeouts());
4770
5511
  hook.slow(this.slow());
4771
5512
  hook.ctx = this.ctx;
4772
5513
  this._afterAll.push(hook);
@@ -4782,11 +5523,18 @@ Suite.prototype.afterAll = function(fn){
4782
5523
  * @api private
4783
5524
  */
4784
5525
 
4785
- Suite.prototype.beforeEach = function(fn){
5526
+ Suite.prototype.beforeEach = function(title, fn){
4786
5527
  if (this.pending) return this;
4787
- var hook = new Hook('"before each" hook', fn);
5528
+ if ('function' === typeof title) {
5529
+ fn = title;
5530
+ title = fn.name;
5531
+ }
5532
+ title = '"before each" hook' + (title ? ': ' + title : '');
5533
+
5534
+ var hook = new Hook(title, fn);
4788
5535
  hook.parent = this;
4789
5536
  hook.timeout(this.timeout());
5537
+ hook.enableTimeouts(this.enableTimeouts());
4790
5538
  hook.slow(this.slow());
4791
5539
  hook.ctx = this.ctx;
4792
5540
  this._beforeEach.push(hook);
@@ -4802,11 +5550,18 @@ Suite.prototype.beforeEach = function(fn){
4802
5550
  * @api private
4803
5551
  */
4804
5552
 
4805
- Suite.prototype.afterEach = function(fn){
5553
+ Suite.prototype.afterEach = function(title, fn){
4806
5554
  if (this.pending) return this;
4807
- var hook = new Hook('"after each" hook', fn);
5555
+ if ('function' === typeof title) {
5556
+ fn = title;
5557
+ title = fn.name;
5558
+ }
5559
+ title = '"after each" hook' + (title ? ': ' + title : '');
5560
+
5561
+ var hook = new Hook(title, fn);
4808
5562
  hook.parent = this;
4809
5563
  hook.timeout(this.timeout());
5564
+ hook.enableTimeouts(this.enableTimeouts());
4810
5565
  hook.slow(this.slow());
4811
5566
  hook.ctx = this.ctx;
4812
5567
  this._afterEach.push(hook);
@@ -4825,6 +5580,7 @@ Suite.prototype.afterEach = function(fn){
4825
5580
  Suite.prototype.addSuite = function(suite){
4826
5581
  suite.parent = this;
4827
5582
  suite.timeout(this.timeout());
5583
+ suite.enableTimeouts(this.enableTimeouts());
4828
5584
  suite.slow(this.slow());
4829
5585
  suite.bail(this.bail());
4830
5586
  this.suites.push(suite);
@@ -4843,6 +5599,7 @@ Suite.prototype.addSuite = function(suite){
4843
5599
  Suite.prototype.addTest = function(test){
4844
5600
  test.parent = this;
4845
5601
  test.timeout(this.timeout());
5602
+ test.enableTimeouts(this.enableTimeouts());
4846
5603
  test.slow(this.slow());
4847
5604
  test.ctx = this.ctx;
4848
5605
  this.tests.push(test);
@@ -4897,10 +5654,18 @@ Suite.prototype.eachTest = function(fn){
4897
5654
  return this;
4898
5655
  };
4899
5656
 
5657
+ /**
5658
+ * This will run the root suite if we happen to be running in delayed mode.
5659
+ */
5660
+ Suite.prototype.run = function run() {
5661
+ if (this.root) {
5662
+ this.emit('run');
5663
+ }
5664
+ };
5665
+
4900
5666
  }); // module: suite.js
4901
5667
 
4902
5668
  require.register("test.js", function(module, exports, require){
4903
-
4904
5669
  /**
4905
5670
  * Module dependencies.
4906
5671
  */
@@ -4940,13 +5705,15 @@ Test.prototype.constructor = Test;
4940
5705
  }); // module: test.js
4941
5706
 
4942
5707
  require.register("utils.js", function(module, exports, require){
4943
-
4944
5708
  /**
4945
5709
  * Module dependencies.
4946
5710
  */
4947
5711
 
4948
5712
  var fs = require('browser/fs')
4949
5713
  , path = require('browser/path')
5714
+ , basename = path.basename
5715
+ , exists = fs.existsSync || path.existsSync
5716
+ , glob = require('browser/glob')
4950
5717
  , join = path.join
4951
5718
  , debug = require('browser/debug')('mocha:watch');
4952
5719
 
@@ -4986,6 +5753,22 @@ exports.forEach = function(arr, fn, scope){
4986
5753
  fn.call(scope, arr[i], i);
4987
5754
  };
4988
5755
 
5756
+ /**
5757
+ * Array#map (<=IE8)
5758
+ *
5759
+ * @param {Array} array
5760
+ * @param {Function} fn
5761
+ * @param {Object} scope
5762
+ * @api private
5763
+ */
5764
+
5765
+ exports.map = function(arr, fn, scope){
5766
+ var result = [];
5767
+ for (var i = 0, l = arr.length; i < l; i++)
5768
+ result.push(fn.call(scope, arr[i], i, arr));
5769
+ return result;
5770
+ };
5771
+
4989
5772
  /**
4990
5773
  * Array#indexOf (<=IE8)
4991
5774
  *
@@ -5051,7 +5834,7 @@ exports.filter = function(arr, fn){
5051
5834
 
5052
5835
  exports.keys = Object.keys || function(obj) {
5053
5836
  var keys = []
5054
- , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
5837
+ , has = Object.prototype.hasOwnProperty; // for `window` on <=IE8
5055
5838
 
5056
5839
  for (var key in obj) {
5057
5840
  if (has.call(obj, key)) {
@@ -5081,6 +5864,28 @@ exports.watch = function(files, fn){
5081
5864
  });
5082
5865
  };
5083
5866
 
5867
+ /**
5868
+ * Array.isArray (<=IE8)
5869
+ *
5870
+ * @param {Object} obj
5871
+ * @return {Boolean}
5872
+ * @api private
5873
+ */
5874
+ var isArray = Array.isArray || function (obj) {
5875
+ return '[object Array]' == {}.toString.call(obj);
5876
+ };
5877
+
5878
+ /**
5879
+ * @description
5880
+ * Buffer.prototype.toJSON polyfill
5881
+ * @type {Function}
5882
+ */
5883
+ if(typeof Buffer !== 'undefined' && Buffer.prototype) {
5884
+ Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () {
5885
+ return Array.prototype.slice.call(this, 0);
5886
+ };
5887
+ }
5888
+
5084
5889
  /**
5085
5890
  * Ignored files.
5086
5891
  */
@@ -5096,19 +5901,22 @@ function ignored(path){
5096
5901
  * @api private
5097
5902
  */
5098
5903
 
5099
- exports.files = function(dir, ret){
5904
+ exports.files = function(dir, ext, ret){
5100
5905
  ret = ret || [];
5906
+ ext = ext || ['js'];
5907
+
5908
+ var re = new RegExp('\\.(' + ext.join('|') + ')$');
5101
5909
 
5102
5910
  fs.readdirSync(dir)
5103
- .filter(ignored)
5104
- .forEach(function(path){
5105
- path = join(dir, path);
5106
- if (fs.statSync(path).isDirectory()) {
5107
- exports.files(path, ret);
5108
- } else if (path.match(/\.(js|coffee)$/)) {
5109
- ret.push(path);
5110
- }
5111
- });
5911
+ .filter(ignored)
5912
+ .forEach(function(path){
5913
+ path = join(dir, path);
5914
+ if (fs.statSync(path).isDirectory()) {
5915
+ exports.files(path, ext, ret);
5916
+ } else if (path.match(re)) {
5917
+ ret.push(path);
5918
+ }
5919
+ });
5112
5920
 
5113
5921
  return ret;
5114
5922
  };
@@ -5135,29 +5943,19 @@ exports.slug = function(str){
5135
5943
 
5136
5944
  exports.clean = function(str) {
5137
5945
  str = str
5138
- .replace(/^function *\(.*\) *{/, '')
5946
+ .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
5947
+ .replace(/^function *\(.*\)\s*{|\(.*\) *=> *{?/, '')
5139
5948
  .replace(/\s+\}$/, '');
5140
5949
 
5141
5950
  var spaces = str.match(/^\n?( *)/)[1].length
5142
- , re = new RegExp('^ {' + spaces + '}', 'gm');
5951
+ , tabs = str.match(/^\n?(\t*)/)[1].length
5952
+ , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm');
5143
5953
 
5144
5954
  str = str.replace(re, '');
5145
5955
 
5146
5956
  return exports.trim(str);
5147
5957
  };
5148
5958
 
5149
- /**
5150
- * Escape regular expression characters in `str`.
5151
- *
5152
- * @param {String} str
5153
- * @return {String}
5154
- * @api private
5155
- */
5156
-
5157
- exports.escapeRegexp = function(str){
5158
- return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
5159
- };
5160
-
5161
5959
  /**
5162
5960
  * Trim the given `str`.
5163
5961
  *
@@ -5205,7 +6003,7 @@ function highlight(js) {
5205
6003
  .replace(/('.*?')/gm, '<span class="string">$1</span>')
5206
6004
  .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
5207
6005
  .replace(/(\d+)/gm, '<span class="number">$1</span>')
5208
- .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
6006
+ .replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
5209
6007
  .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
5210
6008
  }
5211
6009
 
@@ -5217,62 +6015,437 @@ function highlight(js) {
5217
6015
  */
5218
6016
 
5219
6017
  exports.highlightTags = function(name) {
5220
- var code = document.getElementsByTagName(name);
6018
+ var code = document.getElementById('mocha').getElementsByTagName(name);
5221
6019
  for (var i = 0, len = code.length; i < len; ++i) {
5222
6020
  code[i].innerHTML = highlight(code[i].innerHTML);
5223
6021
  }
5224
6022
  };
5225
6023
 
5226
- }); // module: utils.js
5227
6024
  /**
5228
- * Node shims.
6025
+ * If a value could have properties, and has none, this function is called, which returns
6026
+ * a string representation of the empty value.
5229
6027
  *
5230
- * These are meant only to allow
5231
- * mocha.js to run untouched, not
5232
- * to allow running node code in
5233
- * the browser.
6028
+ * Functions w/ no properties return `'[Function]'`
6029
+ * Arrays w/ length === 0 return `'[]'`
6030
+ * Objects w/ no properties return `'{}'`
6031
+ * All else: return result of `value.toString()`
6032
+ *
6033
+ * @param {*} value Value to inspect
6034
+ * @param {string} [type] The type of the value, if known.
6035
+ * @returns {string}
5234
6036
  */
6037
+ var emptyRepresentation = function emptyRepresentation(value, type) {
6038
+ type = type || exports.type(value);
5235
6039
 
5236
- process = {};
5237
- process.exit = function(status){};
5238
- process.stdout = {};
5239
- global = window;
6040
+ switch(type) {
6041
+ case 'function':
6042
+ return '[Function]';
6043
+ case 'object':
6044
+ return '{}';
6045
+ case 'array':
6046
+ return '[]';
6047
+ default:
6048
+ return value.toString();
6049
+ }
6050
+ };
5240
6051
 
5241
6052
  /**
5242
- * next tick implementation.
6053
+ * Takes some variable and asks `{}.toString()` what it thinks it is.
6054
+ * @param {*} value Anything
6055
+ * @example
6056
+ * type({}) // 'object'
6057
+ * type([]) // 'array'
6058
+ * type(1) // 'number'
6059
+ * type(false) // 'boolean'
6060
+ * type(Infinity) // 'number'
6061
+ * type(null) // 'null'
6062
+ * type(new Date()) // 'date'
6063
+ * type(/foo/) // 'regexp'
6064
+ * type('type') // 'string'
6065
+ * type(global) // 'global'
6066
+ * @api private
6067
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
6068
+ * @returns {string}
5243
6069
  */
6070
+ exports.type = function type(value) {
6071
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
6072
+ return 'buffer';
6073
+ }
6074
+ return Object.prototype.toString.call(value)
6075
+ .replace(/^\[.+\s(.+?)\]$/, '$1')
6076
+ .toLowerCase();
6077
+ };
5244
6078
 
5245
- process.nextTick = (function(){
5246
- // postMessage behaves badly on IE8
5247
- if (window.ActiveXObject || !window.postMessage) {
5248
- return function(fn){ fn() };
6079
+ /**
6080
+ * @summary Stringify `value`.
6081
+ * @description Different behavior depending on type of value.
6082
+ * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
6083
+ * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
6084
+ * - If `value` is an *empty* object, function, or array, return result of function
6085
+ * {@link emptyRepresentation}.
6086
+ * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
6087
+ * JSON.stringify().
6088
+ *
6089
+ * @see exports.type
6090
+ * @param {*} value
6091
+ * @return {string}
6092
+ * @api private
6093
+ */
6094
+
6095
+ exports.stringify = function(value) {
6096
+ var type = exports.type(value);
6097
+
6098
+ if (!~exports.indexOf(['object', 'array', 'function'], type)) {
6099
+ if(type != 'buffer') {
6100
+ return jsonStringify(value);
6101
+ }
6102
+ var json = value.toJSON();
6103
+ // Based on the toJSON result
6104
+ return jsonStringify(json.data && json.type ? json.data : json, 2)
6105
+ .replace(/,(\n|$)/g, '$1');
5249
6106
  }
5250
6107
 
5251
- // based on setZeroTimeout by David Baron
5252
- // - http://dbaron.org/log/20100309-faster-timeouts
5253
- var timeouts = []
5254
- , name = 'mocha-zero-timeout'
6108
+ for (var prop in value) {
6109
+ if (Object.prototype.hasOwnProperty.call(value, prop)) {
6110
+ return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1');
6111
+ }
6112
+ }
6113
+
6114
+ return emptyRepresentation(value, type);
6115
+ };
5255
6116
 
5256
- window.addEventListener('message', function(e){
5257
- if (e.source == window && e.data == name) {
5258
- if (e.stopPropagation) e.stopPropagation();
5259
- if (timeouts.length) timeouts.shift()();
6117
+ /**
6118
+ * @description
6119
+ * like JSON.stringify but more sense.
6120
+ * @param {Object} object
6121
+ * @param {Number=} spaces
6122
+ * @param {number=} depth
6123
+ * @returns {*}
6124
+ * @private
6125
+ */
6126
+ function jsonStringify(object, spaces, depth) {
6127
+ if(typeof spaces == 'undefined') return _stringify(object); // primitive types
6128
+
6129
+ depth = depth || 1;
6130
+ var space = spaces * depth
6131
+ , str = isArray(object) ? '[' : '{'
6132
+ , end = isArray(object) ? ']' : '}'
6133
+ , length = object.length || exports.keys(object).length
6134
+ , repeat = function(s, n) { return new Array(n).join(s); }; // `.repeat()` polyfill
6135
+
6136
+ function _stringify(val) {
6137
+ switch (exports.type(val)) {
6138
+ case 'null':
6139
+ case 'undefined':
6140
+ val = '[' + val + ']';
6141
+ break;
6142
+ case 'array':
6143
+ case 'object':
6144
+ val = jsonStringify(val, spaces, depth + 1);
6145
+ break;
6146
+ case 'boolean':
6147
+ case 'regexp':
6148
+ case 'number':
6149
+ val = val === 0 && (1/val) === -Infinity // `-0`
6150
+ ? '-0'
6151
+ : val.toString();
6152
+ break;
6153
+ case 'date':
6154
+ val = '[Date: ' + val.toISOString() + ']';
6155
+ break;
6156
+ case 'buffer':
6157
+ var json = val.toJSON();
6158
+ // Based on the toJSON result
6159
+ json = json.data && json.type ? json.data : json;
6160
+ val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
6161
+ break;
6162
+ default:
6163
+ val = (val == '[Function]' || val == '[Circular]')
6164
+ ? val
6165
+ : '"' + val + '"'; //string
5260
6166
  }
5261
- }, true);
6167
+ return val;
6168
+ }
5262
6169
 
5263
- return function(fn){
5264
- timeouts.push(fn);
5265
- window.postMessage(name, '*');
6170
+ for(var i in object) {
6171
+ if(!object.hasOwnProperty(i)) continue; // not my business
6172
+ --length;
6173
+ str += '\n ' + repeat(' ', space)
6174
+ + (isArray(object) ? '' : '"' + i + '": ') // key
6175
+ + _stringify(object[i]) // value
6176
+ + (length ? ',' : ''); // comma
6177
+ }
6178
+
6179
+ return str + (str.length != 1 // [], {}
6180
+ ? '\n' + repeat(' ', --space) + end
6181
+ : end);
6182
+ }
6183
+
6184
+ /**
6185
+ * Return if obj is a Buffer
6186
+ * @param {Object} arg
6187
+ * @return {Boolean}
6188
+ * @api private
6189
+ */
6190
+ exports.isBuffer = function (arg) {
6191
+ return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
6192
+ };
6193
+
6194
+ /**
6195
+ * @summary Return a new Thing that has the keys in sorted order. Recursive.
6196
+ * @description If the Thing...
6197
+ * - has already been seen, return string `'[Circular]'`
6198
+ * - is `undefined`, return string `'[undefined]'`
6199
+ * - is `null`, return value `null`
6200
+ * - is some other primitive, return the value
6201
+ * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
6202
+ * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
6203
+ * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
6204
+ *
6205
+ * @param {*} value Thing to inspect. May or may not have properties.
6206
+ * @param {Array} [stack=[]] Stack of seen values
6207
+ * @return {(Object|Array|Function|string|undefined)}
6208
+ * @see {@link exports.stringify}
6209
+ * @api private
6210
+ */
6211
+
6212
+ exports.canonicalize = function(value, stack) {
6213
+ var canonicalizedObj,
6214
+ type = exports.type(value),
6215
+ prop,
6216
+ withStack = function withStack(value, fn) {
6217
+ stack.push(value);
6218
+ fn();
6219
+ stack.pop();
6220
+ };
6221
+
6222
+ stack = stack || [];
6223
+
6224
+ if (exports.indexOf(stack, value) !== -1) {
6225
+ return '[Circular]';
6226
+ }
6227
+
6228
+ switch(type) {
6229
+ case 'undefined':
6230
+ case 'buffer':
6231
+ case 'null':
6232
+ canonicalizedObj = value;
6233
+ break;
6234
+ case 'array':
6235
+ withStack(value, function () {
6236
+ canonicalizedObj = exports.map(value, function (item) {
6237
+ return exports.canonicalize(item, stack);
6238
+ });
6239
+ });
6240
+ break;
6241
+ case 'function':
6242
+ for (prop in value) {
6243
+ canonicalizedObj = {};
6244
+ break;
6245
+ }
6246
+ if (!canonicalizedObj) {
6247
+ canonicalizedObj = emptyRepresentation(value, type);
6248
+ break;
6249
+ }
6250
+ /* falls through */
6251
+ case 'object':
6252
+ canonicalizedObj = canonicalizedObj || {};
6253
+ withStack(value, function () {
6254
+ exports.forEach(exports.keys(value).sort(), function (key) {
6255
+ canonicalizedObj[key] = exports.canonicalize(value[key], stack);
6256
+ });
6257
+ });
6258
+ break;
6259
+ case 'date':
6260
+ case 'number':
6261
+ case 'regexp':
6262
+ case 'boolean':
6263
+ canonicalizedObj = value;
6264
+ break;
6265
+ default:
6266
+ canonicalizedObj = value.toString();
6267
+ }
6268
+
6269
+ return canonicalizedObj;
6270
+ };
6271
+
6272
+ /**
6273
+ * Lookup file names at the given `path`.
6274
+ */
6275
+ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
6276
+ var files = [];
6277
+ var re = new RegExp('\\.(' + extensions.join('|') + ')$');
6278
+
6279
+ if (!exists(path)) {
6280
+ if (exists(path + '.js')) {
6281
+ path += '.js';
6282
+ } else {
6283
+ files = glob.sync(path);
6284
+ if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'");
6285
+ return files;
6286
+ }
5266
6287
  }
5267
- })();
6288
+
6289
+ try {
6290
+ var stat = fs.statSync(path);
6291
+ if (stat.isFile()) return path;
6292
+ }
6293
+ catch (ignored) {
6294
+ return;
6295
+ }
6296
+
6297
+ fs.readdirSync(path).forEach(function(file) {
6298
+ file = join(path, file);
6299
+ try {
6300
+ var stat = fs.statSync(file);
6301
+ if (stat.isDirectory()) {
6302
+ if (recursive) {
6303
+ files = files.concat(lookupFiles(file, extensions, recursive));
6304
+ }
6305
+ return;
6306
+ }
6307
+ }
6308
+ catch (ignored) {
6309
+ return;
6310
+ }
6311
+ if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return;
6312
+ files.push(file);
6313
+ });
6314
+
6315
+ return files;
6316
+ };
6317
+
6318
+ /**
6319
+ * Generate an undefined error with a message warning the user.
6320
+ *
6321
+ * @return {Error}
6322
+ */
6323
+
6324
+ exports.undefinedError = function() {
6325
+ return new Error('Caught undefined error, did you throw without specifying what?');
6326
+ };
6327
+
6328
+ /**
6329
+ * Generate an undefined error if `err` is not defined.
6330
+ *
6331
+ * @param {Error} err
6332
+ * @return {Error}
6333
+ */
6334
+
6335
+ exports.getError = function(err) {
6336
+ return err || exports.undefinedError();
6337
+ };
6338
+
6339
+
6340
+ /**
6341
+ * @summary
6342
+ * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
6343
+ * @description
6344
+ * When invoking this function you get a filter function that get the Error.stack as an input,
6345
+ * and return a prettify output.
6346
+ * (i.e: strip Mocha, node_modules, bower and componentJS from stack trace).
6347
+ * @returns {Function}
6348
+ */
6349
+
6350
+ exports.stackTraceFilter = function() {
6351
+ var slash = '/'
6352
+ , is = typeof document === 'undefined'
6353
+ ? { node: true }
6354
+ : { browser: true }
6355
+ , cwd = is.node
6356
+ ? process.cwd() + slash
6357
+ : location.href.replace(/\/[^\/]*$/, '/');
6358
+
6359
+ function isNodeModule (line) {
6360
+ return (~line.indexOf('node_modules'));
6361
+ }
6362
+
6363
+ function isMochaInternal (line) {
6364
+ return (~line.indexOf('node_modules' + slash + 'mocha')) ||
6365
+ (~line.indexOf('components' + slash + 'mochajs')) ||
6366
+ (~line.indexOf('components' + slash + 'mocha'));
6367
+ }
6368
+
6369
+ // node_modules, bower, componentJS
6370
+ function isBrowserModule(line) {
6371
+ return (~line.indexOf('node_modules')) ||
6372
+ (~line.indexOf('components'));
6373
+ }
6374
+
6375
+ function isNodeInternal (line) {
6376
+ return (~line.indexOf('(timers.js:')) ||
6377
+ (~line.indexOf('(events.js:')) ||
6378
+ (~line.indexOf('(node.js:')) ||
6379
+ (~line.indexOf('(module.js:')) ||
6380
+ (~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
6381
+ false
6382
+ }
6383
+
6384
+ return function(stack) {
6385
+ stack = stack.split('\n');
6386
+
6387
+ stack = exports.reduce(stack, function(list, line) {
6388
+ if (is.node && (isNodeModule(line) ||
6389
+ isMochaInternal(line) ||
6390
+ isNodeInternal(line)))
6391
+ return list;
6392
+
6393
+ if (is.browser && (isBrowserModule(line)))
6394
+ return list;
6395
+
6396
+ // Clean up cwd(absolute)
6397
+ list.push(line.replace(cwd, ''));
6398
+ return list;
6399
+ }, []);
6400
+
6401
+ return stack.join('\n');
6402
+ }
6403
+ };
6404
+ }); // module: utils.js
6405
+ // The global object is "self" in Web Workers.
6406
+ var global = (function() { return this; })();
6407
+
6408
+ /**
6409
+ * Save timer references to avoid Sinon interfering (see GH-237).
6410
+ */
6411
+
6412
+ var Date = global.Date;
6413
+ var setTimeout = global.setTimeout;
6414
+ var setInterval = global.setInterval;
6415
+ var clearTimeout = global.clearTimeout;
6416
+ var clearInterval = global.clearInterval;
6417
+
6418
+ /**
6419
+ * Node shims.
6420
+ *
6421
+ * These are meant only to allow
6422
+ * mocha.js to run untouched, not
6423
+ * to allow running node code in
6424
+ * the browser.
6425
+ */
6426
+
6427
+ var process = {};
6428
+ process.exit = function(status){};
6429
+ process.stdout = {};
6430
+
6431
+ var uncaughtExceptionHandlers = [];
6432
+
6433
+ var originalOnerrorHandler = global.onerror;
5268
6434
 
5269
6435
  /**
5270
6436
  * Remove uncaughtException listener.
6437
+ * Revert to original onerror handler if previously defined.
5271
6438
  */
5272
6439
 
5273
- process.removeListener = function(e){
6440
+ process.removeListener = function(e, fn){
5274
6441
  if ('uncaughtException' == e) {
5275
- window.onerror = null;
6442
+ if (originalOnerrorHandler) {
6443
+ global.onerror = originalOnerrorHandler;
6444
+ } else {
6445
+ global.onerror = function() {};
6446
+ }
6447
+ var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
6448
+ if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); }
5276
6449
  }
5277
6450
  };
5278
6451
 
@@ -5282,59 +6455,111 @@ process.removeListener = function(e){
5282
6455
 
5283
6456
  process.on = function(e, fn){
5284
6457
  if ('uncaughtException' == e) {
5285
- window.onerror = function(err, url, line){
6458
+ global.onerror = function(err, url, line){
5286
6459
  fn(new Error(err + ' (' + url + ':' + line + ')'));
6460
+ return true;
5287
6461
  };
6462
+ uncaughtExceptionHandlers.push(fn);
5288
6463
  }
5289
6464
  };
5290
6465
 
5291
- // boot
5292
- ;(function(){
6466
+ /**
6467
+ * Expose mocha.
6468
+ */
5293
6469
 
5294
- /**
5295
- * Expose mocha.
5296
- */
6470
+ var Mocha = global.Mocha = require('mocha'),
6471
+ mocha = global.mocha = new Mocha({ reporter: 'html' });
5297
6472
 
5298
- var Mocha = window.Mocha = require('mocha'),
5299
- mocha = window.mocha = new Mocha({ reporter: 'html' });
6473
+ // The BDD UI is registered by default, but no UI will be functional in the
6474
+ // browser without an explicit call to the overridden `mocha.ui` (see below).
6475
+ // Ensure that this default UI does not expose its methods to the global scope.
6476
+ mocha.suite.removeAllListeners('pre-require');
5300
6477
 
5301
- /**
5302
- * Override ui to ensure that the ui functions are initialized.
5303
- * Normally this would happen in Mocha.prototype.loadFiles.
5304
- */
6478
+ var immediateQueue = []
6479
+ , immediateTimeout;
5305
6480
 
5306
- mocha.ui = function(ui){
5307
- Mocha.prototype.ui.call(this, ui);
5308
- this.suite.emit('pre-require', window, null, this);
5309
- return this;
5310
- };
6481
+ function timeslice() {
6482
+ var immediateStart = new Date().getTime();
6483
+ while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) {
6484
+ immediateQueue.shift()();
6485
+ }
6486
+ if (immediateQueue.length) {
6487
+ immediateTimeout = setTimeout(timeslice, 0);
6488
+ } else {
6489
+ immediateTimeout = null;
6490
+ }
6491
+ }
5311
6492
 
5312
- /**
5313
- * Setup mocha with the given setting options.
5314
- */
6493
+ /**
6494
+ * High-performance override of Runner.immediately.
6495
+ */
5315
6496
 
5316
- mocha.setup = function(opts){
5317
- if ('string' == typeof opts) opts = { ui: opts };
5318
- for (var opt in opts) this[opt](opts[opt]);
5319
- return this;
5320
- };
6497
+ Mocha.Runner.immediately = function(callback) {
6498
+ immediateQueue.push(callback);
6499
+ if (!immediateTimeout) {
6500
+ immediateTimeout = setTimeout(timeslice, 0);
6501
+ }
6502
+ };
6503
+
6504
+ /**
6505
+ * Function to allow assertion libraries to throw errors directly into mocha.
6506
+ * This is useful when running tests in a browser because window.onerror will
6507
+ * only receive the 'message' attribute of the Error.
6508
+ */
6509
+ mocha.throwError = function(err) {
6510
+ Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) {
6511
+ fn(err);
6512
+ });
6513
+ throw err;
6514
+ };
5321
6515
 
5322
- /**
5323
- * Run mocha, returning the Runner.
5324
- */
6516
+ /**
6517
+ * Override ui to ensure that the ui functions are initialized.
6518
+ * Normally this would happen in Mocha.prototype.loadFiles.
6519
+ */
5325
6520
 
5326
- mocha.run = function(fn){
5327
- var options = mocha.options;
5328
- mocha.globals('location');
6521
+ mocha.ui = function(ui){
6522
+ Mocha.prototype.ui.call(this, ui);
6523
+ this.suite.emit('pre-require', global, null, this);
6524
+ return this;
6525
+ };
5329
6526
 
5330
- var query = Mocha.utils.parseQuery(window.location.search || '');
5331
- if (query.grep) mocha.grep(query.grep);
5332
- if (query.invert) mocha.invert();
6527
+ /**
6528
+ * Setup mocha with the given setting options.
6529
+ */
5333
6530
 
5334
- return Mocha.prototype.run.call(mocha, function(){
6531
+ mocha.setup = function(opts){
6532
+ if ('string' == typeof opts) opts = { ui: opts };
6533
+ for (var opt in opts) this[opt](opts[opt]);
6534
+ return this;
6535
+ };
6536
+
6537
+ /**
6538
+ * Run mocha, returning the Runner.
6539
+ */
6540
+
6541
+ mocha.run = function(fn){
6542
+ var options = mocha.options;
6543
+ mocha.globals('location');
6544
+
6545
+ var query = Mocha.utils.parseQuery(global.location.search || '');
6546
+ if (query.grep) mocha.grep(new RegExp(query.grep));
6547
+ if (query.fgrep) mocha.grep(query.fgrep);
6548
+ if (query.invert) mocha.invert();
6549
+
6550
+ return Mocha.prototype.run.call(mocha, function(err){
6551
+ // The DOM Document is not available in Web Workers.
6552
+ var document = global.document;
6553
+ if (document && document.getElementById('mocha') && options.noHighlighting !== true) {
5335
6554
  Mocha.utils.highlightTags('code');
5336
- if (fn) fn();
5337
- });
5338
- };
5339
- })();
6555
+ }
6556
+ if (fn) fn(err);
6557
+ });
6558
+ };
6559
+
6560
+ /**
6561
+ * Expose the process shim.
6562
+ */
6563
+
6564
+ Mocha.process = process;
5340
6565
  })();