mongo_browser 0.2.0.rc2 → 0.2.5

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 (141) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -1
  3. data/.travis.yml +6 -1
  4. data/CHANGELOG.md +15 -0
  5. data/{grunt.js → Gruntfile.js} +10 -8
  6. data/Procfile +1 -0
  7. data/README.md +44 -2
  8. data/Rakefile +1 -12
  9. data/app/assets/images/ajax-loader.gif +0 -0
  10. data/app/assets/javascripts/app/controllers/{breadcrumbs.js.coffee → breadcrumbs_controller.js.coffee} +4 -7
  11. data/app/assets/javascripts/app/controllers/collections/index_controller.js.coffee +38 -0
  12. data/app/assets/javascripts/app/controllers/collections/stats_controller.js.coffee +17 -0
  13. data/app/assets/javascripts/app/controllers/{databases.js.coffee → databases/index_controller.js.coffee} +11 -15
  14. data/app/assets/javascripts/app/controllers/databases/stats_controller.js.coffee +15 -0
  15. data/app/assets/javascripts/app/controllers/documents/index_controller.js.coffee +54 -0
  16. data/app/assets/javascripts/app/controllers/documents/show_controller.js.coffee +38 -0
  17. data/app/assets/javascripts/app/controllers/main_controller.js.coffee +15 -0
  18. data/app/assets/javascripts/app/controllers/servers/show_controller.js.coffee +17 -0
  19. data/app/assets/javascripts/app/directives.js.coffee +23 -0
  20. data/app/assets/javascripts/app/filters.js.coffee +14 -1
  21. data/app/assets/javascripts/app/modules/alerts.js.coffee +58 -0
  22. data/app/assets/javascripts/app/modules/pager.js.coffee +2 -2
  23. data/app/assets/javascripts/app/modules/spinner.js.coffee +29 -0
  24. data/app/assets/javascripts/app/modules/table_filter.js.coffee +4 -4
  25. data/app/assets/javascripts/app/resources.js.coffee +14 -8
  26. data/app/assets/javascripts/app/services.js.coffee +11 -33
  27. data/app/assets/javascripts/application.js.coffee +62 -34
  28. data/app/assets/javascripts/application.test.js.coffee +5 -0
  29. data/app/assets/javascripts/compiled_templates.js.coffee +1 -0
  30. data/app/assets/javascripts/vendor.js.coffee +0 -1
  31. data/app/assets/stylesheets/application.css.scss +36 -18
  32. data/{public/index.html → app/views/index.erb} +8 -8
  33. data/bin/mongo_browser +2 -13
  34. data/config.ru +3 -1
  35. data/lib/mongo_browser.rb +1 -0
  36. data/lib/mongo_browser/api.rb +11 -0
  37. data/lib/mongo_browser/api/collections.rb +34 -0
  38. data/lib/mongo_browser/api/databases.rb +32 -0
  39. data/lib/mongo_browser/api/documents.rb +37 -0
  40. data/lib/mongo_browser/api/mongo.rb +41 -0
  41. data/lib/mongo_browser/application.rb +8 -174
  42. data/lib/mongo_browser/application/development.rb +32 -0
  43. data/lib/mongo_browser/cli.rb +48 -0
  44. data/lib/mongo_browser/entities.rb +43 -0
  45. data/lib/mongo_browser/models/collection.rb +7 -12
  46. data/lib/mongo_browser/models/document.rb +5 -1
  47. data/lib/mongo_browser/models/pager.rb +22 -9
  48. data/lib/mongo_browser/version.rb +1 -1
  49. data/mongo_browser.gemspec +22 -15
  50. data/package.json +30 -0
  51. data/public/ng/templates/alerts.html +6 -0
  52. data/public/ng/templates/collections/index.html +39 -0
  53. data/public/ng/templates/collections/stats.html +18 -0
  54. data/public/ng/templates/databases/index.html +35 -0
  55. data/public/ng/templates/databases/stats.html +18 -0
  56. data/public/ng/templates/documents/index.html +40 -0
  57. data/public/ng/templates/documents/show.html +17 -0
  58. data/{app/assets → public/ng}/templates/pager.html +0 -0
  59. data/{app/assets/templates/server_info.html → public/ng/templates/server/show.html} +1 -1
  60. data/{app/assets → public/ng}/templates/table_filter.html +0 -0
  61. data/script/ci_all +5 -1
  62. data/script/ci_e2e +5 -2
  63. data/script/ci_javascripts +1 -1
  64. data/script/ci_rspec +1 -1
  65. data/spec/javascripts/app/controllers/{breadcrumbs_spec.js.coffee → breadcrumbs_controller_spec.js.coffee} +1 -1
  66. data/spec/javascripts/app/controllers/collections/index_controller_spec.js.coffee +95 -0
  67. data/spec/javascripts/app/controllers/collections/stats_controller_spec.js.coffee +34 -0
  68. data/spec/javascripts/app/controllers/databases/index_controller_spec.js.coffee +93 -0
  69. data/spec/javascripts/app/controllers/databases/stats_controller_spec.js.coffee +30 -0
  70. data/spec/javascripts/app/controllers/documents/index_controller_spec.js.coffee +108 -0
  71. data/spec/javascripts/app/controllers/documents/show_controller_spec.js.coffee +94 -0
  72. data/spec/javascripts/app/controllers/{main_spec.js.coffee → main_controller_spec.js.coffee} +2 -2
  73. data/spec/javascripts/app/controllers/{server_info_spec.js.coffee → server/show_controller_spec.js.coffee} +5 -6
  74. data/spec/javascripts/app/directives_spec.js.coffee +108 -24
  75. data/spec/javascripts/app/filters_spec.js.coffee +31 -5
  76. data/spec/javascripts/app/modules/alerts_spec.js.coffee +138 -0
  77. data/spec/javascripts/app/modules/dialogs_spec.js.coffee +1 -2
  78. data/spec/javascripts/app/modules/pager_spec.js.coffee +0 -1
  79. data/spec/javascripts/app/modules/spinner_spec.js.coffee +65 -0
  80. data/spec/javascripts/app/modules/table_filter_spec.js.coffee +9 -9
  81. data/spec/javascripts/app/resources_spec.js.coffee +99 -0
  82. data/spec/javascripts/app/services_spec.js.coffee +31 -71
  83. data/spec/javascripts/config/{testacular-e2e.conf.js → karma-e2e.conf.js} +1 -1
  84. data/spec/javascripts/config/{testacular.conf.js → karma.conf.js} +2 -3
  85. data/spec/javascripts/e2e/collection_stats_scenario.js.coffee +12 -0
  86. data/spec/javascripts/e2e/collections_scenario.js.coffee +59 -20
  87. data/spec/javascripts/e2e/database_stats_scenario.js.coffee +11 -0
  88. data/spec/javascripts/e2e/databases_scenario.js.coffee +37 -36
  89. data/spec/javascripts/e2e/document_show_scenario.js.coffee +31 -0
  90. data/spec/javascripts/e2e/documents_pagination_scenario.js.coffee +33 -0
  91. data/spec/javascripts/e2e/documents_scenario.js.coffee +43 -4
  92. data/spec/javascripts/e2e/server_info_scenario.js.coffee +8 -2
  93. data/spec/javascripts/helpers/mocks.js.coffee +2 -0
  94. data/spec/javascripts/helpers_e2e/dsl.js.coffee +20 -0
  95. data/spec/javascripts/lib/angular-mocks.js +64 -16
  96. data/spec/javascripts/lib/angular-scenario.js +724 -561
  97. data/spec/javascripts/runner.html +5 -5
  98. data/spec/lib/api/collections_spec.rb +62 -0
  99. data/spec/lib/api/databases_spec.rb +58 -0
  100. data/spec/lib/api/documents_spec.rb +135 -0
  101. data/spec/lib/api/mongo_spec.rb +27 -0
  102. data/spec/lib/cli_spec.rb +19 -0
  103. data/spec/lib/entities_spec.rb +39 -0
  104. data/spec/lib/models/collection_spec.rb +16 -10
  105. data/spec/lib/models/database_spec.rb +4 -4
  106. data/spec/lib/models/document_spec.rb +5 -5
  107. data/spec/lib/models/pager_spec.rb +20 -11
  108. data/spec/spec_helper.rb +7 -15
  109. data/spec/support/api_example_group.rb +45 -0
  110. data/spec/support/fixtures.rb +10 -6
  111. data/spec/support/matchers/expose.rb +18 -0
  112. data/vendor/assets/javascripts/angular/angular-bootstrap.js +1 -1
  113. data/vendor/assets/javascripts/angular/angular-resource.js +78 -56
  114. data/vendor/assets/javascripts/angular/angular-sanitize.js +3 -1
  115. data/vendor/assets/javascripts/angular/angular.js +720 -404
  116. metadata +323 -183
  117. data/app/assets/javascripts/app.js.coffee +0 -8
  118. data/app/assets/javascripts/app/controllers.js.coffee +0 -2
  119. data/app/assets/javascripts/app/controllers/alerts.js.coffee +0 -12
  120. data/app/assets/javascripts/app/controllers/collections.js.coffee +0 -40
  121. data/app/assets/javascripts/app/controllers/documents.js.coffee +0 -49
  122. data/app/assets/javascripts/app/controllers/main.js.coffee +0 -10
  123. data/app/assets/javascripts/app/controllers/server_info.js.coffee +0 -14
  124. data/app/assets/javascripts/templates.js.coffee +0 -1
  125. data/app/assets/javascripts/templates/.gitkeep +0 -0
  126. data/app/assets/templates/collections.html +0 -53
  127. data/app/assets/templates/databases.html +0 -32
  128. data/app/assets/templates/documents.html +0 -45
  129. data/config-e2e.ru +0 -20
  130. data/spec/features/collections_list_spec.rb +0 -64
  131. data/spec/features/documents_list_spec.rb +0 -139
  132. data/spec/features/server_info_spec.rb +0 -23
  133. data/spec/javascripts/app/controllers/alerts_spec.js.coffee +0 -36
  134. data/spec/javascripts/app/controllers/collections_spec.js.coffee +0 -78
  135. data/spec/javascripts/app/controllers/databases_spec.js.coffee +0 -55
  136. data/spec/javascripts/app/controllers/documents_spec.js.coffee +0 -62
  137. data/spec/javascripts/helpers_e2e/app_element.js.coffee +0 -6
  138. data/spec/support/feature_example_group.rb +0 -53
  139. data/spec/support/matchers/have_flash_message.rb +0 -16
  140. data/spec/support/mongod.rb +0 -91
  141. data/spec/support/mongodb.conf +0 -47
