mongo_browser 0.1.3 → 0.2.0.rc2
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.
- data/.gitignore +3 -0
- data/.travis.yml +3 -3
- data/README.md +1 -25
- data/Rakefile +0 -1
- data/app/assets/images/background.png +0 -0
- data/app/assets/javascripts/app/controllers/alerts.js.coffee +12 -0
- data/app/assets/javascripts/app/controllers/breadcrumbs.js.coffee +25 -0
- data/app/assets/javascripts/app/controllers/collections.js.coffee +40 -0
- data/app/assets/javascripts/app/controllers/databases.js.coffee +39 -0
- data/app/assets/javascripts/app/controllers/documents.js.coffee +49 -0
- data/app/assets/javascripts/app/controllers/main.js.coffee +10 -0
- data/app/assets/javascripts/app/controllers/server_info.js.coffee +14 -0
- data/app/assets/javascripts/app/controllers.js.coffee +2 -0
- data/app/assets/javascripts/app/directives.js.coffee +20 -0
- data/app/assets/javascripts/app/filters.js.coffee +48 -0
- data/app/assets/javascripts/app/modules/dialogs.js.coffee +29 -0
- data/app/assets/javascripts/app/modules/pager.js.coffee +87 -0
- data/app/assets/javascripts/app/modules/table_filter.js.coffee +27 -0
- data/app/assets/javascripts/app/resources.js.coffee +22 -0
- data/app/assets/javascripts/app/services.js.coffee +36 -0
- data/app/assets/javascripts/app.js.coffee +8 -0
- data/app/assets/javascripts/application.js.coffee +35 -6
- data/app/assets/javascripts/templates/.gitkeep +0 -0
- data/app/assets/javascripts/templates.js.coffee +1 -0
- data/app/assets/javascripts/vendor.js.coffee +5 -0
- data/app/assets/stylesheets/application.css.scss +46 -3
- data/app/assets/templates/collections.html +53 -0
- data/app/assets/templates/databases.html +32 -0
- data/app/assets/templates/documents.html +45 -0
- data/app/assets/templates/pager.html +13 -0
- data/app/{views/server_info.erb → assets/templates/server_info.html} +5 -7
- data/app/assets/templates/table_filter.html +10 -0
- data/bin/mongo_browser +8 -45
- data/config-e2e.ru +20 -0
- data/grunt.js +36 -0
- data/lib/mongo_browser/application.rb +143 -64
- data/lib/mongo_browser/middleware/sprockets_base.rb +11 -0
- data/lib/mongo_browser/middleware/sprockets_sinatra.rb +3 -7
- data/lib/mongo_browser/middleware/sprockets_specs.rb +18 -0
- data/lib/mongo_browser/models/collection.rb +67 -0
- data/lib/mongo_browser/models/database.rb +52 -0
- data/lib/mongo_browser/models/document.rb +17 -0
- data/lib/mongo_browser/models/pager.rb +30 -0
- data/lib/mongo_browser/models/server.rb +51 -0
- data/lib/mongo_browser/version.rb +1 -1
- data/lib/mongo_browser.rb +10 -5
- data/mongo_browser.gemspec +4 -11
- data/public/index.html +47 -0
- data/script/ci_all +21 -0
- data/script/ci_e2e +11 -0
- data/script/ci_javascripts +4 -0
- data/script/ci_rspec +3 -0
- data/spec/features/collections_list_spec.rb +6 -49
- data/spec/features/documents_list_spec.rb +15 -14
- data/spec/features/server_info_spec.rb +3 -3
- data/spec/javascripts/app/controllers/alerts_spec.js.coffee +36 -0
- data/spec/javascripts/app/controllers/breadcrumbs_spec.js.coffee +28 -0
- data/spec/javascripts/app/controllers/collections_spec.js.coffee +78 -0
- data/spec/javascripts/app/controllers/databases_spec.js.coffee +55 -0
- data/spec/javascripts/app/controllers/documents_spec.js.coffee +62 -0
- data/spec/javascripts/app/controllers/main_spec.js.coffee +20 -0
- data/spec/javascripts/app/controllers/server_info_spec.js.coffee +21 -0
- data/spec/javascripts/app/directives_spec.js.coffee +58 -0
- data/spec/javascripts/app/filters_spec.js.coffee +99 -0
- data/spec/javascripts/app/modules/dialogs_spec.js.coffee +51 -0
- data/spec/javascripts/app/modules/pager_spec.js.coffee +104 -0
- data/spec/javascripts/app/modules/table_filter_spec.js.coffee +76 -0
- data/spec/javascripts/app/services_spec.js.coffee +83 -0
- data/spec/javascripts/config/testacular-e2e.conf.js +19 -0
- data/spec/javascripts/config/testacular.conf.js +43 -0
- data/spec/javascripts/e2e/collections_scenario.js.coffee +49 -0
- data/spec/javascripts/e2e/databases_scenario.js.coffee +74 -0
- data/spec/javascripts/e2e/documents_scenario.js.coffee +18 -0
- data/spec/javascripts/e2e/server_info_scenario.js.coffee +12 -0
- data/spec/javascripts/helpers/matchers.js.coffee +5 -0
- data/spec/javascripts/helpers/mocks.js.coffee +7 -0
- data/spec/javascripts/helpers_e2e/app_element.js.coffee +6 -0
- data/spec/javascripts/lib/angular-mocks.js +1740 -0
- data/spec/javascripts/lib/angular-scenario.js +26147 -0
- data/spec/javascripts/lib/jasmine-html.js +681 -0
- data/spec/javascripts/lib/jasmine.css +82 -0
- data/spec/javascripts/lib/jasmine.js +2600 -0
- data/spec/javascripts/runner.html +54 -0
- data/spec/javascripts/runner_e2e.html +10 -0
- data/spec/javascripts/spec.js.coffee +2 -0
- data/spec/javascripts/spec_e2e.js.coffee +2 -0
- data/spec/lib/models/collection_spec.rb +80 -0
- data/spec/lib/models/database_spec.rb +75 -0
- data/spec/lib/models/document_spec.rb +17 -0
- data/spec/lib/models/pager_spec.rb +64 -0
- data/spec/lib/models/server_spec.rb +76 -0
- data/spec/lib/mongo_browser_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -19
- data/spec/support/feature_example_group.rb +8 -3
- data/spec/support/fixtures/databases.json +8 -0
- data/spec/support/fixtures.rb +20 -3
- data/spec/support/matchers/have_flash_message.rb +1 -1
- data/spec/support/mongod.rb +37 -21
- data/spec/support/mongodb.conf +47 -0
- data/vendor/assets/javascripts/angular/angular-bootstrap.js +166 -0
- data/vendor/assets/javascripts/angular/angular-resource.js +435 -0
- data/vendor/assets/javascripts/angular/angular-sanitize.js +535 -0
- data/vendor/assets/javascripts/angular/angular.js +14531 -0
- data/vendor/assets/javascripts/underscore.js +1200 -0
- metadata +136 -148
- data/app/assets/javascripts/app/table_filter.js.coffee +0 -50
- data/app/assets/javascripts/ujs.js.coffee +0 -23
- data/app/views/collections/index.erb +0 -59
- data/app/views/databases/index.erb +0 -29
- data/app/views/documents/index.erb +0 -61
- data/app/views/layout/_flash_messages.erb +0 -10
- data/app/views/layout/_navbar.erb +0 -22
- data/app/views/layout.erb +0 -20
- data/app/views/shared/_filter.erb +0 -14
- data/features/mongo_browser.feature +0 -18
- data/features/step_definitions/mongo_browser_steps.rb +0 -1
- data/features/support/env.rb +0 -18
- data/spec/features/databases_list_spec.rb +0 -65
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
describe "services", ->
|
|
2
|
+
beforeEach module("mb.services")
|
|
3
|
+
|
|
4
|
+
describe "alerts", ->
|
|
5
|
+
it "is defined", inject (alerts) ->
|
|
6
|
+
expect(alerts).toBeDefined()
|
|
7
|
+
|
|
8
|
+
describe "#nextId", ->
|
|
9
|
+
it "return the next id for the new flash message", inject (alerts) ->
|
|
10
|
+
expect(alerts.nextId()).toEqual(1)
|
|
11
|
+
_(4).times -> alerts.nextId()
|
|
12
|
+
expect(alerts.nextId()).toEqual(6)
|
|
13
|
+
|
|
14
|
+
describe "#push", ->
|
|
15
|
+
beforeEach inject (alerts) ->
|
|
16
|
+
spyOn(alerts, "delayedDispose")
|
|
17
|
+
|
|
18
|
+
it "returns an id for the new flash message", inject (alerts) ->
|
|
19
|
+
expect(alerts.push("info", "Test..")).toEqual(1)
|
|
20
|
+
expect(alerts.delayedDispose).toHaveBeenCalledWith(1)
|
|
21
|
+
|
|
22
|
+
expect(alerts.push("error", "Test error..")).toEqual(2)
|
|
23
|
+
expect(alerts.delayedDispose).toHaveBeenCalledWith(2)
|
|
24
|
+
|
|
25
|
+
describe "#info", ->
|
|
26
|
+
it "pushesh the given message", inject (alerts) ->
|
|
27
|
+
# Given
|
|
28
|
+
testMessage = "This is a test message!"
|
|
29
|
+
otherTestMessage = "This is a second test message!"
|
|
30
|
+
|
|
31
|
+
# When
|
|
32
|
+
alerts.info(testMessage)
|
|
33
|
+
expect(alerts.delayedDispose).toHaveBeenCalledWith(1)
|
|
34
|
+
|
|
35
|
+
alerts.info(otherTestMessage)
|
|
36
|
+
expect(alerts.delayedDispose).toHaveBeenCalledWith(2)
|
|
37
|
+
|
|
38
|
+
# Then
|
|
39
|
+
expect(alerts.messages).toContain(id: 1, type: "info", text: testMessage)
|
|
40
|
+
expect(alerts.messages).toContain(id: 2, type: "info", text: otherTestMessage)
|
|
41
|
+
|
|
42
|
+
describe "#error", ->
|
|
43
|
+
it "pushesh the given message", inject (alerts) ->
|
|
44
|
+
# Given
|
|
45
|
+
testMessage = "This is a test message!"
|
|
46
|
+
|
|
47
|
+
# When
|
|
48
|
+
alerts.error(testMessage)
|
|
49
|
+
expect(alerts.delayedDispose).toHaveBeenCalledWith(1)
|
|
50
|
+
|
|
51
|
+
# Then
|
|
52
|
+
expect(alerts.messages).toContain(id: 1, type: "error", text: testMessage)
|
|
53
|
+
|
|
54
|
+
describe "#dispose", ->
|
|
55
|
+
it "removes a message with the given id", inject (alerts) ->
|
|
56
|
+
# Given
|
|
57
|
+
alerts.info("First message")
|
|
58
|
+
alerts.info("Second message")
|
|
59
|
+
alerts.info("Third message")
|
|
60
|
+
alerts.error("Error message")
|
|
61
|
+
|
|
62
|
+
# When
|
|
63
|
+
alerts.dispose(2)
|
|
64
|
+
|
|
65
|
+
# Then
|
|
66
|
+
expect(alerts.messages).toContain(id: 1, type: "info", text: "First message")
|
|
67
|
+
expect(alerts.messages).not.toContain(id: 2, type: "info", text: "Second message")
|
|
68
|
+
expect(alerts.messages).toContain(id: 3, type: "info", text: "Third message")
|
|
69
|
+
expect(alerts.messages).toContain(id: 4, type: "error", text: "Error message")
|
|
70
|
+
|
|
71
|
+
describe "#delayedDispose", ->
|
|
72
|
+
it "removes a message after the given time", inject (alerts, $timeout) ->
|
|
73
|
+
# Given
|
|
74
|
+
alerts.info("First message")
|
|
75
|
+
|
|
76
|
+
# When
|
|
77
|
+
alerts.delayedDispose(1)
|
|
78
|
+
expect(alerts.messages).toContain(id: 1, type: "info", text: "First message")
|
|
79
|
+
|
|
80
|
+
$timeout.flush()
|
|
81
|
+
|
|
82
|
+
# Then
|
|
83
|
+
expect(alerts.messages).toEqual([])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
basePath = "../../../";
|
|
2
|
+
|
|
3
|
+
files = [
|
|
4
|
+
ANGULAR_SCENARIO,
|
|
5
|
+
ANGULAR_SCENARIO_ADAPTER,
|
|
6
|
+
"spec/javascripts/helpers_e2e/**/*.js.coffee",
|
|
7
|
+
"spec/javascripts/e2e/**/*.js.coffee"
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
reporters = ["dots"];
|
|
11
|
+
browsers = ["PhantomJS"];
|
|
12
|
+
|
|
13
|
+
autoWatch = false;
|
|
14
|
+
singleRun = true;
|
|
15
|
+
|
|
16
|
+
urlRoot = '/__testacular/';
|
|
17
|
+
proxies = {
|
|
18
|
+
"/": "http://localhost:3001/"
|
|
19
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
basePath = "../../../";
|
|
2
|
+
|
|
3
|
+
files = [
|
|
4
|
+
JASMINE,
|
|
5
|
+
JASMINE_ADAPTER,
|
|
6
|
+
|
|
7
|
+
// Libraries
|
|
8
|
+
"vendor/assets/javascripts/jquery.js",
|
|
9
|
+
"vendor/assets/javascripts/angular/angular.js",
|
|
10
|
+
"vendor/assets/javascripts/angular/angular-resource.js",
|
|
11
|
+
"vendor/assets/javascripts/angular/angular-sanitize.js",
|
|
12
|
+
"vendor/assets/javascripts/angular/angular-bootstrap.js",
|
|
13
|
+
"vendor/assets/javascripts/*.js",
|
|
14
|
+
|
|
15
|
+
// The app
|
|
16
|
+
"app/assets/javascripts/app/controllers.js.coffee",
|
|
17
|
+
"app/assets/javascripts/app/**/*.js.coffee",
|
|
18
|
+
"app/assets/javascripts/application.js.coffee",
|
|
19
|
+
|
|
20
|
+
// Specs
|
|
21
|
+
"spec/javascripts/lib/angular-mocks.js",
|
|
22
|
+
|
|
23
|
+
"spec/javascripts/helpers/**/*.js.coffee",
|
|
24
|
+
"spec/javascripts/**/*_spec.js.coffee",
|
|
25
|
+
"spec/javascripts/**/*_spec.js.coffee",
|
|
26
|
+
|
|
27
|
+
// Templates
|
|
28
|
+
"app/assets/javascripts/templates/*.html.js"
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
preprocessors = {
|
|
32
|
+
"**/*.js.coffee": "coffee",
|
|
33
|
+
"**/*.html": "html2js"
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Test results reporter to use
|
|
37
|
+
// possible values: dots || progress
|
|
38
|
+
reporters = ["dots"];
|
|
39
|
+
|
|
40
|
+
browsers = ["PhantomJS"];
|
|
41
|
+
|
|
42
|
+
autoWatch = true;
|
|
43
|
+
singleRun = false;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
describe "collections 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
|
+
|
|
8
|
+
it "navigates to the valid url", ->
|
|
9
|
+
expect(browser().location().url()).toBe("/databases/first_database/collections")
|
|
10
|
+
|
|
11
|
+
it "displays a valid section title", ->
|
|
12
|
+
title = element("h2").text()
|
|
13
|
+
expect(title).toEqual("first_database collections")
|
|
14
|
+
|
|
15
|
+
it "displays available collections ordered by name", ->
|
|
16
|
+
expect(repeater("table.collections tbody tr").column("collection.name"))
|
|
17
|
+
.toEqual(["first_collection", "second_collection", "system.indexes", "third_collection"])
|
|
18
|
+
|
|
19
|
+
it "filters the collections list", ->
|
|
20
|
+
input("value").enter("")
|
|
21
|
+
expect(repeater("table.collections tbody tr").count()).toBe(4)
|
|
22
|
+
expect(repeater("table.collections tbody tr").column("collection.name"))
|
|
23
|
+
.toEqual(["first_collection", "second_collection", "system.indexes", "third_collection"])
|
|
24
|
+
|
|
25
|
+
input("value").enter("first")
|
|
26
|
+
expect(repeater("table.collections tbody tr").count()).toBe(1)
|
|
27
|
+
expect(repeater("table.collections tbody tr").column("collection.name"))
|
|
28
|
+
.toEqual(["first_collection"])
|
|
29
|
+
|
|
30
|
+
input("value").enter("second")
|
|
31
|
+
expect(repeater("table.collections tbody tr").count()).toBe(1)
|
|
32
|
+
expect(repeater("table.collections tbody tr").column("collection.name"))
|
|
33
|
+
.toEqual(["second_collection"])
|
|
34
|
+
|
|
35
|
+
input("value").enter("collection")
|
|
36
|
+
expect(repeater("table.collections tbody tr").count()).toBe(3)
|
|
37
|
+
expect(repeater("table.collections tbody tr").column("collection.name"))
|
|
38
|
+
.toEqual(["first_collection", "second_collection", "third_collection"])
|
|
39
|
+
|
|
40
|
+
input("value").enter("not existing")
|
|
41
|
+
expect(repeater("table.collections tbody tr").count()).toBe(0)
|
|
42
|
+
expect(element(".filter.alert:visible").text()).toMatch(/Nothing has been found./)
|
|
43
|
+
|
|
44
|
+
element("button:contains('Clear')").click()
|
|
45
|
+
expect(repeater("table.collections tbody tr").count()).toBe(4)
|
|
46
|
+
|
|
47
|
+
it "displays a tab with database stats", ->
|
|
48
|
+
element(".tabbable a:contains('Database stats')").click()
|
|
49
|
+
expect(repeater("table tbody tr").count()).toBeGreaterThan(0)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
describe "databases list page", ->
|
|
2
|
+
beforeEach ->
|
|
3
|
+
browser().navigateTo("/e2e/load_fixtures")
|
|
4
|
+
browser().navigateTo("/")
|
|
5
|
+
|
|
6
|
+
it "navigates to the valid url", ->
|
|
7
|
+
expect(browser().location().url()).toBe("/")
|
|
8
|
+
|
|
9
|
+
it "displays a valid section title", ->
|
|
10
|
+
title = element("h2").text()
|
|
11
|
+
expect(title).toEqual("localhost databases")
|
|
12
|
+
|
|
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
|
+
describe "filtering by database name", ->
|
|
18
|
+
it "displays all databases when the filter is not provided", ->
|
|
19
|
+
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"])
|
|
23
|
+
|
|
24
|
+
it "filters by database name", ->
|
|
25
|
+
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"])
|
|
29
|
+
|
|
30
|
+
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"])
|
|
34
|
+
|
|
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./)
|
|
38
|
+
|
|
39
|
+
it "displays all records when the filter is cleared", ->
|
|
40
|
+
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"])
|
|
44
|
+
|
|
45
|
+
describe "delete a database", ->
|
|
46
|
+
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?")
|
|
49
|
+
|
|
50
|
+
describe "when the dialog was disposed", ->
|
|
51
|
+
beforeEach ->
|
|
52
|
+
appElement "div.modal .modal-footer a:contains('Cancel')", ($element) ->
|
|
53
|
+
$element.click()
|
|
54
|
+
|
|
55
|
+
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"])
|
|
59
|
+
|
|
60
|
+
describe "when the dialog was confirmed", ->
|
|
61
|
+
beforeEach ->
|
|
62
|
+
appElement "div.bootbox a:contains('OK')", ($element) ->
|
|
63
|
+
$element.click()
|
|
64
|
+
|
|
65
|
+
# TODO figure out how to test it
|
|
66
|
+
xit "shows the alert", ->
|
|
67
|
+
expect(repeater("aside#alerts .alert").count()).toBe(1)
|
|
68
|
+
expect(repeater("aside#alerts .alert").column("message.text"))
|
|
69
|
+
.toContain("Database third_database has been deleted.")
|
|
70
|
+
|
|
71
|
+
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"])
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
|
|
9
|
+
it "shows the documents page", ->
|
|
10
|
+
expect(browser().location().url())
|
|
11
|
+
.toBe("/databases/first_database/collections/first_collection/documents")
|
|
12
|
+
|
|
13
|
+
title = element("h2").text()
|
|
14
|
+
expect(title).toEqual("first_collection documents")
|
|
15
|
+
|
|
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)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
describe "server info page", ->
|
|
2
|
+
beforeEach ->
|
|
3
|
+
browser().navigateTo("/")
|
|
4
|
+
element("a:contains('Server Info')").click()
|
|
5
|
+
|
|
6
|
+
it "displays details about the server", ->
|
|
7
|
+
expect(browser().location().url()).toBe("/server_info")
|
|
8
|
+
|
|
9
|
+
title = element("h2").text()
|
|
10
|
+
expect(title).toEqual("server info")
|
|
11
|
+
|
|
12
|
+
expect(repeater("table tbody tr").count()).toBeGreaterThan(0)
|