From efe6f3a95d68a530a4428b5de191103566ee9e6c Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 22 Mar 2023 09:43:39 -0400 Subject: [PATCH 1/5] Functioning searchbox --- .../job-status/deployment-history.hbs | 8 ++++++- .../job-status/deployment-history.js | 22 +++++++++++++++++++ .../styles/components/job-status-panel.scss | 12 +++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/ui/app/components/job-status/deployment-history.hbs b/ui/app/components/job-status/deployment-history.hbs index dc2fe778718..57c96aa88b9 100644 --- a/ui/app/components/job-status/deployment-history.hbs +++ b/ui/app/components/job-status/deployment-history.hbs @@ -1,5 +1,11 @@
-

Deployment History

+
+

Deployment History

+ +
    {{#each this.history as |deployment-log|}}
  1. diff --git a/ui/app/components/job-status/deployment-history.js b/ui/app/components/job-status/deployment-history.js index 8fc539bd966..44849c710a1 100644 --- a/ui/app/components/job-status/deployment-history.js +++ b/ui/app/components/job-status/deployment-history.js @@ -55,6 +55,7 @@ export default class JobStatusDeploymentHistoryComponent extends Component { .flat() ) .flat() + .filter((a) => this.filterOnSearchTerm(a)) .sort((a, b) => a.get('time') - b.get('time')) .reverse(); } catch (e) { @@ -71,4 +72,25 @@ export default class JobStatusDeploymentHistoryComponent extends Component { color: 'critical', }); } + + // #region search + + /** + * @type { string } + */ + @tracked searchTerm = ''; + + /** + * @param { import('../../models/task-event').default } taskEvent + * @returns { boolean } + */ + filterOnSearchTerm(taskEvent) { + return ( + taskEvent.message.toLowerCase().includes(this.searchTerm.toLowerCase()) || + taskEvent.type.toLowerCase().includes(this.searchTerm.toLowerCase()) || + taskEvent.state.allocation.shortId.includes(this.searchTerm.toLowerCase()) + ); + } + + // #endregion search } diff --git a/ui/app/styles/components/job-status-panel.scss b/ui/app/styles/components/job-status-panel.scss index 56613bbed2f..c6eafeaf482 100644 --- a/ui/app/styles/components/job-status-panel.scss +++ b/ui/app/styles/components/job-status-panel.scss @@ -173,10 +173,20 @@ display: grid; grid-template-columns: 70% auto; gap: 1rem; - margin-top: 1rem; // TODO: grid-ify the deployment panel generally and just use gap for this + margin-top: 2rem; // TODO: grid-ify the deployment panel generally and just use gap for this } .deployment-history { + & > header { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 1rem; + margin-bottom: 1rem; + align-items: end; + & > .search-box { + max-width: unset; + } + } & > ol { max-height: 300px; overflow-y: auto; From eb14519303bf086d45d8ca4c6cbd827044eb7fbf Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 22 Mar 2023 10:01:25 -0400 Subject: [PATCH 2/5] Some nice animations for history items --- .../job-status/deployment-history.hbs | 20 +++++++---- .../styles/components/job-status-panel.scss | 36 +++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/ui/app/components/job-status/deployment-history.hbs b/ui/app/components/job-status/deployment-history.hbs index 57c96aa88b9..515c3fe77a7 100644 --- a/ui/app/components/job-status/deployment-history.hbs +++ b/ui/app/components/job-status/deployment-history.hbs @@ -25,12 +25,20 @@
{{else}} - {{#if this.deploymentAllocations}} -
  • -
    - No deployment events yet -
    -
  • + {{#if this.deploymentAllocations.length}} + {{#if this.searchTerm}} +
  • +
    + No events march {{this.searchTerm}} +
    +
  • + {{else}} +
  • +
    + No deployment events yet +
    +
  • + {{/if}} {{else}}
  • diff --git a/ui/app/styles/components/job-status-panel.scss b/ui/app/styles/components/job-status-panel.scss index c6eafeaf482..3b7f78dd64c 100644 --- a/ui/app/styles/components/job-status-panel.scss +++ b/ui/app/styles/components/job-status-panel.scss @@ -192,6 +192,22 @@ overflow-y: auto; } & > ol > li { + @for $i from 1 through 50 { + &:nth-child(#{$i}) { + animation-name: historyItemSlide; + animation-duration: 0.2s; + animation-fill-mode: both; + animation-delay: 0.1s + (0.05 * $i); + } + + &:nth-child(#{$i}) > div { + animation-name: historyItemShine; + animation-duration: 1s; + animation-fill-mode: both; + animation-delay: 0.1s + (0.05 * $i); + } + } + & > div { gap: 0.5rem; } @@ -228,3 +244,23 @@ } } } + +@keyframes historyItemSlide { + from { + opacity: 0; + top: 40px; + } + to { + opacity: 1; + top: 0px; + } +} + +@keyframes historyItemShine { + from { + box-shadow: inset 0 0 0 100px rgba(255, 200, 0, 0.2); + } + to { + box-shadow: inset 0 0 0 100px rgba(255, 200, 0, 0); + } +} From 8adb9f0d3f53c8e49dc394baf6185ead79300297 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 22 Mar 2023 11:19:58 -0400 Subject: [PATCH 3/5] History search test --- .../job-status/deployment-history.hbs | 5 +- ui/mirage/factories/allocation.js | 2 +- ui/mirage/factories/task-event.js | 4 +- ui/tests/acceptance/job-status-panel-test.js | 70 ++++++++++++++++++- 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/ui/app/components/job-status/deployment-history.hbs b/ui/app/components/job-status/deployment-history.hbs index 515c3fe77a7..ec0efacc3d6 100644 --- a/ui/app/components/job-status/deployment-history.hbs +++ b/ui/app/components/job-status/deployment-history.hbs @@ -2,6 +2,7 @@

    Deployment History

    @@ -27,9 +28,9 @@ {{else}} {{#if this.deploymentAllocations.length}} {{#if this.searchTerm}} -
  • +
  • - No events march {{this.searchTerm}} + No events match {{this.searchTerm}}
  • {{else}} diff --git a/ui/mirage/factories/allocation.js b/ui/mirage/factories/allocation.js index 724faddfe77..6822419d138 100644 --- a/ui/mirage/factories/allocation.js +++ b/ui/mirage/factories/allocation.js @@ -13,7 +13,7 @@ const REF_TIME = new Date(); export default Factory.extend({ id: (i) => (i >= 100 ? `${UUIDS[i % 100]}-${i}` : UUIDS[i]), - jobVersion: 1, + jobVersion: 0, modifyIndex: () => faker.random.number({ min: 10, max: 2000 }), modifyTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000, diff --git a/ui/mirage/factories/task-event.js b/ui/mirage/factories/task-event.js index 0bbd8ae8a7a..961828741b0 100644 --- a/ui/mirage/factories/task-event.js +++ b/ui/mirage/factories/task-event.js @@ -12,5 +12,7 @@ export default Factory.extend({ exitCode: () => null, time: () => faker.date.past(2 / 365, REF_TIME) * 1000000, - displayMessage: () => faker.lorem.sentence(), + // TODO: I think this is probably an archaic property name. See if tests collapse with this removal and decide to remove then. + // displayMessage: () => faker.lorem.sentence(), + message: () => faker.lorem.sentence(), }); diff --git a/ui/tests/acceptance/job-status-panel-test.js b/ui/tests/acceptance/job-status-panel-test.js index e27b3f4fdca..798c5035a33 100644 --- a/ui/tests/acceptance/job-status-panel-test.js +++ b/ui/tests/acceptance/job-status-panel-test.js @@ -2,7 +2,14 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { click, visit, find, triggerEvent } from '@ember/test-helpers'; +import { + click, + visit, + find, + findAll, + fillIn, + triggerEvent, +} from '@ember/test-helpers'; import { setupMirage } from 'ember-cli-mirage/test-support'; import faker from 'nomad-ui/mirage/faker'; @@ -400,4 +407,65 @@ module('Acceptance | job status panel', function (hooks) { 'Summary block has the correct number of grouped running allocs' ); }); + + module('deployment history', function () { + test('Deployment history can be searched', async function (assert) { + faker.seed(1); + + let groupTaskCount = 10; + + let job = server.create('job', { + status: 'running', + datacenters: ['*'], + type: 'service', + resourceSpec: ['M: 256, C: 500'], // a single group + createAllocations: true, + allocStatusDistribution: { + running: 1, + failed: 0, + unknown: 0, + lost: 0, + }, + groupTaskCount, + shallow: true, + activeDeployment: true, + }); + + let state = server.create('task-state'); + state.events = server.schema.taskEvents.where({ taskStateId: state.id }); + + server.schema.allocations + .where({ jobId: job.id }) + .update({ taskStateIds: [state.id] }); + + await visit(`/jobs/${job.id}`); + assert.dom('.job-status-panel').exists(); + + const serverEvents = server.schema.taskEvents.where({ + taskStateId: state.id, + }); + const shownEvents = findAll('.timeline-object'); + const jobAllocations = server.db.allocations.where({ jobId: job.id }); + assert.equal( + shownEvents.length, + serverEvents.length * jobAllocations.length, + 'All events are shown' + ); + + await fillIn( + '[data-test-history-search] input', + serverEvents.models[0].message + ); + assert.equal( + findAll('.timeline-object').length, + jobAllocations.length, + 'Only events matching the search are shown' + ); + + await fillIn('[data-test-history-search] input', 'foo bar baz'); + assert + .dom('[data-test-history-search-no-match]') + .exists('No match message is shown'); + }); + }); }); From a3a9f70446398552c52d669fb9475d1dc34ee177 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 22 Mar 2023 13:11:52 -0400 Subject: [PATCH 4/5] Fixing up some old mirage conventions --- ui/app/components/job-status/deployment-history.js | 4 ++-- ui/mirage/factories/allocation.js | 2 +- ui/mirage/factories/task-event.js | 2 -- ui/tests/acceptance/allocation-detail-test.js | 2 +- ui/tests/acceptance/job-status-panel-test.js | 8 +++++--- ui/tests/acceptance/task-detail-test.js | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/app/components/job-status/deployment-history.js b/ui/app/components/job-status/deployment-history.js index 44849c710a1..61d76b2bab7 100644 --- a/ui/app/components/job-status/deployment-history.js +++ b/ui/app/components/job-status/deployment-history.js @@ -55,7 +55,7 @@ export default class JobStatusDeploymentHistoryComponent extends Component { .flat() ) .flat() - .filter((a) => this.filterOnSearchTerm(a)) + .filter((a) => this.containsSearchTerm(a)) .sort((a, b) => a.get('time') - b.get('time')) .reverse(); } catch (e) { @@ -84,7 +84,7 @@ export default class JobStatusDeploymentHistoryComponent extends Component { * @param { import('../../models/task-event').default } taskEvent * @returns { boolean } */ - filterOnSearchTerm(taskEvent) { + containsSearchTerm(taskEvent) { return ( taskEvent.message.toLowerCase().includes(this.searchTerm.toLowerCase()) || taskEvent.type.toLowerCase().includes(this.searchTerm.toLowerCase()) || diff --git a/ui/mirage/factories/allocation.js b/ui/mirage/factories/allocation.js index 6822419d138..724faddfe77 100644 --- a/ui/mirage/factories/allocation.js +++ b/ui/mirage/factories/allocation.js @@ -13,7 +13,7 @@ const REF_TIME = new Date(); export default Factory.extend({ id: (i) => (i >= 100 ? `${UUIDS[i % 100]}-${i}` : UUIDS[i]), - jobVersion: 0, + jobVersion: 1, modifyIndex: () => faker.random.number({ min: 10, max: 2000 }), modifyTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000, diff --git a/ui/mirage/factories/task-event.js b/ui/mirage/factories/task-event.js index 961828741b0..bb426cec56a 100644 --- a/ui/mirage/factories/task-event.js +++ b/ui/mirage/factories/task-event.js @@ -12,7 +12,5 @@ export default Factory.extend({ exitCode: () => null, time: () => faker.date.past(2 / 365, REF_TIME) * 1000000, - // TODO: I think this is probably an archaic property name. See if tests collapse with this removal and decide to remove then. - // displayMessage: () => faker.lorem.sentence(), message: () => faker.lorem.sentence(), }); diff --git a/ui/tests/acceptance/allocation-detail-test.js b/ui/tests/acceptance/allocation-detail-test.js index cf31ddccbcd..564c8cb3eac 100644 --- a/ui/tests/acceptance/allocation-detail-test.js +++ b/ui/tests/acceptance/allocation-detail-test.js @@ -211,7 +211,7 @@ module('Acceptance | allocation detail', function (hooks) { assert.equal(taskRow.name, task.name, 'Name'); assert.equal(taskRow.state, task.state, 'State'); - assert.equal(taskRow.message, event.displayMessage, 'Event Message'); + assert.equal(taskRow.message, event.message, 'Event Message'); assert.equal( taskRow.time, moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"), diff --git a/ui/tests/acceptance/job-status-panel-test.js b/ui/tests/acceptance/job-status-panel-test.js index 798c5035a33..9fa1b94a7d3 100644 --- a/ui/tests/acceptance/job-status-panel-test.js +++ b/ui/tests/acceptance/job-status-panel-test.js @@ -429,14 +429,16 @@ module('Acceptance | job status panel', function (hooks) { groupTaskCount, shallow: true, activeDeployment: true, + version: 0, }); let state = server.create('task-state'); state.events = server.schema.taskEvents.where({ taskStateId: state.id }); - server.schema.allocations - .where({ jobId: job.id }) - .update({ taskStateIds: [state.id] }); + server.schema.allocations.where({ jobId: job.id }).update({ + taskStateIds: [state.id], + jobVersion: 0, + }); await visit(`/jobs/${job.id}`); assert.dom('.job-status-panel').exists(); diff --git a/ui/tests/acceptance/task-detail-test.js b/ui/tests/acceptance/task-detail-test.js index 6e4a22d09ba..b9418fc8617 100644 --- a/ui/tests/acceptance/task-detail-test.js +++ b/ui/tests/acceptance/task-detail-test.js @@ -217,7 +217,7 @@ module('Acceptance | task detail', function (hooks) { 'Event timestamp' ); assert.equal(recentEvent.type, event.type, 'Event type'); - assert.equal(recentEvent.message, event.displayMessage, 'Event message'); + assert.equal(recentEvent.message, event.message, 'Event message'); }); test('when the allocation is not found, the application errors', async function (assert) { From ca9e90a9f4b9539e074d88d36cd1b916f6278298 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Wed, 22 Mar 2023 15:02:59 -0400 Subject: [PATCH 5/5] some a11y rule override to account for scss keyframes --- .../integration/components/job-page/service-test.js | 12 ++++++++++-- .../integration/components/job-status-panel-test.js | 7 +++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ui/tests/integration/components/job-page/service-test.js b/ui/tests/integration/components/job-page/service-test.js index 03edf640238..f90709e5b92 100644 --- a/ui/tests/integration/components/job-page/service-test.js +++ b/ui/tests/integration/components/job-page/service-test.js @@ -272,7 +272,11 @@ module('Integration | Component | job-page/service', function (hooks) { 'The error message mentions ACLs' ); - await componentA11yAudit(this.element, assert); + await componentA11yAudit( + this.element, + assert, + 'scrollable-region-focusable' + ); //keyframe animation fades from opacity 0 await click('[data-test-job-error-close]'); @@ -335,7 +339,11 @@ module('Integration | Component | job-page/service', function (hooks) { 'The error message mentions ACLs' ); - await componentA11yAudit(this.element, assert); + await componentA11yAudit( + this.element, + assert, + 'scrollable-region-focusable' + ); //keyframe animation fades from opacity 0 await click('[data-test-job-error-close]'); diff --git a/ui/tests/integration/components/job-status-panel-test.js b/ui/tests/integration/components/job-status-panel-test.js index f321083d1ba..da930f277c9 100644 --- a/ui/tests/integration/components/job-status-panel-test.js +++ b/ui/tests/integration/components/job-status-panel-test.js @@ -111,8 +111,11 @@ module( // deployment.get('statusDescription'), // 'Status description is in the metrics block' // ); - - await componentA11yAudit(this.element, assert); + await componentA11yAudit( + this.element, + assert, + 'scrollable-region-focusable' + ); //keyframe animation fades from opacity 0 }); test('when there is no running deployment, the latest deployment section shows up for the last deployment', async function (assert) {