@@ -0,0 +1,11 @@
1
+ describe "database stats page", ->
2
+
3
+ beforeEach ->
4
+ browser().navigateTo("/e2e/load_fixtures")
5
+ browser().navigateTo("/")
6
+
7
+ element("table.databases tbody tr a:contains('first_database')").click()
8
+ element("a.btn:contains('Database stats')").click()
9
+
10
+ it "displays database stats", ->
11
+ expect(repeater("table tbody tr").count()).toBeGreaterThan(0)
@@ -1,74 +1,75 @@
1
1
  describe "databases list page", ->
2
+ databasesList = null
3
+
4
+ shouldShowDatabase = (name) ->
5
+ expect(databasesList.column("database.name")).toContain(name)
6
+
7
+ shouldShowAllDatabases = ->
8
+ shouldShowDatabase("first_database")
9
+ shouldShowDatabase("second_database")
10
+ shouldShowDatabase("third_database")
11
+
2
12
  beforeEach ->
3
13
  browser().navigateTo("/e2e/load_fixtures")
4
14
  browser().navigateTo("/")
5
15
 
16
+ databasesList = repeater("table.databases tbody tr")
17
+
6
18
  it "navigates to the valid url", ->
7
19
  expect(browser().location().url()).toBe("/")
8
20
 
