diff --git a/ui-v2/app/components/consul-node-list/index.hbs b/ui-v2/app/components/consul-node-list/index.hbs new file mode 100644 index 0000000000..8b2c516e9c --- /dev/null +++ b/ui-v2/app/components/consul-node-list/index.hbs @@ -0,0 +1,41 @@ +{{#if (gt items.length 0)}} + + +
+
+ Health +
+
+ + {{#if (eq 'critical' item.Status)}} + At least one health check on this node is failing. + {{else if (eq 'warning' item.Status)}} + At least one health check on this node has a warning. + {{else if (eq 'passing' item.Status)}} + All health checks are passing. + {{else}} + There are no health checks. + {{/if}} + +
+
+ + {{item.Node}} + +
+ + {{#if (eq item.Address leader.Address)}} + Leader + {{/if}} +
+
+ +
+
{{item.Address}}
+
+
+
+{{/if}} diff --git a/ui-v2/app/components/consul-node-list/index.js b/ui-v2/app/components/consul-node-list/index.js new file mode 100644 index 0000000000..4798652642 --- /dev/null +++ b/ui-v2/app/components/consul-node-list/index.js @@ -0,0 +1,5 @@ +import Component from '@ember/component'; + +export default Component.extend({ + tagName: '', +}); diff --git a/ui-v2/app/controllers/dc/nodes/index.js b/ui-v2/app/controllers/dc/nodes/index.js index 5454ee88a2..a33e295d30 100644 --- a/ui-v2/app/controllers/dc/nodes/index.js +++ b/ui-v2/app/controllers/dc/nodes/index.js @@ -2,26 +2,10 @@ import Controller from '@ember/controller'; export default Controller.extend({ queryParams: { - filterBy: { - as: 'status', - }, + sortBy: 'sort', search: { as: 'filter', replace: true, }, }, - actions: { - hasStatus: function(status, checks) { - if (status === '') { - return true; - } - return checks.some(item => item.Status === status); - }, - isHealthy: function(checks) { - return !this.actions.isUnhealthy.apply(this, [checks]); - }, - isUnhealthy: function(checks) { - return checks.some(item => item.Status === 'critical' || item.Status === 'warning'); - }, - }, }); diff --git a/ui-v2/app/initializers/search.js b/ui-v2/app/initializers/search.js index 42b92fad58..e6ed797ab7 100644 --- a/ui-v2/app/initializers/search.js +++ b/ui-v2/app/initializers/search.js @@ -23,8 +23,7 @@ export function initialize(application) { policy: policy(filterable), role: role(filterable), kv: kv(filterable), - healthyNode: node(filterable), - unhealthyNode: node(filterable), + node: node(filterable), serviceInstance: serviceNode(filterable), nodeservice: nodeService(filterable), service: service(filterable), diff --git a/ui-v2/app/initializers/sort.js b/ui-v2/app/initializers/sort.js index 4649bcfb62..6b173481b2 100644 --- a/ui-v2/app/initializers/sort.js +++ b/ui-v2/app/initializers/sort.js @@ -6,6 +6,7 @@ import token from 'consul-ui/sort/comparators/token'; import role from 'consul-ui/sort/comparators/role'; import policy from 'consul-ui/sort/comparators/policy'; import nspace from 'consul-ui/sort/comparators/nspace'; +import node from 'consul-ui/sort/comparators/node'; export function initialize(container) { // Service-less injection using private properties at a per-project level @@ -19,6 +20,7 @@ export function initialize(container) { role: role(), policy: policy(), nspace: nspace(), + node: node(), }; Sort.reopen({ comparator: function(type) { diff --git a/ui-v2/app/models/node.js b/ui-v2/app/models/node.js index 2081f6f719..f21186f845 100644 --- a/ui-v2/app/models/node.js +++ b/ui-v2/app/models/node.js @@ -1,5 +1,6 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; +import { computed } from '@ember/object'; export const PRIMARY_KEY = 'uid'; export const SLUG_KEY = 'ID'; @@ -20,4 +21,25 @@ export default Model.extend({ Coord: attr(), SyncTime: attr('number'), meta: attr(), + Status: computed('Checks.[]', 'ChecksCritical', 'ChecksPassing', 'ChecksWarning', function() { + switch (true) { + case this.ChecksCritical !== 0: + return 'critical'; + case this.ChecksWarning !== 0: + return 'warning'; + case this.ChecksPassing !== 0: + return 'passing'; + default: + return 'empty'; + } + }), + ChecksCritical: computed('Checks.[]', function() { + return this.Checks.filter(item => item.Status === 'critical').length; + }), + ChecksPassing: computed('Checks.[]', function() { + return this.Checks.filter(item => item.Status === 'passing').length; + }), + ChecksWarning: computed('Checks.[]', function() { + return this.Checks.filter(item => item.Status === 'warning').length; + }), }); diff --git a/ui-v2/app/routes/dc/nodes/index.js b/ui-v2/app/routes/dc/nodes/index.js index 783f1efa4f..472366e5b1 100644 --- a/ui-v2/app/routes/dc/nodes/index.js +++ b/ui-v2/app/routes/dc/nodes/index.js @@ -6,6 +6,7 @@ export default Route.extend({ repo: service('repository/node'), data: service('data-source/service'), queryParams: { + sortBy: 'sort', search: { as: 'filter', replace: true, diff --git a/ui-v2/app/sort/comparators/node.js b/ui-v2/app/sort/comparators/node.js new file mode 100644 index 0000000000..a5c906d10a --- /dev/null +++ b/ui-v2/app/sort/comparators/node.js @@ -0,0 +1,37 @@ +export default () => key => { + if (key.startsWith('Status:')) { + return function(serviceA, serviceB) { + const [, dir] = key.split(':'); + let a, b; + if (dir === 'asc') { + b = serviceA; + a = serviceB; + } else { + a = serviceA; + b = serviceB; + } + switch (true) { + case a.ChecksCritical > b.ChecksCritical: + return 1; + case a.ChecksCritical < b.ChecksCritical: + return -1; + default: + switch (true) { + case a.ChecksWarning > b.ChecksWarning: + return 1; + case a.ChecksWarning < b.ChecksWarning: + return -1; + default: + switch (true) { + case a.ChecksPassing < b.ChecksPassing: + return 1; + case a.ChecksPassing > b.ChecksPassing: + return -1; + } + } + return 0; + } + }; + } + return key; +}; diff --git a/ui-v2/app/styles/base/components/pill/layout.scss b/ui-v2/app/styles/base/components/pill/layout.scss index 6f97559aec..56cf8623b1 100644 --- a/ui-v2/app/styles/base/components/pill/layout.scss +++ b/ui-v2/app/styles/base/components/pill/layout.scss @@ -27,5 +27,9 @@ width: 14px; height: 14px; margin-right: 2px; + margin-top: 1px; position: relative; } +%reduced-pill.leader::before { + margin-right: 4px; +} diff --git a/ui-v2/app/styles/base/components/pill/skin.scss b/ui-v2/app/styles/base/components/pill/skin.scss index 31d1ed0b95..2ba8e1ff90 100644 --- a/ui-v2/app/styles/base/components/pill/skin.scss +++ b/ui-v2/app/styles/base/components/pill/skin.scss @@ -34,3 +34,7 @@ @extend %with-gateway-mask; background-color: $gray-500; } +%reduced-pill.leader::before { + @extend %with-star-outline-mask; + background-color: $gray-500; +} diff --git a/ui-v2/app/styles/base/icons/base-variables.scss b/ui-v2/app/styles/base/icons/base-variables.scss index d3e2e48d97..09fc28c599 100644 --- a/ui-v2/app/styles/base/icons/base-variables.scss +++ b/ui-v2/app/styles/base/icons/base-variables.scss @@ -156,7 +156,7 @@ $settings-svg: url('data:image/svg+xml;charset=UTF-8,'); $sort-svg: url('data:image/svg+xml;charset=UTF-8,'); $star-fill-svg: url('data:image/svg+xml;charset=UTF-8,'); -$star-outline-svg: url('data:image/svg+xml;charset=UTF-8,'); +$star-outline-svg: url('data:image/svg+xml;charset=UTF-8,'); $star-svg: url('data:image/svg+xml;charset=UTF-8,'); $sub-left-svg: url('data:image/svg+xml;charset=UTF-8,'); $sub-right-svg: url('data:image/svg+xml;charset=UTF-8,'); diff --git a/ui-v2/app/styles/components/composite-row/layout.scss b/ui-v2/app/styles/components/composite-row/layout.scss index 2f08ea6021..289dd4f3e4 100644 --- a/ui-v2/app/styles/components/composite-row/layout.scss +++ b/ui-v2/app/styles/components/composite-row/layout.scss @@ -44,6 +44,7 @@ } %composite-row-icon { margin-right: 6px; + margin-left: -2px; } %composite-row-icon dt { display: none; diff --git a/ui-v2/app/styles/components/pill.scss b/ui-v2/app/styles/components/pill.scss index af6ecd29c8..c9222f5586 100644 --- a/ui-v2/app/styles/components/pill.scss +++ b/ui-v2/app/styles/components/pill.scss @@ -6,7 +6,8 @@ td strong { span.policy-service-identity, span.policy-node-identity, .consul-external-source, -.consul-kind { +.consul-kind, +.leader { @extend %reduced-pill; } span.policy-service-identity::before, diff --git a/ui-v2/app/templates/dc/nodes/index.hbs b/ui-v2/app/templates/dc/nodes/index.hbs index 3ecdf5cf9f..c5b38372b6 100644 --- a/ui-v2/app/templates/dc/nodes/index.hbs +++ b/ui-v2/app/templates/dc/nodes/index.hbs @@ -1,137 +1,74 @@ {{title 'Nodes'}} -{{#let (selectable-key-values - (array "" "All (Any Status)") - (array "critical" "Critical Checks") - (array "warning" "Warning Checks") - (array "passing" "Passing Checks") - selected=filterBy - ) - as |filter| -}} - - -

- Nodes {{format-number items.length}} total -

- -
- -{{#if (gt items.length 0) }} +{{#let (or sortBy "Node:asc") as |sort|}} + + +

+ Nodes {{format-number items.length}} total +

+ +
+ + {{#if (gt items.length 0) }} -{{/if}} - - -{{#let (filter-by "Checks" (action "isUnhealthy") items) as |unhealthy|}} - {{#if (gt unhealthy.length 0) }} -
-

Unhealthy Nodes

-
- {{! think about 2 differing views here }} -
    - - - {{#each unhealthy as |item|}} - - - {{#if (eq item.Address leader.Address)}} - Leader - {{/if}} - - - {{/each}} - - -
  • - - -

    No nodes found

    -
    - -

    - There don't seem to be any nodes matching that search. -

    -
    -
    -
  • -
    -
    -
-
-
- {{/if}} -{{/let}} -{{#let (filter-by "Checks" (action "isHealthy") items) as |healthy|}} - {{#if (gt healthy.length 0) }} -
-

Healthy Nodes

- - - - - - {{#if (eq item.Address leader.Address)}} - Leader - {{/if}} - - - - - - - -

No nodes found

-
- -

- There don't seem to be any nodes matching that search. -

-
-
-
-
-
- {{/if}} -{{/let}} -{{#if (eq items.length 0) }} - - -

Welcome to Nodes

+ class="with-sort" + > + + + + + {{#let (from-entries (array + (array "Node:asc" "A to Z") + (array "Node:desc" "Z to A") + (array "Status:asc" "Unhealthy to Healthy") + (array "Status:desc" "Healthy to Unhealthy") + )) + as |selectable| + }} + {{get selectable sort}} + {{/let}} + + + + {{#let components.Optgroup components.Option as |Optgroup Option|}} + + + + + + + + + {{/let}} + + - -

- There don't seem to be any nodes, or you may not have access to view nodes yet. -

+ + {{/if}} +
+ + {{#let (sort-by (comparator 'node' sort) items) as |sorted|}} + + + -
-{{/if}} -
-
+ + + +

+ There don't seem to be any registered nodes, or you may not have access to view nodes yet. +

+
+
+
+ + {{/let}} +
+
{{/let}} \ No newline at end of file diff --git a/ui-v2/tests/acceptance/components/catalog-filter.feature b/ui-v2/tests/acceptance/components/catalog-filter.feature index 68b9e02c5e..82a4b14b43 100644 --- a/ui-v2/tests/acceptance/components/catalog-filter.feature +++ b/ui-v2/tests/acceptance/components/catalog-filter.feature @@ -3,68 +3,6 @@ # to use the name filter UI also, then they can stay together @setupApplicationTest Feature: components / catalog-filter - Scenario: Filtering [Model] - Given 1 datacenter model with the value "dc-1" - And 4 service models from yaml - --- - - ChecksPassing: 1 - ChecksWarning: 0 - ChecksCritical: 0 - - ChecksPassing: 0 - ChecksWarning: 1 - ChecksCritical: 0 - - ChecksPassing: 0 - ChecksWarning: 0 - ChecksCritical: 1 - - ChecksPassing: 1 - ChecksWarning: 0 - ChecksCritical: 0 - --- - And 4 node models from yaml - --- - - Checks: - - Status: passing - - Checks: - - Status: warning - - Checks: - - Status: critical - - Checks: - - Status: passing - --- - When I visit the [Page] page for yaml - --- - dc: dc-1 - --- - Then the url should be [Url] - - Then I see 4 [Model] models - And I see allIsSelected on the filter - - When I click passing on the filter - And I see passingIsSelected on the filter - And I see 2 [Model] models - - When I click warning on the filter - And I see warningIsSelected on the filter - And I see 1 [Model] model - - When I click critical on the filter - And I see criticalIsSelected on the filter - And I see 1 [Model] model - - When I click all on the filter - And I see allIsSelected on the filter - Then I fill in with yaml - --- - s: [Model]-0 - --- - And I see 1 [Model] model with the name "[Model]-0" - - Where: - ------------------------------------------------- - | Model | Page | Url | - | node | nodes | /dc-1/nodes | - ------------------------------------------------- Scenario: Filtering [Model] in [Page] Given 1 datacenter model with the value "dc1" And 1 node model from yaml diff --git a/ui-v2/tests/acceptance/dc/nodes/empty-ids.feature b/ui-v2/tests/acceptance/dc/nodes/empty-ids.feature index e4807d2944..52df760afb 100644 --- a/ui-v2/tests/acceptance/dc/nodes/empty-ids.feature +++ b/ui-v2/tests/acceptance/dc/nodes/empty-ids.feature @@ -20,14 +20,11 @@ Feature: dc / nodes / empty-ids: Hedge for if nodes come in over the API with no dc: dc-1 --- Then the url should be /dc-1/nodes - Then I see name on the nodes like yaml + Then I see name on the nodes vertically like yaml --- - name-1 - name-2 - name-3 - name-4 - name-5 - -@ignore - Scenario: Visually comparing - Then the ".unhealthy" element should look like the "/node_modules/@hashicorp/consul-testing-extras/fixtures/dc/nodes/empty-ids.png" image + --- \ No newline at end of file diff --git a/ui-v2/tests/acceptance/dc/nodes/index.feature b/ui-v2/tests/acceptance/dc/nodes/index.feature index e42bb68213..07a4f3ef40 100644 --- a/ui-v2/tests/acceptance/dc/nodes/index.feature +++ b/ui-v2/tests/acceptance/dc/nodes/index.feature @@ -16,7 +16,7 @@ Feature: dc / nodes / index Then the url should be /dc-1/nodes And the title should be "Nodes - Consul" Then I see 3 node models - Scenario: Seeing the leader in unhealthy listing + Scenario: Seeing the leader in node listing Given 3 node models from yaml --- - Address: 211.245.86.75 @@ -32,24 +32,7 @@ Feature: dc / nodes / index --- Then the url should be /dc-1/nodes Then I see 3 node models - And I see leader on the unHealthyNodes - Scenario: Seeing the leader in healthy listing - Given 3 node models from yaml - --- - - Address: 211.245.86.75 - Checks: - - Status: passing - Name: Passing check - - Address: 10.0.0.1 - - Address: 10.0.0.3 - --- - When I visit the nodes page for yaml - --- - dc: dc-1 - --- - Then the url should be /dc-1/nodes - Then I see 3 node models - And I see leader on the healthyNodes + And I see leader on the nodes.0 Scenario: Searching the nodes with name and IP address Given 3 node models from yaml --- @@ -76,4 +59,4 @@ Feature: dc / nodes / index s: 10.0.0.1 --- And I see 1 node model - And I see 1 node model with the name "node-02" + And I see 1 node model with the name "node-02" \ No newline at end of file diff --git a/ui-v2/tests/acceptance/dc/nodes/navigation.feature b/ui-v2/tests/acceptance/dc/nodes/navigation.feature new file mode 100644 index 0000000000..116e26ff89 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/nodes/navigation.feature @@ -0,0 +1,16 @@ +@setupApplicationTest +Feature: dc / nodes / navigation + Scenario: Clicking a node in the listing and back again + Given 1 datacenter model with the value "dc-1" + And 3 node models + When I visit the nodes page for yaml + --- + dc: dc-1 + --- + Then the url should be /dc-1/nodes + And the title should be "Nodes - Consul" + Then I see 3 node models + When I click node on the nodes + And I click "[data-test-back]" + Then the url should be /dc-1/nodes + diff --git a/ui-v2/tests/acceptance/dc/nodes/sorting.feature b/ui-v2/tests/acceptance/dc/nodes/sorting.feature new file mode 100644 index 0000000000..d23795d924 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/nodes/sorting.feature @@ -0,0 +1,73 @@ +@setupApplicationTest +Feature: dc / nodes / sorting + Scenario: + Given 1 datacenter model with the value "dc-1" + And 6 node models from yaml + --- + - Node: Node-A + Checks: + - Status: critical + - Node: Node-B + Checks: + - Status: passing + - Node: Node-C + Checks: + - Status: warning + - Node: Node-D + Checks: + - Status: critical + - Node: Node-E + Checks: + - Status: critical + - Node: Node-F + Checks: + - Status: warning + --- + When I visit the nodes page for yaml + --- + dc: dc-1 + --- + When I click selected on the sort + When I click options.3.button on the sort + Then I see name on the nodes vertically like yaml + --- + - Node-B + - Node-C + - Node-F + - Node-A + - Node-D + - Node-E + --- + When I click selected on the sort + When I click options.2.button on the sort + Then I see name on the nodes vertically like yaml + --- + - Node-A + - Node-D + - Node-E + - Node-C + - Node-F + - Node-B + --- + When I click selected on the sort + When I click options.0.button on the sort + Then I see name on the nodes vertically like yaml + --- + - Node-A + - Node-B + - Node-C + - Node-D + - Node-E + - Node-F + --- + When I click selected on the sort + When I click options.1.button on the sort + Then I see name on the nodes vertically like yaml + --- + - Node-F + - Node-E + - Node-D + - Node-C + - Node-B + - Node-A + --- diff --git a/ui-v2/tests/acceptance/page-navigation.feature b/ui-v2/tests/acceptance/page-navigation.feature index 6331a044ed..b1e98fcf39 100644 --- a/ui-v2/tests/acceptance/page-navigation.feature +++ b/ui-v2/tests/acceptance/page-navigation.feature @@ -43,7 +43,6 @@ Feature: page-navigation ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | Item | Model | URL | Endpoint | Back | | service | services | /dc-1/services/service-0/instances | /v1/discovery-chain/service-0?dc=dc-1&ns=@namespace | /dc-1/services | - | node | nodes | /dc-1/nodes/node-0/health-checks | /v1/session/node/node-0?dc=dc-1&ns=@namespace | /dc-1/nodes | | kv | kvs | /dc-1/kv/0-key-value/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1&ns=@namespace | /dc-1/kv | # | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/ui-v2/tests/acceptance/steps/dc/nodes/navigation-steps.js b/ui-v2/tests/acceptance/steps/dc/nodes/navigation-steps.js new file mode 100644 index 0000000000..ba1093295f --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/nodes/navigation-steps.js @@ -0,0 +1,10 @@ +import steps from '../../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui-v2/tests/acceptance/steps/dc/nodes/sorting-steps.js b/ui-v2/tests/acceptance/steps/dc/nodes/sorting-steps.js new file mode 100644 index 0000000000..ba1093295f --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/nodes/sorting-steps.js @@ -0,0 +1,10 @@ +import steps from '../../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui-v2/tests/acceptance/steps/nodes/sorting-steps.js b/ui-v2/tests/acceptance/steps/nodes/sorting-steps.js new file mode 100644 index 0000000000..3c9a76f69f --- /dev/null +++ b/ui-v2/tests/acceptance/steps/nodes/sorting-steps.js @@ -0,0 +1,10 @@ +import steps from '../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui-v2/tests/integration/components/consul-node-list-test.js b/ui-v2/tests/integration/components/consul-node-list-test.js new file mode 100644 index 0000000000..6ad53b10da --- /dev/null +++ b/ui-v2/tests/integration/components/consul-node-list-test.js @@ -0,0 +1,26 @@ +import { module, skip } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | consul-node-list', function(hooks) { + setupRenderingTest(hooks); + + skip('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); diff --git a/ui-v2/tests/pages.js b/ui-v2/tests/pages.js index 24ae13a0a8..2735a3a47f 100644 --- a/ui-v2/tests/pages.js +++ b/ui-v2/tests/pages.js @@ -79,9 +79,6 @@ const tokenList = tokenListFactory(clickable, attribute, collection, deletable); const authForm = authFormFactory(submitable, clickable, attribute); const freetextFilter = freetextFilterFactory(triggerable); const catalogToolbar = searchBarFactory(freetextFilter); -const catalogFilter = searchBarFactory(freetextFilter, () => - radiogroup('status', ['', 'passing', 'warning', 'critical']) -); const aclFilter = searchBarFactory(freetextFilter, () => radiogroup('type', ['', 'management', 'client']) ); @@ -153,7 +150,7 @@ export default { service(visitable, attribute, collection, text, consulIntentionList, catalogToolbar, tabgroup) ), instance: create(instance(visitable, attribute, collection, text, tabgroup)), - nodes: create(nodes(visitable, clickable, attribute, collection, catalogFilter)), + nodes: create(nodes(visitable, text, clickable, attribute, collection, popoverSelect)), node: create(node(visitable, deletable, clickable, attribute, collection, tabgroup, text)), kvs: create(kvs(visitable, creatable, consulKvList)), kv: create(kv(visitable, attribute, submitable, deletable, cancelable, clickable)), diff --git a/ui-v2/tests/pages/dc/nodes/index.js b/ui-v2/tests/pages/dc/nodes/index.js index 335c87afe3..c26bbd4153 100644 --- a/ui-v2/tests/pages/dc/nodes/index.js +++ b/ui-v2/tests/pages/dc/nodes/index.js @@ -1,14 +1,13 @@ -export default function(visitable, clickable, attribute, collection, filter) { +export default function(visitable, text, clickable, attribute, collection, popoverSelect) { const node = { - name: attribute('data-test-node'), + name: text('[data-test-node]'), leader: attribute('data-test-leader', '[data-test-leader]'), - node: clickable('header a'), + node: clickable('a'), }; return { visit: visitable('/:dc/nodes'), - nodes: collection('[data-test-node]', node), - healthyNodes: collection('.healthy [data-test-node]', node), - unHealthyNodes: collection('.unhealthy [data-test-node]', node), - filter: filter('[data-test-catalog-filter]'), + nodes: collection('.consul-node-list [data-test-list-row]', node), + home: clickable('[data-test-home]'), + sort: popoverSelect(), }; }