From b7e97817d7d7af4bef0d23cbb36642f30da76273 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 29 Sep 2017 17:40:45 -0700 Subject: [PATCH 1/5] Expose the token service in all templates for convenience --- ui/app/initializers/app-token.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 ui/app/initializers/app-token.js diff --git a/ui/app/initializers/app-token.js b/ui/app/initializers/app-token.js new file mode 100644 index 000000000..f8b3dcee2 --- /dev/null +++ b/ui/app/initializers/app-token.js @@ -0,0 +1,12 @@ +export function initialize() { + const application = arguments[1] || arguments[0]; + + // Provides the acl token service to all templates + application.inject('controller', 'token', 'service:token'); + application.inject('component', 'token', 'service:token'); +} + +export default { + name: 'app-token', + initialize, +}; From 080ebec708abe06288780243f8e0f4c6cbe4024a Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 29 Sep 2017 17:41:12 -0700 Subject: [PATCH 2/5] Add empty states to the job list page --- ui/app/components/search-box.js | 2 +- ui/app/styles/components.scss | 1 + ui/app/styles/components/empty-message.scss | 21 +++++++++++++++ ui/app/templates/jobs/index.hbs | 20 +++++++++++--- ui/tests/acceptance/jobs-list-test.js | 29 ++++++++++++++++++++- 5 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 ui/app/styles/components/empty-message.scss diff --git a/ui/app/components/search-box.js b/ui/app/components/search-box.js index 8efb5dd5e..19aa76bef 100644 --- a/ui/app/components/search-box.js +++ b/ui/app/components/search-box.js @@ -12,7 +12,7 @@ export default Component.extend({ // Used to throttle sets to searchTerm debounce: 150, - classNames: ['field', 'has-addons'], + classNames: ['search-box', 'field', 'has-addons'], actions: { setSearchTerm(e) { diff --git a/ui/app/styles/components.scss b/ui/app/styles/components.scss index 7832c934d..50f6ef1c8 100644 --- a/ui/app/styles/components.scss +++ b/ui/app/styles/components.scss @@ -1,6 +1,7 @@ @import "./components/badge"; @import "./components/boxed-section"; @import "./components/breadcrumbs"; +@import "./components/empty-message"; @import "./components/gutter"; @import "./components/inline-definitions"; @import "./components/job-diff"; diff --git a/ui/app/styles/components/empty-message.scss b/ui/app/styles/components/empty-message.scss new file mode 100644 index 000000000..3f00021eb --- /dev/null +++ b/ui/app/styles/components/empty-message.scss @@ -0,0 +1,21 @@ +.empty-message { + padding: 1.5rem; + background: $white-ter; + border-radius: $radius; + + .empty-message-headline { + font-size: $size-3; + color: $grey; + text-align: center; + } + + .empty-message-body { + padding: 0 20%; + text-align: center; + color: $grey; + + strong { + color: $grey; + } + } +} diff --git a/ui/app/templates/jobs/index.hbs b/ui/app/templates/jobs/index.hbs index 4ab6544fe..e5a57b38c 100644 --- a/ui/app/templates/jobs/index.hbs +++ b/ui/app/templates/jobs/index.hbs @@ -3,9 +3,11 @@ {{/global-header}} {{#gutter-menu class="page-body"}}
-
-
{{search-box searchTerm=(mut searchTerm) placeholder="Search jobs..."}}
-
+ {{#if model.length}} +
+
{{search-box searchTerm=(mut searchTerm) placeholder="Search jobs..."}}
+
+ {{/if}} {{#list-pagination source=sortedJobs size=pageSize @@ -37,6 +39,18 @@
    + {{else}} +
    + {{#if (eq model.length 0)}} +

    No Jobs

    +

    + There are currently no visible jobs in the cluster. It could be that the cluster is empty. It could also mean {{#link-to "settings.tokens"}}you don't have access to see any jobs{{/link-to}}. +

    + {{else if searchTerm}} +

    No Matches

    +

    No jobs match the term {{searchTerm}}

    + {{/if}} +
    {{/list-pagination}}
    {{/gutter-menu}} diff --git a/ui/tests/acceptance/jobs-list-test.js b/ui/tests/acceptance/jobs-list-test.js index 699739fe4..36f0fb576 100644 --- a/ui/tests/acceptance/jobs-list-test.js +++ b/ui/tests/acceptance/jobs-list-test.js @@ -1,5 +1,5 @@ import Ember from 'ember'; -import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers'; +import { click, find, findAll, currentURL, visit, fillIn } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; @@ -80,3 +80,30 @@ test('each job row should link to the corresponding job', function(assert) { assert.equal(currentURL(), `/jobs/${job.id}`); }); }); + +test('when there are no jobs, there is an empty message', function(assert) { + visit('/jobs'); + + andThen(() => { + assert.ok(find('.empty-message')); + assert.equal(find('.empty-message-headline').textContent, 'No Jobs'); + }); +}); + +test('when there are jobs, but no matches for a search result, there is an empty message', function( + assert +) { + server.create('job', { name: 'cat 1' }); + server.create('job', { name: 'cat 2' }); + + visit('/jobs'); + + andThen(() => { + fillIn('.search-box input', 'dog'); + }); + + andThen(() => { + assert.ok(find('.empty-message')); + assert.equal(find('.empty-message-headline').textContent, 'No Matches'); + }); +}); From 7cae2e92f2be6f2672d148d17a9aa84741dea622 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 29 Sep 2017 18:33:57 -0700 Subject: [PATCH 3/5] Empty states for clients --- ui/app/templates/nodes/index.hbs | 20 ++++++++++++++--- ui/tests/acceptance/nodes-list-test.js | 31 +++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/ui/app/templates/nodes/index.hbs b/ui/app/templates/nodes/index.hbs index 580adbbae..1b324c69e 100644 --- a/ui/app/templates/nodes/index.hbs +++ b/ui/app/templates/nodes/index.hbs @@ -3,9 +3,11 @@ {{/global-header}} {{#gutter-menu class="page-body"}}
    -
    -
    {{search-box searchTerm=(mut searchTerm) placeholder="Search nodes..."}}
    -
    + {{#if nodes.length}} +
    +
    {{search-box searchTerm=(mut searchTerm) placeholder="Search nodes..."}}
    +
    + {{/if}} {{#list-pagination source=sortedNodes size=pageSize @@ -38,6 +40,18 @@
      + {{else}} +
      + {{#if (eq nodes.length 0)}} +

      No Clients

      +

      + There are currently no visible nodes in the cluster. This could mean that the cluster is bootstrapped with no clients. It could also mean {{#link-to "settings.tokens"}}you don't have access to see any clients{{/link-to}}. +

      + {{else if searchTerm}} +

      No Matches

      +

      No clients match the term {{searchTerm}}

      + {{/if}} +
      {{/list-pagination}}
      {{/gutter-menu}} diff --git a/ui/tests/acceptance/nodes-list-test.js b/ui/tests/acceptance/nodes-list-test.js index 4a6ee0301..81cb509e6 100644 --- a/ui/tests/acceptance/nodes-list-test.js +++ b/ui/tests/acceptance/nodes-list-test.js @@ -1,5 +1,5 @@ import Ember from 'ember'; -import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers'; +import { click, find, findAll, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; import { findLeader } from '../../mirage/config'; @@ -75,6 +75,35 @@ test('each client should link to the client detail page', function(assert) { }); }); +test('when there are no clients, there is an empty message', function(assert) { + server.createList('agent', 1); + + visit('/nodes'); + + andThen(() => { + assert.ok(find('.empty-message')); + assert.equal(find('.empty-message-headline').textContent, 'No Clients'); + }); +}); + +test('when there are clients, but no matches for a search term, there is an empty message', function( + assert +) { + server.createList('agent', 1); + server.create('node', { name: 'node' }); + + visit('/nodes'); + + andThen(() => { + fillIn('.search-box input', 'client'); + }); + + andThen(() => { + assert.ok(find('.empty-message')); + assert.equal(find('.empty-message-headline').textContent, 'No Matches'); + }); +}); + test('/servers should list all servers', function(assert) { const agentsCount = 10; const pageSize = 8; From 945cb3f03eb6d2cbf9f9310c5c61a2fa2d84c467 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 29 Sep 2017 19:20:28 -0700 Subject: [PATCH 4/5] Empty states for servers --- ui/app/templates/servers.hbs | 13 ++++++++++++- ui/tests/acceptance/nodes-list-test.js | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ui/app/templates/servers.hbs b/ui/app/templates/servers.hbs index e4d12d28b..3244a306e 100644 --- a/ui/app/templates/servers.hbs +++ b/ui/app/templates/servers.hbs @@ -1,6 +1,6 @@
      {{#global-header class="page-header"}} - Nodes + Servers {{/global-header}} {{#gutter-menu class="page-body"}}
      @@ -35,6 +35,17 @@
        + {{else}} +
        +

        Invalid Permissions

        +

        + {{#if token.secret}} + Your ACL token does not grant access to see servers. + {{else}} + You have no ACL token set. {{#link-to "settings.tokens"}}Provide a token{{/link-to}} with the appropriate permissions to see servers. + {{/if}} +

        +
        {{/list-pagination}} {{outlet}} diff --git a/ui/tests/acceptance/nodes-list-test.js b/ui/tests/acceptance/nodes-list-test.js index 81cb509e6..23e1b10ed 100644 --- a/ui/tests/acceptance/nodes-list-test.js +++ b/ui/tests/acceptance/nodes-list-test.js @@ -170,3 +170,24 @@ test('each server should link to the server detail page', function(assert) { assert.equal(currentURL(), `/servers/${agent.name}`); }); }); + +test('when the API returns no agents, show an empty message', function(assert) { + minimumSetup(); + + // Override the members handler to act as if server-side permissions + // are preventing a qualified response. + server.pretender.get('/v1/agent/members', () => [ + 200, + {}, + JSON.stringify({ + Members: [], + }), + ]); + + visit('/servers'); + + andThen(() => { + assert.ok(find('.empty-message')); + assert.equal(find('.empty-message-headline').textContent, 'Invalid Permissions'); + }); +}); From c988de30c5528a785694dfe755843820d8111c8b Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Mon, 2 Oct 2017 12:44:07 -0700 Subject: [PATCH 5/5] Empty state for allocations search on task group detail --- ui/app/templates/jobs/job/task-group.hbs | 7 +++++++ ui/tests/acceptance/task-group-detail-test.js | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index fc39e5589..bb4a0ed93 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -90,6 +90,13 @@ {{#p.last class="pagination-link"}} >| {{/p.last}} + {{else}} +
        +
        +

        No Matches

        +

        No allocations match the term {{searchTerm}}

        +
        +
        {{/list-pagination}} diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js index 800e57544..593412317 100644 --- a/ui/tests/acceptance/task-group-detail-test.js +++ b/ui/tests/acceptance/task-group-detail-test.js @@ -1,5 +1,5 @@ import Ember from 'ember'; -import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers'; +import { click, find, findAll, fillIn, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; @@ -41,6 +41,11 @@ moduleForAcceptance('Acceptance | task group detail', { taskGroup: taskGroups[1].name, }); + // Set a static name to make the search test deterministic + server.db.allocations.forEach(alloc => { + alloc.name = 'aaaaa'; + }); + visit(`/jobs/${job.id}/${taskGroup.name}`); }, }); @@ -212,3 +217,12 @@ test('each allocation should show stats about the allocation, retrieved directly `Requests ${nodeStatsUrl}` ); }); + +test('when the allocation search has no matches, there is an empty message', function(assert) { + fillIn('.search-box input', 'zzzzzz'); + + andThen(() => { + assert.ok(find('.allocations .empty-message')); + assert.equal(find('.allocations .empty-message-headline').textContent, 'No Matches'); + }); +});