21
+ it "shows the breadcrumbs", ->
22
+ link = element(".container a.brand")
23
+ expect(link.text()).toEqual("Mongo Browser")
24
+ expect(link.attr("href")).toEqual("/")
25
+
9
26
  it "displays a valid section title", ->
10
27
  title = element("h2").text()
11
28
  expect(title).toEqual("localhost databases")
12
29
 
13
- it "displays available databases ordered by name", ->
14
- expect(repeater("table.databases tbody tr").column("database.name"))
15
- .toEqual(["first_database", "second_database", "third_database"])
16
-
17
30
  describe "filtering by database name", ->
18
31
  it "displays all databases when the filter is not provided", ->
19
32
  input("value").enter("")
20
- expect(repeater("table.databases tbody tr").count()).toBe(3)
21
- expect(repeater("table.databases tbody tr").column("database.name"))
22
- .toEqual(["first_database", "second_database", "third_database"])
33
+ shouldShowAllDatabases()
23
34
 
24
35
  it "filters by database name", ->
25
36
  input("value").enter("first")
26
- expect(repeater("table.databases tbody tr").count()).toBe(1)
27
- expect(repeater("table.databases tbody tr").column("database.name"))
28
- .toEqual(["first_database"])
37
+ shouldShowDatabase("first_database")
29
38
 
30
39
  input("value").enter("second")
31
- expect(repeater("table.databases tbody tr").count()).toBe(1)
32
- expect(repeater("table.databases tbody tr").column("database.name"))
33
- .toEqual(["second_database"])
40
+ shouldShowDatabase("second_database")
34
41
 
35
- input("value").enter("not existing")
36
- expect(repeater("table.databases tbody tr").count()).toBe(0)
37
- expect(element(".filter.alert:visible").text()).toMatch(/Nothing has been found./)
42
+ input("value").enter("not existing database")
43
+ expect(databasesList.count()).toBe(0)
44
+ expect(element(".alert:visible").text()).toMatch(/Nothing has been found./)
38
45
 
39
46
  it "displays all records when the filter is cleared", ->
40
47
  element("button:contains('Clear')").click()
41
- expect(repeater("table.databases tbody tr").count()).toBe(3)
42
- expect(repeater("table.databases tbody tr").column("database.name"))
43
- .toEqual(["first_database", "second_database", "third_database"])
48
+ shouldShowAllDatabases()
44
49
 
45
50
  describe "delete a database", ->
51
+ deleteDatabase = (name) ->
52
+ element("table.databases tbody tr:contains('#{name}') td:last-child a:contains('Delete')")
53
+ .click()
54
+
46
55
  beforeEach ->
47
- element("table.databases tbody tr:contains('third_database') td.actions a:contains('Delete')").click()
48
- expect(element("div.modal .modal-body").text()).toContain("Deleting third_database. Are you sure?")
56
+ deleteDatabase("third_database")
57
+ expect(element("div.modal .modal-body").text())
58
+ .toContain("Deleting third_database. Are you sure?")
49
59
 
50
60
  describe "when the dialog was disposed", ->
51
- beforeEach ->
52
- appElement "div.modal .modal-footer a:contains('Cancel')", ($element) ->
53
- $element.click()
61
+ beforeEach -> disposeDialog()
54
62
 
55
63
  it "does nothig", ->
56
- expect(repeater("table.databases tbody tr").count()).toBe(3)
57
- expect(repeater("table.databases tbody tr").column("database.name"))
58
- .toEqual(["first_database", "second_database", "third_database"])
64
+ shouldShowAllDatabases()
59
65
 
60
66
  describe "when the dialog was confirmed", ->
61
- beforeEach ->
62
- appElement "div.bootbox a:contains('OK')", ($element) ->
63
- $element.click()
67
+ beforeEach -> confirmDialog()
64
68
 
65
- # TODO figure out how to test it
66
- xit "shows the alert", ->
69
+ it "shows the alert", ->
67
70
  expect(repeater("aside#alerts .alert").count()).toBe(1)
68
71
  expect(repeater("aside#alerts .alert").column("message.text"))
69
- .toContain("Database third_database has been deleted.")
72
+ .toContain("Database third_database has been deleted.")
70
73
 
71
74
  it "deletes a database", ->
72
- expect(repeater("table.databases tbody tr").count()).toBe(2)
73
- expect(repeater("table.databases tbody tr").column("database.name"))
74
- .toEqual(["first_database", "second_database"])
75
+ expect(databasesList.column("database.name")).not().toContain("third_database")
@@ -0,0 +1,31 @@
1
+ describe "documents list page", ->
2
+ beforeEach ->
3
+ browser().navigateTo("/e2e/load_fixtures")
4
+ browser().navigateTo("/")
5
+
6
+ element("table.databases tbody tr a:contains('first_database')").click()
7
+ element("table.collections tbody tr a:contains('first_collection')").click()
8
+ element("table.documents tbody tr:first td span.id a").click()
9
+
10
+ it "shows the document page", ->
11
+ expect(browser().location().url()).toMatch(/\/databases\/first_database\/collections\/first_collection\/documents\/.+/)
12
+
13
+ it "shows the alerts when the document has been refreshed", ->
14
+ element("a:contains('Refresh')").click()
15
+
16
+ expect(repeater("aside#alerts .alert").count()).toBe(1)
17
+ expect(repeater("aside#alerts .alert").column("message.text"))
18
+ .toContain("Document was refreshed")
19
+
20
+ describe "when the document has not been found", ->
21
+ beforeEach ->
22
+ notExistingDocumentId = "50f486f1dac5d50540000003"
23
+ browser().navigateTo("/databases/first_database/collections/first_collection/documents/#{notExistingDocumentId}")
24
+
25
+ it "redirects to the documents list page", ->
26
+ expect(browser().location().url()).toEqual("/databases/first_database/collections/first_collection/documents")
27
+
28
+ it "sets the flash error message", ->
29
+ expect(repeater("aside#alerts .alert").count()).toBe(1)
30
+ expect(repeater("aside#alerts .alert").column("message.text"))
31
+ .toEqual(["Document not found"])
@@ -0,0 +1,33 @@
1
+ describe "documents list page", ->
2
+ documentsList = null
3
+
4
+ beforeEach ->
5
+ browser().navigateTo("/e2e/load_fixtures")
6
+ browser().navigateTo("/")
7
+
8
+ element("table.databases tbody tr a:contains('first_database')").click()
9
+ element("table.collections tbody tr a:contains('second_collection')").click()
10
+
11
+ documentsList = repeater("table.documents tbody tr")
12
+
13
+ describe "pagination", ->
14
+ it "displays the pager", ->
15
+ expect(documentsList.count()).toEqual(25)
16
+ expect(repeater("div.pagination:first li").count()).toEqual(5)
17
+
18
+ # TODO extend this spec
19
+ it "paginate documents", ->
20
+ element("div.pagination:first li a:contains('Next')").click()
21
+ expect(documentsList.count()).toEqual(25)
22
+ expect(browser().location().url())
23
+ .toBe("/databases/first_database/collections/second_collection/documents?page=2")
24
+
25
+ element("div.pagination:first li a:contains('Next')").click()
26
+ expect(documentsList.count()).toEqual(20)
27
+ expect(browser().location().url())
28
+ .toBe("/databases/first_database/collections/second_collection/documents?page=3")
29
+
30
+ element("div.pagination:first li a:contains('1')").click()
31
+ expect(documentsList.count()).toEqual(25)
32
+ expect(browser().location().url())
33
+ .toBe("/databases/first_database/collections/second_collection/documents")
@@ -1,4 +1,6 @@
1
1
  describe "documents list page", ->
2
+ documentsList = null
3
+
2
4
  beforeEach ->
3
5
  browser().navigateTo("/e2e/load_fixtures")
4
6
  browser().navigateTo("/")
@@ -6,13 +8,50 @@ describe "documents list page", ->
6
8
  element("table.databases tbody tr a:contains('first_database')").click()
7
9
  element("table.collections tbody tr a:contains('first_collection')").click()
8
10
 
11
+ documentsList = repeater("table.documents tbody tr")
12
+
9
13
  it "shows the documents page", ->
10
14
  expect(browser().location().url())
11
- .toBe("/databases/first_database/collections/first_collection/documents")
15
+ .toBe("/databases/first_database/collections/first_collection/documents")
12
16
 
13
17
  title = element("h2").text()
14
18
  expect(title).toEqual("first_collection documents")
15
19
 
16
- it "displays a tab with collection stats", ->
17
- element(".tabbable a:contains('Collection stats')").click()
18
- expect(repeater("table tbody tr").count()).toBeGreaterThan(0)
20
+ it "shows the breadcrumbs", ->
21
+ link = element(".container a.brand")
22
+ expect(link.text()).toEqual("Mongo Browser")
23
+ expect(link.attr("href")).toEqual("/")
24
+
25
+ dbLink = element(".container .breadcrumbs li:nth-child(1) a")
26
+ expect(dbLink.text()).toEqual("first_database")
27
+ expect(dbLink.attr("href")).toEqual("/databases/first_database/collections")
28
+
29
+ collectionLink = element(".container .breadcrumbs li:nth-child(2) a")
30
+ expect(collectionLink.text()).toEqual("first_collection")
31
+ expect(collectionLink.attr("href"))
32
+ .toEqual("/databases/first_database/collections/first_collection/documents")
33
+
34
+ describe "delete a document", ->
35
+ beforeEach ->
36
+ element("table.documents tbody tr:first td:last-child a:contains('Delete')")
37
+ .click()
38
+
39
+ expect(element("div.modal .modal-body").text())
40
+ .toContain("Are you sure?")
41
+
42
+ describe "when the dialog was disposed", ->
43
+ beforeEach -> disposeDialog()
44
+
45
+ it "does nothig", ->
46
+ expect(documentsList.count()).toBe(2)
47
+
48
+ describe "when the dialog was confirmed", ->
49
+ beforeEach -> confirmDialog()
50
+
51
+ it "shows the alert", ->
52
+ expect(repeater("aside#alerts .alert").count()).toBe(1)
53
+ expect(repeater("aside#alerts .alert").column("message.text"))
54
+ .toMatch(/Document \w+ has been deleted./)
55
+
56
+ it "deletes a database", ->
57
+ expect(documentsList.count()).toBe(1)
@@ -3,10 +3,16 @@ describe "server info page", ->
3
3
  browser().navigateTo("/")
4
4
  element("a:contains('Server Info')").click()
5
5
 
6
- it "displays details about the server", ->
6
+ it "navigates to the valid url", ->
7
7
  expect(browser().location().url()).toBe("/server_info")
8
8
 
9
+ it "displays a valid section title", ->
9
10
  title = element("h2").text()
10
11
  expect(title).toEqual("server info")
11
12
 
12
- expect(repeater("table tbody tr").count()).toBeGreaterThan(0)
13
+ it "displays details about the server", ->
14
+ list = repeater("table tbody tr")
15
+ expect(list.count()).toBeGreaterThan(0)
16
+
17
+ for property in ["version", "gitVersion", "sysInfo", "versionArray", "bits", "debug", "maxBsonObjectSize", "ok"]
18
+ expect(list.column("property")).toContain(property)
@@ -1,5 +1,7 @@
1
1
  # Create a mock for dialogsHandler
2
2
  angular.module("mocks", []).config ($provide) ->
3
+ $provide.value("alertTimeout", 3000)
4
+
3
5
  $provide.factory "dialogsHandler", ->
4
6
  confirm: (message, callback) ->
5
7
  @callback = callback
@@ -0,0 +1,20 @@
1
+ dsl = angular.scenario.dsl
2
+
3
+ # Access to iframe's jQuery, see: https://gist.github.com/1700488
4
+ dsl "appElement", ->
5
+ (selector, fn) ->
6
+ @addFutureAction "element " + selector, ($window, $document, done) ->
7
+ fn.call this, $window.angular.element(selector)
8
+ done()
9
+
10
+ dsl "confirmDialog", ->
11
+ ->
12
+ @addFutureAction "confirm dialog", ($window, $document, done) ->
13
+ $window.angular.element("div.bootbox a:contains('OK')").click()
14
+ done()
15
+
16
+ dsl "disposeDialog", ->
17
+ ->
18
+ @addFutureAction "confirm dialog", ($window, $document, done) ->
19
+ $window.angular.element("div.bootbox a:contains('Cancel')").click()
20
+ done()
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.3
2
+ * @license AngularJS v1.0.7
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  *
@@ -202,6 +202,30 @@ angular.mock.$Browser.prototype = {
202
202
  * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
203
203
  * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
204
204
  * information.
205
+ *
206
+ *
207
+ * <pre>
208
+ * describe('$exceptionHandlerProvider', function() {
209
+ *
210
+ * it('should capture log messages and exceptions', function() {
211
+ *
212
+ * module(function($exceptionHandlerProvider) {
213
+ * $exceptionHandlerProvider.mode('log');
214
+ * });
215
+ *
216
+ * inject(function($log, $exceptionHandler, $timeout) {
217
+ * $timeout(function() { $log.log(1); });
218
+ * $timeout(function() { $log.log(2); throw 'banana peel'; });
219
+ * $timeout(function() { $log.log(3); });
220
+ * expect($exceptionHandler.errors).toEqual([]);
221
+ * expect($log.assertEmpty());
222
+ * $timeout.flush();
223
+ * expect($exceptionHandler.errors).toEqual(['banana peel']);
224
+ * expect($log.log.logs).toEqual([[1], [2], [3]]);
225
+ * });
226
+ * });
227
+ * });
228
+ * </pre>
205
229
  */
206
230
 
207
231
  angular.mock.$ExceptionHandlerProvider = function() {
@@ -217,11 +241,11 @@ angular.mock.$ExceptionHandlerProvider = function() {
217
241
  *
218
242
  * @param {string} mode Mode of operation, defaults to `rethrow`.
219
243
  *
220
- * - `rethrow`: If any errors are are passed into the handler in tests, it typically
244
+ * - `rethrow`: If any errors are passed into the handler in tests, it typically
221
245
  * means that there is a bug in the application or test, so this mock will
222
246
  * make these tests fail.
223
- * - `log`: Sometimes it is desirable to test that an error is throw, for this case the `log` mode stores the
224
- * error and allows later assertion of it.
247
+ * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an
248
+ * array of errors in `$exceptionHandler.errors`, to allow later assertion of them.
225
249
  * See {@link ngMock.$log#assertEmpty assertEmpty()} and
226
250
  * {@link ngMock.$log#reset reset()}
227
251
  */
@@ -298,7 +322,13 @@ angular.mock.$LogProvider = function() {
298
322
  * @propertyOf ngMock.$log
299
323
  *
300
324
  * @description
301
- * Array of logged messages.
325
+ * Array of messages logged using {@link ngMock.$log#log}.
326
+ *
327
+ * @example
328
+ * <pre>
329
+ * $log.log('Some Log');
330
+ * var first = $log.log.logs.unshift();
331
+ * </pre>
302
332
  */
303
333
  $log.log.logs = [];
304
334
  /**
@@ -307,7 +337,13 @@ angular.mock.$LogProvider = function() {
307
337
  * @propertyOf ngMock.$log
308
338
  *
309
339
  * @description
310
- * Array of logged messages.
340
+ * Array of messages logged using {@link ngMock.$log#warn}.
341
+ *
342
+ * @example
343
+ * <pre>
344
+ * $log.warn('Some Warning');
345
+ * var first = $log.warn.logs.unshift();
346
+ * </pre>
311
347
  */
312
348
  $log.warn.logs = [];
313
349
  /**
@@ -316,7 +352,13 @@ angular.mock.$LogProvider = function() {
316
352
  * @propertyOf ngMock.$log
317
353
  *
318
354
  * @description
319
- * Array of logged messages.
355
+ * Array of messages logged using {@link ngMock.$log#info}.
356
+ *
357
+ * @example
358
+ * <pre>
359
+ * $log.info('Some Info');
360
+ * var first = $log.info.logs.unshift();
361
+ * </pre>
320
362
  */
321
363
  $log.info.logs = [];
322
364
  /**
@@ -325,7 +367,13 @@ angular.mock.$LogProvider = function() {
325
367
  * @propertyOf ngMock.$log
326
368
  *
327
369
  * @description
328
- * Array of logged messages.
370
+ * Array of messages logged using {@link ngMock.$log#error}.
371
+ *
372
+ * @example
373
+ * <pre>
374
+ * $log.log('Some Error');
375
+ * var first = $log.error.logs.unshift();
376
+ * </pre>
329
377
  */
330
378
  $log.error.logs = [];
331
379
  };
@@ -406,7 +454,7 @@ angular.mock.$LogProvider = function() {
406
454
  *
407
455
  * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
408
456
  *
409
- * Mock of the Date type which has its timezone specified via constroctor arg.
457
+ * Mock of the Date type which has its timezone specified via constructor arg.
410
458
  *
411
459
  * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
412
460
  * offset, so that we can test code that depends on local timezone settings without dependency on
@@ -561,7 +609,7 @@ angular.mock.$LogProvider = function() {
561
609
 
562
610
  /**
563
611
  * @ngdoc function
564
- * @name angular.mock.debug
612
+ * @name angular.mock.dump
565
613
  * @description
566
614
  *
567
615
  * *NOTE*: this is not an injectable instance, just a globally available function.
@@ -629,10 +677,10 @@ angular.mock.dump = function(object) {
629
677
  * @ngdoc object
630
678
  * @name ngMock.$httpBackend
631
679
  * @description
632
- * Fake HTTP backend implementation suitable for unit testing application that use the
680
+ * Fake HTTP backend implementation suitable for unit testing applications that use the
633
681
  * {@link ng.$http $http service}.
634
682
  *
635
- * *Note*: For fake http backend implementation suitable for end-to-end testing or backend-less
683
+ * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
636
684
  * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
637
685
  *
638
686
  * During unit testing, we want our unit tests to run quickly and have no external dependencies so
@@ -744,7 +792,7 @@ angular.mock.dump = function(object) {
744
792
  }
745
793
 
746
794
  // testing controller
747
- var $http;
795
+ var $httpBackend;
748
796
 
749
797
  beforeEach(inject(function($injector) {
750
798
  $httpBackend = $injector.get('$httpBackend');
@@ -1629,7 +1677,7 @@ window.jasmine && (function(window) {
1629
1677
  * @name angular.mock.module
1630
1678
  * @description
1631
1679
  *
1632
- * *NOTE*: This is function is also published on window for easy access.<br>
1680
+ * *NOTE*: This function is also published on window for easy access.<br>
1633
1681
  * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1634
1682
  *
1635
1683
  * This function registers a module configuration code. It collects the configuration information
@@ -1663,7 +1711,7 @@ window.jasmine && (function(window) {
1663
1711
  * @name angular.mock.inject
1664
1712
  * @description
1665
1713
  *
1666
- * *NOTE*: This is function is also published on window for easy access.<br>
1714
+ * *NOTE*: This function is also published on window for easy access.<br>
1667
1715
  * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1668
1716
  *
1669
1717
  * The inject function wraps a function into an injectable function. The inject() creates new
@@ -1729,7 +1777,7 @@ window.jasmine && (function(window) {
1729
1777
  try {
1730
1778
  injector.invoke(blockFns[i] || angular.noop, this);
1731
1779
  } catch (e) {
1732
- if(e.stack) e.stack += '\n' + errorForStack.stack;
1780
+ if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack;
1733
1781
  throw e;
1734
1782
  } finally {
1735
1783
  errorForStack = null;