From acd5687e7e3e52a4f43ebc4440e7dbc070b32c24 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Mon, 25 Sep 2017 18:13:58 -0700 Subject: [PATCH 1/4] Demonstrate link in action table row bug --- ui/tests/acceptance/task-group-detail-test.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js index 41b03aa1d..ad29f7b43 100644 --- a/ui/tests/acceptance/task-group-detail-test.js +++ b/ui/tests/acceptance/task-group-detail-test.js @@ -10,6 +10,7 @@ const sum = (total, n) => total + n; moduleForAcceptance('Acceptance | task group detail', { beforeEach() { + server.create('agent'); server.create('node', 'forceIPv4'); job = server.create('job', { @@ -158,8 +159,14 @@ test('each allocation should show basic information about the allocation', funct .text() .trim(), server.db.nodes.find(allocation.nodeId).id.split('-')[0], - 'Node name' + 'Node ID' ); + + click(allocationRow.find('td:eq(3) a')); + + andThen(() => { + assert.equal(currentURL(), `/nodes/${allocation.nodeId}`, 'Node links to node page'); + }); }); test('each allocation should show stats about the allocation, retrieved directly from the node', function( From 819baa5deb9b16ba88ca4983ec18e1188f0775c5 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Mon, 25 Sep 2017 18:32:18 -0700 Subject: [PATCH 2/4] Switch to native dom helpers --- ui/package.json | 1 + ui/yarn.lock | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/ui/package.json b/ui/package.json index c4f37ed75..66840e74f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -59,6 +59,7 @@ "ember-href-to": "^1.13.0", "ember-load-initializers": "^1.0.0", "ember-moment": "^7.3.1", + "ember-native-dom-helpers": "^0.5.4", "ember-resolver": "^4.0.0", "ember-sinon": "^0.7.0", "ember-source": "~2.14.0", diff --git a/ui/yarn.lock b/ui/yarn.lock index 84ee0a99e..cb724121b 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -3103,6 +3103,13 @@ ember-moment@^7.3.1: ember-getowner-polyfill "^2.0.1" ember-macro-helpers "^0.15.1" +ember-native-dom-helpers@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.5.4.tgz#0bc1506a643fb7adc0abf1d09c44a7914459296b" + dependencies: + broccoli-funnel "^1.1.0" + ember-cli-babel "^6.6.0" + ember-qunit@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/ember-qunit/-/ember-qunit-2.2.0.tgz#3cdf400031c93a38de781a7304819738753b7f99" From 819fceeac501e4139ab76c904e7c078b04ce17c4 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Tue, 26 Sep 2017 00:47:16 -0700 Subject: [PATCH 3/4] Migrate tests over to native helpers as much as possible --- ui/app/templates/nodes/node.hbs | 2 +- ui/tests/acceptance/allocation-detail-test.js | 45 ++++--- ui/tests/acceptance/client-detail-test.js | 41 +++---- ui/tests/acceptance/job-definition-test.js | 5 +- ui/tests/acceptance/job-deployments-test.js | 59 +++++---- ui/tests/acceptance/job-detail-test.js | 114 ++++++++---------- ui/tests/acceptance/job-versions-test.js | 8 +- ui/tests/acceptance/jobs-list-test.js | 24 +++- ui/tests/acceptance/nodes-list-test.js | 26 ++-- ui/tests/acceptance/server-detail-test.js | 14 ++- ui/tests/acceptance/task-group-detail-test.js | 34 ++++-- ui/tests/acceptance/token-test.js | 13 +- ui/tests/integration/attributes-table-test.js | 29 ++++- ui/tests/integration/job-diff-test.js | 41 ++++--- ui/tests/integration/list-pagination-test.js | 55 +++++---- ui/tests/integration/list-table-test.js | 58 +++++---- 16 files changed, 319 insertions(+), 249 deletions(-) diff --git a/ui/app/templates/nodes/node.hbs b/ui/app/templates/nodes/node.hbs index d44a40eab..0fb81d11e 100644 --- a/ui/app/templates/nodes/node.hbs +++ b/ui/app/templates/nodes/node.hbs @@ -69,7 +69,7 @@ Attributes
- {{attributes-table attributes=model.attributes.attributesStructured}} + {{attributes-table attributes=model.attributes.attributesStructured class="attributes-table"}}
diff --git a/ui/tests/acceptance/allocation-detail-test.js b/ui/tests/acceptance/allocation-detail-test.js index 70d567262..51997cef6 100644 --- a/ui/tests/acceptance/allocation-detail-test.js +++ b/ui/tests/acceptance/allocation-detail-test.js @@ -1,7 +1,11 @@ +import Ember from 'ember'; +import { click, findAll, currentURL, find, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; import moment from 'moment'; +const { $ } = Ember; + let job; let node; let allocation; @@ -23,32 +27,27 @@ moduleForAcceptance('Acceptance | allocation detail', { test('/allocation/:id should name the allocation and link to the corresponding job and node', function( assert ) { + assert.ok(find('h1').textContent.includes(allocation.name), 'Allocation name is in the heading'); + assert.ok(find('h3').textContent.includes(job.name), 'Job name is in the subheading'); assert.ok( - find('h1') - .text() - .includes(allocation.name), - 'Allocation name is in the heading' - ); - assert.ok( - find('h3') - .text() - .includes(job.name), - 'Job name is in the subheading' - ); - assert.ok( - find('h3') - .text() - .includes(node.id.split('-')[0]), + find('h3').textContent.includes(node.id.split('-')[0]), 'Node short id is in the subheading' ); - click('h3 a:eq(0)'); + andThen(() => { + click(findAll('h3 a')[0]); + }); + andThen(() => { assert.equal(currentURL(), `/jobs/${job.id}`, 'Job link navigates to the job'); }); visit(`/allocations/${allocation.id}`); - click('h3 a:eq(1)'); + + andThen(() => { + click(findAll('h3 a')[1]); + }); + andThen(() => { assert.equal(currentURL(), `/nodes/${node.id}`, 'Node link navigates to the node'); }); @@ -56,7 +55,7 @@ test('/allocation/:id should name the allocation and link to the corresponding j test('/allocation/:id should list all tasks for the allocation', function(assert) { assert.equal( - find('.tasks tbody tr').length, + findAll('.tasks tbody tr').length, server.db.taskStates.where({ allocationId: allocation.id }).length, 'Table lists all tasks' ); @@ -68,7 +67,7 @@ test('each task row should list high-level information for the task', function(a .map(id => server.db.taskResources.find(id)) .sortBy('name')[0]; const reservedPorts = taskResources.resources.Networks[0].ReservedPorts; - const taskRow = find('.tasks tbody tr:eq(0)'); + const taskRow = $(findAll('.tasks tbody tr')[0]); const events = server.db.taskEvents.where({ taskStateId: task.id }); const event = events[events.length - 1]; @@ -117,7 +116,7 @@ test('each task row should list high-level information for the task', function(a test('/allocation/:id should list recent events for each task', function(assert) { const tasks = server.db.taskStates.where({ allocationId: allocation.id }); assert.equal( - find('.task-state-events').length, + findAll('.task-state-events').length, tasks.length, 'A task state event block per task' ); @@ -127,7 +126,7 @@ test('each recent events list should include the name, state, and time info for assert ) { const task = server.db.taskStates.where({ allocationId: allocation.id })[0]; - const recentEventsSection = find('.task-state-events:eq(0)'); + const recentEventsSection = $(findAll('.task-state-events')[0]); const heading = recentEventsSection .find('.message-header') .text() @@ -146,7 +145,7 @@ test('each recent events list should list all recent events for the task', funct const events = server.db.taskEvents.where({ taskStateId: task.id }); assert.equal( - find('.task-state-events:eq(0) .task-events tbody tr').length, + findAll('.task-state-events')[0].querySelectorAll('.task-events tbody tr').length, events.length, `Lists ${events.length} events` ); @@ -157,7 +156,7 @@ test('each recent event should list the time, type, and description of the event ) { const task = server.db.taskStates.where({ allocationId: allocation.id })[0]; const event = server.db.taskEvents.where({ taskStateId: task.id })[0]; - const recentEvent = find('.task-state-events:eq(0) .task-events tbody tr:last'); + const recentEvent = $('.task-state-events:eq(0) .task-events tbody tr:last'); assert.equal( recentEvent diff --git a/ui/tests/acceptance/client-detail-test.js b/ui/tests/acceptance/client-detail-test.js index 521f6757c..78a8e66f4 100644 --- a/ui/tests/acceptance/client-detail-test.js +++ b/ui/tests/acceptance/client-detail-test.js @@ -1,6 +1,10 @@ +import Ember from 'ember'; +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'; +const { $ } = Ember; + let node; moduleForAcceptance('Acceptance | client detail', { @@ -18,52 +22,45 @@ moduleForAcceptance('Acceptance | client detail', { }); test('/nodes/:id should have a breadrcumb trail linking back to nodes', function(assert) { - assert.equal(find('.breadcrumb:eq(0)').text(), 'Nodes', 'First breadcrumb says nodes'); + assert.equal(findAll('.breadcrumb')[0].textContent, 'Nodes', 'First breadcrumb says nodes'); assert.equal( - find('.breadcrumb:eq(1)').text(), + findAll('.breadcrumb')[1].textContent, node.id.split('-')[0], 'Second breadcrumb says the node short id' ); - click('.breadcrumb:eq(0)'); + click(findAll('.breadcrumb')[0]); andThen(() => { assert.equal(currentURL(), '/nodes', 'First breadcrumb links back to nodes'); }); }); test('/nodes/:id should list immediate details for the node in the title', function(assert) { + assert.ok(find('.title').textContent.includes(node.name), 'Title includes name'); + assert.ok(find('.title').textContent.includes(node.id), 'Title includes id'); assert.ok( - find('.title') - .text() - .includes(node.name), - 'Title includes name' + findAll(`.title .node-status-light.${node.status}`).length, + 'Title includes status light' ); - assert.ok( - find('.title') - .text() - .includes(node.id), - 'Title includes id' - ); - assert.ok(find(`.title .node-status-light.${node.status}`).length, 'Title includes status light'); }); test('/nodes/:id should list additional detail for the node below the title', function(assert) { assert.equal( - find('.inline-definitions .pair:eq(0)').text(), + findAll('.inline-definitions .pair')[0].textContent, `Status ${node.status}`, 'Status is in additional details' ); assert.ok( - find('.inline-definitions .pair:eq(0) .status-text').hasClass(`node-${node.status}`), + $('.inline-definitions .pair:eq(0) .status-text').hasClass(`node-${node.status}`), 'Status is decorated with a status class' ); assert.equal( - find('.inline-definitions .pair:eq(1)').text(), + findAll('.inline-definitions .pair')[1].textContent, `Address ${node.httpAddr}`, 'Address is in additional detals' ); assert.equal( - find('.inline-definitions .pair:eq(2)').text(), + findAll('.inline-definitions .pair')[2].textContent, `Datacenter ${node.datacenter}`, 'Datacenter is in additional details' ); @@ -72,14 +69,14 @@ test('/nodes/:id should list additional detail for the node below the title', fu test('/nodes/:id should list all allocations on the node', function(assert) { const allocationsCount = server.db.allocations.where({ nodeId: node.id }).length; assert.equal( - find('.allocations tbody tr').length, + findAll('.allocations tbody tr').length, allocationsCount, `Allocations table lists all ${allocationsCount} associated allocations` ); }); test('each allocation should have high-level details for the allocation', function(assert) { - const allocationRow = find('.allocations tbody tr:eq(0)'); + const allocationRow = $(findAll('.allocations tbody tr')[0]); const allocation = server.db.allocations .where({ nodeId: node.id }) .sortBy('modifyIndex') @@ -156,7 +153,7 @@ test('each allocation should link to the allocation detail page', function(asser .sortBy('modifyIndex') .reverse()[0]; - click('.allocations tbody tr:eq(0) td:eq(0) a'); + click($('.allocations tbody tr:eq(0) td:eq(0) a').get(0)); andThen(() => { assert.equal( @@ -170,7 +167,7 @@ test('each allocation should link to the allocation detail page', function(asser test('each allocation should link to the job the allocation belongs to', function(assert) { const allocation = server.db.allocations.where({ nodeId: node.id })[0]; const job = server.db.jobs.find(allocation.jobId); - click('.allocations tbody tr:eq(0) td:eq(3) a'); + click($('.allocations tbody tr:eq(0) td:eq(3) a').get(0)); andThen(() => { assert.equal( diff --git a/ui/tests/acceptance/job-definition-test.js b/ui/tests/acceptance/job-definition-test.js index 77496f905..f097ba024 100644 --- a/ui/tests/acceptance/job-definition-test.js +++ b/ui/tests/acceptance/job-definition-test.js @@ -1,9 +1,10 @@ +import { findAll, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; let job; -moduleForAcceptance('Acceptance | job detail', { +moduleForAcceptance('Acceptance | job definition', { beforeEach() { server.create('node'); server.create('job'); @@ -17,7 +18,7 @@ test('visiting /jobs/:job_id/definition', function(assert) { }); test('the job definition page contains a json viewer component', function(assert) { - assert.ok(find('.json-viewer').length, 'JSON viewer found'); + assert.ok(findAll('.json-viewer').length, 'JSON viewer found'); }); test('the job definition page requests the job to display in an unmutated form', function(assert) { diff --git a/ui/tests/acceptance/job-deployments-test.js b/ui/tests/acceptance/job-deployments-test.js index 8e46e889b..f69bebea0 100644 --- a/ui/tests/acceptance/job-deployments-test.js +++ b/ui/tests/acceptance/job-deployments-test.js @@ -1,9 +1,10 @@ +import { click, findAll, find, visit } from 'ember-native-dom-helpers'; import Ember from 'ember'; import { test } from 'qunit'; import moment from 'moment'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; -const { get } = Ember; +const { get, $ } = Ember; const sum = (list, key) => list.reduce((sum, item) => sum + get(item, key), 0); let job; @@ -32,7 +33,7 @@ test('/jobs/:id/deployments should list all job deployments', function(assert) { visit(`/jobs/${job.id}/deployments`); andThen(() => { assert.ok( - find('.timeline-object').length, + findAll('.timeline-object').length, deployments.length, 'Each deployment gets a row in the timeline' ); @@ -50,7 +51,7 @@ test('each deployment mentions the deployment shortId, status, version, and time jobId: deployment.jobId, version: deployment.versionNumber, }); - const deploymentRow = find('.timeline-object:eq(0)'); + const deploymentRow = $(findAll('.timeline-object')[0]); assert.ok(deploymentRow.text().includes(deployment.id.split('-')[0]), 'Short ID'); assert.equal(deploymentRow.find('.tag').text(), deployment.status, 'Status'); @@ -89,7 +90,7 @@ test('when the deployment is running and needs promotion, the deployment item sa visit(`/jobs/${job.id}/deployments`); andThen(() => { - const deploymentRow = find('.timeline-object:eq(0)'); + const deploymentRow = $(findAll('.timeline-object')[0]); assert.ok( deploymentRow.find('.badge:contains("Requires Promotion")').length, 'Requires Promotion badge found' @@ -103,11 +104,11 @@ test('each deployment item can be opened to show details', function(assert) { visit(`/jobs/${job.id}/deployments`); andThen(() => { - deploymentRow = find('.timeline-object:eq(0)'); + deploymentRow = $(findAll('.timeline-object')[0]); assert.ok(deploymentRow.find('.boxed-section-body').length === 0, 'No deployment body'); - click(deploymentRow.find('button')); + click(deploymentRow.find('button').get(0)); andThen(() => { assert.ok(deploymentRow.find('.boxed-section-body').length, 'Deployment body found'); @@ -120,18 +121,18 @@ test('when open, a deployment shows the deployment metrics', function(assert) { andThen(() => { const deployment = sortedDeployments.models[0]; - const deploymentRow = find('.timeline-object:eq(0)'); + const deploymentRow = $(findAll('.timeline-object')[0]); const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => server.db.deploymentTaskGroupSummaries.find(id) ); - click(deploymentRow.find('button')); + click(deploymentRow.find('button').get(0)); andThen(() => { assert.equal( - find('.deployment-metrics .label:contains("Canaries") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Canaries") + .value') + .get(0) + .textContent.trim(), `${sum(taskGroupSummaries, 'placedCanaries')} / ${sum( taskGroupSummaries, 'desiredCanaries' @@ -140,41 +141,39 @@ test('when open, a deployment shows the deployment metrics', function(assert) { ); assert.equal( - find('.deployment-metrics .label:contains("Placed") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Placed") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'placedAllocs'), 'Placed allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Desired") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Desired") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'desiredTotal'), 'Desired allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Healthy") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Healthy") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'healthyAllocs'), 'Healthy allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Unhealthy") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Unhealthy") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'unhealthyAllocs'), 'Unhealthy allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .notification') - .text() - .trim(), + find('.deployment-metrics .notification').textContent.trim(), deployment.statusDescription, 'Status description is in the metrics block' ); @@ -189,12 +188,12 @@ test('when open, a deployment shows a list of all task groups and their respecti andThen(() => { const deployment = sortedDeployments.models[0]; - const deploymentRow = find('.timeline-object:eq(0)'); + const deploymentRow = $(findAll('.timeline-object')[0]); const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => server.db.deploymentTaskGroupSummaries.find(id) ); - click(deploymentRow.find('button')); + click(deploymentRow.find('button').get(0)); andThen(() => { assert.ok( @@ -282,13 +281,13 @@ test('when open, a deployment shows a list of all allocations for the deployment andThen(() => { const deployment = sortedDeployments.models[0]; - const deploymentRow = find('.timeline-object:eq(0)'); + const deploymentRow = $(findAll('.timeline-object')[0]); // TODO: Make this less brittle. This logic is copied from the mirage config, // since there is no reference to allocations on the deployment model. const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3); - click(deploymentRow.find('button')); + click(deploymentRow.find('button').get(0)); andThen(() => { assert.ok( diff --git a/ui/tests/acceptance/job-detail-test.js b/ui/tests/acceptance/job-detail-test.js index e9abf8340..ba73c7d10 100644 --- a/ui/tests/acceptance/job-detail-test.js +++ b/ui/tests/acceptance/job-detail-test.js @@ -1,9 +1,10 @@ +import { click, findAll, currentURL, find, visit } from 'ember-native-dom-helpers'; import Ember from 'ember'; import moment from 'moment'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; -const { get } = Ember; +const { get, $ } = Ember; const sum = (list, key) => list.reduce((sum, item) => sum + get(item, key), 0); let job; @@ -22,39 +23,28 @@ test('visiting /jobs/:job_id', function(assert) { }); test('breadcrumbs includes job name and link back to the jobs list', function(assert) { - assert.equal(find('.breadcrumb:eq(0)').text(), 'Jobs', 'First breadcrumb says jobs'); - assert.equal(find('.breadcrumb:eq(1)').text(), job.name, 'Second breadcrumb says the job name'); + assert.equal(findAll('.breadcrumb')[0].textContent, 'Jobs', 'First breadcrumb says jobs'); + assert.equal( + findAll('.breadcrumb')[1].textContent, + job.name, + 'Second breadcrumb says the job name' + ); - click('.breadcrumb:eq(0)'); + click(findAll('.breadcrumb')[0]); andThen(() => { assert.equal(currentURL(), '/jobs', 'First breadcrumb links back to jobs'); }); }); test('the job detail page should contain basic information about the job', function(assert) { - assert.ok( - find('.title .tag:eq(0)') - .text() - .includes(job.status), - 'Status' - ); - assert.ok( - find('.job-stats span:eq(0)') - .text() - .includes(job.type), - 'Type' - ); - assert.ok( - find('.job-stats span:eq(1)') - .text() - .includes(job.priority), - 'Priority' - ); + assert.ok(findAll('.title .tag')[0].textContent.includes(job.status), 'Status'); + assert.ok(findAll('.job-stats span')[0].textContent.includes(job.type), 'Type'); + assert.ok(findAll('.job-stats span')[1].textContent.includes(job.priority), 'Priority'); }); test('the job detail page should list all task groups', function(assert) { assert.equal( - find('.task-group-row').length, + findAll('.task-group-row').length, server.db.taskGroups.where({ jobId: job.id }).length ); }); @@ -63,7 +53,7 @@ test('each row in the task group table should show basic information about the t assert ) { const taskGroup = job.taskGroupIds.map(id => server.db.taskGroups.find(id)).sortBy('name')[0]; - const taskGroupRow = find('.task-group-row:eq(0)'); + const taskGroupRow = $(findAll('.task-group-row')[0]); const tasks = server.db.tasks.where({ taskGroupId: taskGroup.id }); const sum = (list, key) => list.reduce((sum, item) => sum + get(item, key), 0); @@ -104,37 +94,37 @@ test('the allocations diagram lists all allocation status figures', function(ass ); assert.equal( - legend.find('li.queued .value').text(), + legend.querySelector('li.queued .value').textContent, statusCounts.queued, `${statusCounts.queued} are queued` ); assert.equal( - legend.find('li.starting .value').text(), + legend.querySelector('li.starting .value').textContent, statusCounts.starting, `${statusCounts.starting} are starting` ); assert.equal( - legend.find('li.running .value').text(), + legend.querySelector('li.running .value').textContent, statusCounts.running, `${statusCounts.running} are running` ); assert.equal( - legend.find('li.complete .value').text(), + legend.querySelector('li.complete .value').textContent, statusCounts.complete, `${statusCounts.complete} are complete` ); assert.equal( - legend.find('li.failed .value').text(), + legend.querySelector('li.failed .value').textContent, statusCounts.failed, `${statusCounts.failed} are failed` ); assert.equal( - legend.find('li.lost .value').text(), + legend.querySelector('li.lost .value').textContent, statusCounts.lost, `${statusCounts.lost} are lost` ); @@ -149,7 +139,7 @@ test('there is no active deployment section when the job has no active deploymen visit(`/jobs/${job.id}`); andThen(() => { - assert.ok(find('.active-deployment').length === 0, 'No active deployment'); + assert.ok(findAll('.active-deployment').length === 0, 'No active deployment'); }); }); @@ -168,27 +158,27 @@ test('the active deployment section shows up for the currently running deploymen visit(`/jobs/${job.id}`); andThen(() => { - assert.ok(find('.active-deployment').length === 1, 'Active deployment'); + assert.ok(findAll('.active-deployment').length === 1, 'Active deployment'); assert.equal( - find('.active-deployment > .boxed-section-head .badge') - .text() - .trim(), + $('.active-deployment > .boxed-section-head .badge') + .get(0) + .textContent.trim(), deployment.id.split('-')[0], 'The active deployment is the most recent running deployment' ); assert.equal( - find('.active-deployment > .boxed-section-head .submit-time') - .text() - .trim(), + $('.active-deployment > .boxed-section-head .submit-time') + .get(0) + .textContent.trim(), moment(version.submitTime / 1000000).fromNow(), 'Time since the job was submitted is in the active deployment header' ); assert.equal( - find('.deployment-metrics .label:contains("Canaries") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Canaries") + .value') + .get(0) + .textContent.trim(), `${sum(taskGroupSummaries, 'placedCanaries')} / ${sum( taskGroupSummaries, 'desiredCanaries' @@ -197,41 +187,41 @@ test('the active deployment section shows up for the currently running deploymen ); assert.equal( - find('.deployment-metrics .label:contains("Placed") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Placed") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'placedAllocs'), 'Placed allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Desired") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Desired") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'desiredTotal'), 'Desired allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Healthy") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Healthy") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'healthyAllocs'), 'Healthy allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .label:contains("Unhealthy") + .value') - .text() - .trim(), + $('.deployment-metrics .label:contains("Unhealthy") + .value') + .get(0) + .textContent.trim(), sum(taskGroupSummaries, 'unhealthyAllocs'), 'Unhealthy allocs aggregates across task groups' ); assert.equal( - find('.deployment-metrics .notification') - .text() - .trim(), + $('.deployment-metrics .notification') + .get(0) + .textContent.trim(), deployment.statusDescription, 'Status description is in the metrics block' ); @@ -246,24 +236,26 @@ test('the active deployment section can be expanded to show task groups and allo andThen(() => { assert.ok( - find('.active-deployment .boxed-section-head:contains("Task Groups")').length === 0, + $('.active-deployment .boxed-section-head:contains("Task Groups")').length === 0, 'Task groups not found' ); assert.ok( - find('.active-deployment .boxed-section-head:contains("Allocations")').length === 0, + $('.active-deployment .boxed-section-head:contains("Allocations")').length === 0, 'Allocations not found' ); }); - click('.active-deployment-details-toggle'); + andThen(() => { + click('.active-deployment-details-toggle'); + }); andThen(() => { assert.ok( - find('.active-deployment .boxed-section-head:contains("Task Groups")').length === 1, + $('.active-deployment .boxed-section-head:contains("Task Groups")').length === 1, 'Task groups found' ); assert.ok( - find('.active-deployment .boxed-section-head:contains("Allocations")').length === 1, + $('.active-deployment .boxed-section-head:contains("Allocations")').length === 1, 'Allocations found' ); }); diff --git a/ui/tests/acceptance/job-versions-test.js b/ui/tests/acceptance/job-versions-test.js index 0843243ea..578eb28bf 100644 --- a/ui/tests/acceptance/job-versions-test.js +++ b/ui/tests/acceptance/job-versions-test.js @@ -1,7 +1,11 @@ +import Ember from 'ember'; +import { findAll, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; import moment from 'moment'; +const { $ } = Ember; + let job; let versions; @@ -16,7 +20,7 @@ moduleForAcceptance('Acceptance | job versions', { test('/jobs/:id/versions should list all job versions', function(assert) { assert.ok( - find('.timeline-object').length, + findAll('.timeline-object').length, versions.length, 'Each version gets a row in the timeline' ); @@ -26,7 +30,7 @@ test('each version mentions the version number, the stability, and the submitted assert ) { const version = versions.sortBy('submitTime').reverse()[0]; - const versionRow = find('.timeline-object:eq(0)'); + const versionRow = $(findAll('.timeline-object')[0]); assert.ok(versionRow.text().includes(`Version #${version.version}`), 'Version #'); assert.equal( diff --git a/ui/tests/acceptance/jobs-list-test.js b/ui/tests/acceptance/jobs-list-test.js index 61f9a070a..699739fe4 100644 --- a/ui/tests/acceptance/jobs-list-test.js +++ b/ui/tests/acceptance/jobs-list-test.js @@ -1,6 +1,10 @@ +import Ember from 'ember'; +import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; +const { $ } = Ember; + moduleForAcceptance('Acceptance | jobs list', { beforeEach() { // Required for placing allocations (a result of creating jobs) @@ -25,10 +29,10 @@ test('/jobs should list the first page of jobs sorted by modify index', function andThen(() => { const sortedJobs = server.db.jobs.sortBy('modifyIndex').reverse(); - assert.equal(find('.job-row').length, pageSize); + assert.equal(findAll('.job-row').length, pageSize); for (var jobNumber = 0; jobNumber < pageSize; jobNumber++) { assert.equal( - find(`.job-row:eq(${jobNumber}) td:eq(0)`).text(), + $(`.job-row:eq(${jobNumber}) td:eq(0)`).text(), sortedJobs[jobNumber].name, 'Jobs are ordered' ); @@ -44,11 +48,18 @@ test('each job row should contain information about the job', function(assert) { visit('/jobs'); andThen(() => { - const jobRow = find('.job-row:eq(0)'); + const jobRow = $(findAll('.job-row')[0]); assert.equal(jobRow.find('td:eq(0)').text(), job.name, 'Name'); assert.equal(jobRow.find('td:eq(0) a').attr('href'), `/ui/jobs/${job.id}`, 'Detail Link'); - assert.equal(jobRow.find('td:eq(1)').text().trim(), job.status, 'Status'); + assert.equal( + jobRow + .find('td:eq(1)') + .text() + .trim(), + job.status, + 'Status' + ); assert.equal(jobRow.find('td:eq(2)').text(), job.type, 'Type'); assert.equal(jobRow.find('td:eq(3)').text(), job.priority, 'Priority'); assert.equal(jobRow.find('td:eq(4)').text(), taskGroups.length, '# Groups'); @@ -60,7 +71,10 @@ test('each job row should link to the corresponding job', function(assert) { const job = server.db.jobs[0]; visit('/jobs'); - click('.job-row:eq(0) td:eq(0) a'); + + andThen(() => { + click($('.job-row:eq(0) td:eq(0) a').get(0)); + }); andThen(() => { assert.equal(currentURL(), `/jobs/${job.id}`); diff --git a/ui/tests/acceptance/nodes-list-test.js b/ui/tests/acceptance/nodes-list-test.js index 2b506aab6..4a6ee0301 100644 --- a/ui/tests/acceptance/nodes-list-test.js +++ b/ui/tests/acceptance/nodes-list-test.js @@ -1,8 +1,12 @@ +import Ember from 'ember'; +import { click, 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'; import ipParts from 'nomad-ui/utils/ip-parts'; +const { $ } = Ember; + function minimumSetup() { server.createList('node', 1); server.createList('agent', 1); @@ -21,14 +25,14 @@ test('/nodes should list one page of clients', function(assert) { visit('/nodes'); andThen(() => { - assert.equal(find('.client-node-row').length, pageSize); - assert.ok(find('.pagination').length, 'Pagination found on the page'); + assert.equal(findAll('.client-node-row').length, pageSize); + assert.ok(findAll('.pagination').length, 'Pagination found on the page'); const sortedNodes = server.db.nodes.sortBy('modifyIndex').reverse(); for (var nodeNumber = 0; nodeNumber < pageSize; nodeNumber++) { assert.equal( - find(`.client-node-row:eq(${nodeNumber}) td:eq(0)`).text(), + $(`.client-node-row:eq(${nodeNumber}) td:eq(0)`).text(), sortedNodes[nodeNumber].id.split('-')[0], 'Nodes are ordered' ); @@ -43,7 +47,7 @@ test('each client record should show high-level info of the client', function(as visit('/nodes'); andThen(() => { - const nodeRow = find('.client-node-row:eq(0)'); + const nodeRow = $(findAll('.client-node-row')[0]); const allocations = server.db.allocations.where({ nodeId: node.id }); const { address, port } = ipParts(node.httpAddr); @@ -62,7 +66,9 @@ test('each client should link to the client detail page', function(assert) { const node = server.db.nodes[0]; visit('/nodes'); - click('.client-node-row:eq(0)'); + andThen(() => { + click(findAll('.client-node-row')[0]); + }); andThen(() => { assert.equal(currentURL(), `/nodes/${node.id}`); @@ -81,7 +87,7 @@ test('/servers should list all servers', function(assert) { visit('/servers'); andThen(() => { - assert.equal(find('.server-agent-row').length, pageSize); + assert.equal(findAll('.server-agent-row').length, pageSize); const sortedAgents = server.db.agents .sort((a, b) => { @@ -96,7 +102,7 @@ test('/servers should list all servers', function(assert) { for (var agentNumber = 0; agentNumber < 8; agentNumber++) { assert.equal( - find(`.server-agent-row:eq(${agentNumber}) td:eq(0)`).text(), + $(`.server-agent-row:eq(${agentNumber}) td:eq(0)`).text(), sortedAgents[agentNumber].name, 'Clients are ordered' ); @@ -111,7 +117,7 @@ test('each server should show high-level info of the server', function(assert) { visit('/servers'); andThen(() => { - const agentRow = find('.server-agent-row:eq(0)'); + const agentRow = $(findAll('.server-agent-row')[0]); assert.equal(agentRow.find('td:eq(0)').text(), agent.name, 'Name'); assert.equal(agentRow.find('td:eq(1)').text(), agent.status, 'Status'); @@ -127,7 +133,9 @@ test('each server should link to the server detail page', function(assert) { const agent = server.db.agents[0]; visit('/servers'); - click('.server-agent-row:eq(0)'); + andThen(() => { + click(findAll('.server-agent-row')[0]); + }); andThen(() => { assert.equal(currentURL(), `/servers/${agent.name}`); diff --git a/ui/tests/acceptance/server-detail-test.js b/ui/tests/acceptance/server-detail-test.js index 164a0449a..4b4af6673 100644 --- a/ui/tests/acceptance/server-detail-test.js +++ b/ui/tests/acceptance/server-detail-test.js @@ -1,6 +1,10 @@ +import Ember from 'ember'; +import { findAll, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; +const { $ } = Ember; + let agent; moduleForAcceptance('Acceptance | server detail', { @@ -18,22 +22,22 @@ test('visiting /servers/:server_name', function(assert) { test('the server detail page should list all tags for the server', function(assert) { const tags = agent.tags; - assert.equal(find('.server-tags tbody tr').length, Object.keys(tags).length, '# of tags'); + assert.equal(findAll('.server-tags tbody tr').length, Object.keys(tags).length, '# of tags'); Object.keys(tags).forEach((key, index) => { - const row = find(`.server-tags tbody tr:eq(${index})`); + const row = $(`.server-tags tbody tr:eq(${index})`); assert.equal(row.find('td:eq(0)').text(), key, `Label: ${key}`); assert.equal(row.find('td:eq(1)').text(), tags[key], `Value: ${tags[key]}`); }); }); test('the list of servers from /servers should still be present', function(assert) { - assert.equal(find('.server-agent-row').length, server.db.agents.length, '# of servers'); + assert.equal(findAll('.server-agent-row').length, server.db.agents.length, '# of servers'); }); test('the active server should be denoted in the table', function(assert) { - assert.equal(find('.server-agent-row.is-active').length, 1, 'Only one active server'); + assert.equal(findAll('.server-agent-row.is-active').length, 1, 'Only one active server'); assert.equal( - find('.server-agent-row.is-active td:eq(0)').text(), + findAll('.server-agent-row.is-active td')[0].textContent, agent.name, 'Active server matches current route' ); diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js index ad29f7b43..800e57544 100644 --- a/ui/tests/acceptance/task-group-detail-test.js +++ b/ui/tests/acceptance/task-group-detail-test.js @@ -1,6 +1,10 @@ +import Ember from 'ember'; +import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; +const { $ } = Ember; + let job; let taskGroup; let tasks; @@ -47,39 +51,43 @@ test('/jobs/:id/:task-group should list high-level metrics for the allocation', const totalDisk = taskGroup.ephemeralDisk.SizeMB; assert.equal( - find('.inline-definitions .pair:eq(0)').text(), + findAll('.inline-definitions .pair')[0].textContent, `# Tasks ${tasks.length}`, '# Tasks' ); assert.equal( - find('.inline-definitions .pair:eq(1)').text(), + findAll('.inline-definitions .pair')[1].textContent, `Reserved CPU ${totalCPU} MHz`, 'Aggregated CPU reservation for all tasks' ); assert.equal( - find('.inline-definitions .pair:eq(2)').text(), + findAll('.inline-definitions .pair')[2].textContent, `Reserved Memory ${totalMemory} MiB`, 'Aggregated Memory reservation for all tasks' ); assert.equal( - find('.inline-definitions .pair:eq(3)').text(), + findAll('.inline-definitions .pair')[3].textContent, `Reserved Disk ${totalDisk} MiB`, 'Aggregated Disk reservation for all tasks' ); }); test('/jobs/:id/:task-group should have breadcrumbs for job and jobs', function(assert) { - assert.equal(find('.breadcrumb:eq(0)').text(), 'Jobs', 'First breadcrumb says jobs'); - assert.equal(find('.breadcrumb:eq(1)').text(), job.name, 'Second breadcrumb says the job name'); + assert.equal(findAll('.breadcrumb')[0].textContent, 'Jobs', 'First breadcrumb says jobs'); assert.equal( - find('.breadcrumb:eq(2)').text(), + findAll('.breadcrumb')[1].textContent, + job.name, + 'Second breadcrumb says the job name' + ); + assert.equal( + findAll('.breadcrumb')[2].textContent, taskGroup.name, 'Third breadcrumb says the job name' ); }); test('/jobs/:id/:task-group first breadcrumb should link to jobs', function(assert) { - click('.breadcrumb:eq(0)'); + click(findAll('.breadcrumb')[0]); andThen(() => { assert.equal(currentURL(), '/jobs', 'First breadcrumb links back to jobs'); }); @@ -88,7 +96,7 @@ test('/jobs/:id/:task-group first breadcrumb should link to jobs', function(asse test('/jobs/:id/:task-group second breadcrumb should link to the job for the task group', function( assert ) { - click('.breadcrumb:eq(1)'); + click(findAll('.breadcrumb')[1]); andThen(() => { assert.equal( currentURL(), @@ -118,7 +126,7 @@ test('/jobs/:id/:task-group should list one page of allocations for the task gro ); assert.equal( - find('.allocations tbody tr').length, + findAll('.allocations tbody tr').length, pageSize, 'All allocations for the task group' ); @@ -127,7 +135,7 @@ test('/jobs/:id/:task-group should list one page of allocations for the task gro test('each allocation should show basic information about the allocation', function(assert) { const allocation = allocations.sortBy('name')[0]; - const allocationRow = find('.allocations tbody tr:eq(0)'); + const allocationRow = $(findAll('.allocations tbody tr')[0]); assert.equal( allocationRow @@ -162,7 +170,7 @@ test('each allocation should show basic information about the allocation', funct 'Node ID' ); - click(allocationRow.find('td:eq(3) a')); + click(allocationRow.find('td:eq(3) a').get(0)); andThen(() => { assert.equal(currentURL(), `/nodes/${allocation.nodeId}`, 'Node links to node page'); @@ -173,7 +181,7 @@ test('each allocation should show stats about the allocation, retrieved directly assert ) { const allocation = allocations.sortBy('name')[0]; - const allocationRow = find('.allocations tbody tr:eq(0)'); + const allocationRow = $(findAll('.allocations tbody tr')[0]); const allocStats = server.db.clientAllocationStats.find(allocation.id); const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id)); diff --git a/ui/tests/acceptance/token-test.js b/ui/tests/acceptance/token-test.js index 9c176a4e3..e4c6f2a5d 100644 --- a/ui/tests/acceptance/token-test.js +++ b/ui/tests/acceptance/token-test.js @@ -1,3 +1,4 @@ +import { fillIn, visit } from 'ember-native-dom-helpers'; import { test } from 'qunit'; import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; @@ -22,14 +23,18 @@ test('the token form sets the token in session storage', function(assert) { assert.ok(window.sessionStorage.nomadTokenSecret == null, 'No token secret set'); assert.ok(window.sessionStorage.nomadTokenAccessor == null, 'No token accessor set'); - fillIn('.token-secret', secret); + andThen(() => { + fillIn('.token-secret', secret); + }); andThen(() => { assert.equal(window.sessionStorage.nomadTokenSecret, secret, 'Token secret was set'); assert.ok(window.sessionStorage.nomadTokenAccessor == null, 'Token accessor was not set'); }); - fillIn('.token-accessor', accessor); + andThen(() => { + fillIn('.token-accessor', accessor); + }); andThen(() => { assert.equal(window.sessionStorage.nomadTokenAccessor, accessor, 'Token accessor was set'); @@ -55,7 +60,9 @@ test('the X-Nomad-Token header gets sent with requests once it is set', function }); visit('/settings/tokens'); - fillIn('.token-secret', secret); + andThen(() => { + fillIn('.token-secret', secret); + }); visit(`/jobs/${job.id}`); visit(`/nodes/${node.id}`); diff --git a/ui/tests/integration/attributes-table-test.js b/ui/tests/integration/attributes-table-test.js index 384f7dc92..3147ff638 100644 --- a/ui/tests/integration/attributes-table-test.js +++ b/ui/tests/integration/attributes-table-test.js @@ -1,3 +1,4 @@ +import { findAll } from 'ember-native-dom-helpers'; import { test, moduleForComponent } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; import flat from 'npm:flat'; @@ -42,23 +43,39 @@ test('should render the full path of key/value pair from the root of the object' this.render(hbs`{{attributes-table attributes=attributes}}`); assert.equal( - this.$('tbody tr:eq(0) td:eq(0)').text().trim(), + this.$('tbody tr:eq(0) td') + .get(0) + .textContent.trim(), 'key', 'Simple row renders only the key' ); - assert.equal(this.$('tbody tr:eq(0) td:eq(1)').text().trim(), 'value'); + assert.equal( + this.$('tbody tr:eq(0) td') + .get(1) + .textContent.trim(), + 'value' + ); assert.equal( - this.$('tbody tr:eq(8) td:eq(0)').text().trim(), + this.$('tbody tr:eq(8) td') + .get(0) + .textContent.trim(), 'so.are.deeply.nested', 'Complex row renders the full path to the key' ); assert.equal( - this.$('tbody tr:eq(8) td:eq(0) .is-faded').text().trim(), + this.$('tbody tr:eq(8) td:eq(0) .is-faded') + .get(0) + .textContent.trim(), 'so.are.deeply.', 'The prefix is faded to put emphasis on the attribute' ); - assert.equal(this.$('tbody tr:eq(8) td:eq(1)').text().trim(), 'properties'); + assert.equal( + this.$('tbody tr:eq(8) td') + .get(1) + .textContent.trim(), + 'properties' + ); }); test('should render a row for key/value pairs even when the value is another object', function( @@ -69,7 +86,7 @@ test('should render a row for key/value pairs even when the value is another obj const countOfParentRows = countOfParentKeys(commonAttributes); assert.equal( - this.$('tbody tr td[colspan="2"]').length, + findAll('tbody tr td[colspan="2"]').length, countOfParentRows, 'Each key for a nested object gets a row with no value' ); diff --git a/ui/tests/integration/job-diff-test.js b/ui/tests/integration/job-diff-test.js index 77d88e6d9..087cbbd9f 100644 --- a/ui/tests/integration/job-diff-test.js +++ b/ui/tests/integration/job-diff-test.js @@ -1,3 +1,4 @@ +import { findAll, find } from 'ember-native-dom-helpers'; import { test, moduleForComponent } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; @@ -28,22 +29,22 @@ test('job field diffs', function(assert) { this.render(commonTemplate); assert.equal( - this.$('.diff-section-label').length, + findAll('.diff-section-label').length, 5, 'A section label for each line, plus one for the group' ); assert.equal( - cleanWhitespace(this.$('.diff-section-label .diff-section-label.is-added').text()), + cleanWhitespace(find('.diff-section-label .diff-section-label.is-added').textContent), '+ Added Field: "Foobar"', 'Added field is rendered correctly' ); assert.equal( - cleanWhitespace(this.$('.diff-section-label .diff-section-label.is-edited').text()), + cleanWhitespace(find('.diff-section-label .diff-section-label.is-edited').textContent), '+/- Edited Field: "256" => "512"', 'Edited field is rendered correctly' ); assert.equal( - cleanWhitespace(this.$('.diff-section-label .diff-section-label.is-deleted').text()), + cleanWhitespace(find('.diff-section-label .diff-section-label.is-deleted').textContent), '- Removed Field: "12"', 'Removed field is rendered correctly' ); @@ -96,51 +97,51 @@ test('job object diffs', function(assert) { this.render(commonTemplate); assert.ok( - cleanWhitespace(this.$('.diff-section-label > .diff-section-label.is-added').text()).startsWith( - '+ DeepConfiguration {' - ), + cleanWhitespace( + find('.diff-section-label > .diff-section-label.is-added').textContent + ).startsWith('+ DeepConfiguration {'), 'Added object starts with a JSON block' ); assert.ok( cleanWhitespace( - this.$('.diff-section-label > .diff-section-label.is-edited').text() + find('.diff-section-label > .diff-section-label.is-edited').textContent ).startsWith('+/- ComplexProperty {'), 'Edited object starts with a JSON block' ); assert.ok( cleanWhitespace( - this.$('.diff-section-label > .diff-section-label.is-deleted').text() + find('.diff-section-label > .diff-section-label.is-deleted').textContent ).startsWith('- DatedStuff {'), 'Removed object starts with a JSON block' ); assert.ok( - cleanWhitespace(this.$('.diff-section-label > .diff-section-label.is-added').text()).endsWith( - '}' - ), + cleanWhitespace( + find('.diff-section-label > .diff-section-label.is-added').textContent + ).endsWith('}'), 'Added object ends the JSON block' ); assert.ok( - cleanWhitespace(this.$('.diff-section-label > .diff-section-label.is-edited').text()).endsWith( - '}' - ), + cleanWhitespace( + find('.diff-section-label > .diff-section-label.is-edited').textContent + ).endsWith('}'), 'Edited object starts with a JSON block' ); assert.ok( - cleanWhitespace(this.$('.diff-section-label > .diff-section-label.is-deleted').text()).endsWith( - '}' - ), + cleanWhitespace( + find('.diff-section-label > .diff-section-label.is-deleted').textContent + ).endsWith('}'), 'Removed object ends the JSON block' ); assert.equal( - this.$('.diff-section-label > .diff-section-label.is-added > .diff-section-label').length, + findAll('.diff-section-label > .diff-section-label.is-added > .diff-section-label').length, this.get('diff').Objects[1].Objects.length + this.get('diff').Objects[1].Fields.length, 'Edited block contains each nested field and object' ); assert.equal( - this.$( + findAll( '.diff-section-label > .diff-section-label.is-added > .diff-section-label > .diff-section-label .diff-section-table-row' ).length, this.get('diff').Objects[1].Objects[0].Fields.length, diff --git a/ui/tests/integration/list-pagination-test.js b/ui/tests/integration/list-pagination-test.js index 6e1c40529..a3091d62a 100644 --- a/ui/tests/integration/list-pagination-test.js +++ b/ui/tests/integration/list-pagination-test.js @@ -1,3 +1,4 @@ +import { findAll, find } from 'ember-native-dom-helpers'; import { test, skip, moduleForComponent } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; @@ -12,7 +13,9 @@ const defaults = { spread: 2, }; -const list100 = Array(100).fill(null).map((_, i) => i); +const list100 = Array(100) + .fill(null) + .map((_, i) => i); test('the source property', function(assert) { this.set('source', list100); @@ -33,31 +36,31 @@ test('the source property', function(assert) { {{/list-pagination}} `); - assert.ok(!this.$('.first').length, 'On the first page, there is no first link'); - assert.ok(!this.$('.prev').length, 'On the first page, there is no prev link'); + assert.ok(!findAll('.first').length, 'On the first page, there is no first link'); + assert.ok(!findAll('.prev').length, 'On the first page, there is no prev link'); assert.equal( - this.$('.link').length, + findAll('.link').length, defaults.spread + 1, 'Pages links spread to the right by the spread amount' ); for (var pageNumber = 1; pageNumber <= defaults.spread + 1; pageNumber++) { - assert.ok(this.$(`.link.page-${pageNumber}`).length, `Page link includes ${pageNumber}`); + assert.ok(findAll(`.link.page-${pageNumber}`).length, `Page link includes ${pageNumber}`); } - assert.ok(this.$('.next').length, 'While not on the last page, there is a next link'); - assert.ok(this.$('.last').length, 'While not on the last page, there is a last link'); + assert.ok(findAll('.next').length, 'While not on the last page, there is a next link'); + assert.ok(findAll('.last').length, 'While not on the last page, there is a last link'); assert.ok( - this.$('.item').length, + findAll('.item').length, defaults.size, `Only ${defaults.size} (the default) number of items are rendered` ); for (var item = 0; item < defaults.size; item++) { assert.equal( - this.$(`.item:eq(${item})`).text(), + findAll('.item')[item].textContent, item, 'Rendered items are in the current page' ); @@ -76,7 +79,7 @@ test('the size property', function(assert) { `); const totalPages = Math.ceil(this.get('source').length / this.get('size')); - assert.equal(this.$('.page-info').text(), `1 of ${totalPages}`, `${totalPages} total pages`); + assert.equal(find('.page-info').textContent, `1 of ${totalPages}`, `${totalPages} total pages`); }); test('the spread property', function(assert) { @@ -144,14 +147,14 @@ test('there are no pagination links when source is less than page size', functio {{/list-pagination}} `); - assert.ok(!this.$('.first').length, 'No first link'); - assert.ok(!this.$('.prev').length, 'No prev link'); - assert.ok(!this.$('.next').length, 'No next link'); - assert.ok(!this.$('.last').length, 'No last link'); + assert.ok(!findAll('.first').length, 'No first link'); + assert.ok(!findAll('.prev').length, 'No prev link'); + assert.ok(!findAll('.next').length, 'No next link'); + assert.ok(!findAll('.last').length, 'No last link'); - assert.equal(this.$('.page-info').text(), '1 of 1', 'Only one page'); + assert.equal(find('.page-info').textContent, '1 of 1', 'Only one page'); assert.equal( - this.$('.item').length, + findAll('.item').length, this.get('source.length'), 'Number of items equals length of source' ); @@ -180,10 +183,10 @@ test('when there are no items in source', function(assert) { `); assert.ok( - !this.$('.page-info, .first, .prev, .link, .next, .last, .item').length, + !findAll('.page-info, .first, .prev, .link, .next, .last, .item').length, 'Nothing in the yield renders' ); - assert.ok(this.$('.empty-state').length, 'Empty state is rendered'); + assert.ok(findAll('.empty-state').length, 'Empty state is rendered'); }); // when there is less pages than the total spread amount @@ -210,13 +213,13 @@ test('when there is less pages than the total spread amount', function(assert) { {{/list-pagination}} `); - assert.ok(this.$('.first').length, 'First page still exists'); - assert.ok(this.$('.prev').length, 'Prev page still exists'); - assert.ok(this.$('.next').length, 'Next page still exists'); - assert.ok(this.$('.last').length, 'Last page still exists'); - assert.equal(this.$('.link').length, totalPages, 'Every page gets a page link'); + assert.ok(findAll('.first').length, 'First page still exists'); + assert.ok(findAll('.prev').length, 'Prev page still exists'); + assert.ok(findAll('.next').length, 'Next page still exists'); + assert.ok(findAll('.last').length, 'Last page still exists'); + assert.equal(findAll('.link').length, totalPages, 'Every page gets a page link'); for (var pageNumber = 1; pageNumber < totalPages; pageNumber++) { - assert.ok(this.$(`.link.page-${pageNumber}`).length, `Page link for ${pageNumber} exists`); + assert.ok(findAll(`.link.page-${pageNumber}`).length, `Page link for ${pageNumber} exists`); } }); @@ -224,7 +227,7 @@ function testSpread(assert) { const { spread, currentPage } = this.getProperties('spread', 'currentPage'); for (var pageNumber = currentPage - spread; pageNumber <= currentPage + spread; pageNumber++) { assert.ok( - this.$(`.link.page-${pageNumber}`).length, + findAll(`.link.page-${pageNumber}`).length, `Page links for currentPage (${currentPage}) +/- spread of ${spread} (${pageNumber})` ); } @@ -234,7 +237,7 @@ function testItems(assert) { const { currentPage, size } = this.getProperties('currentPage', 'size'); for (var item = 0; item < size; item++) { assert.equal( - this.$(`.item:eq(${item})`).text(), + findAll('.item')[item].textContent, item + (currentPage - 1) * size, `Rendered items are in the current page, ${currentPage} (${item + (currentPage - 1) * size})` ); diff --git a/ui/tests/integration/list-table-test.js b/ui/tests/integration/list-table-test.js index a9c688035..d399f87f8 100644 --- a/ui/tests/integration/list-table-test.js +++ b/ui/tests/integration/list-table-test.js @@ -1,3 +1,4 @@ +import { findAll, find } from 'ember-native-dom-helpers'; import { test, skip, moduleForComponent } from 'ember-qunit'; import { faker } from 'ember-cli-mirage'; import hbs from 'htmlbars-inline-precompile'; @@ -6,11 +7,13 @@ moduleForComponent('list-table', 'Integration | Component | list table', { integration: true, }); -const commonTable = Array(10).fill(null).map(() => ({ - firstName: faker.name.firstName(), - lastName: faker.name.lastName(), - age: faker.random.number({ min: 18, max: 60 }), -})); +const commonTable = Array(10) + .fill(null) + .map(() => ({ + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + age: faker.random.number({ min: 18, max: 60 }), + })); // thead test('component exposes a thead contextual component', function(assert) { @@ -25,12 +28,8 @@ test('component exposes a thead contextual component', function(assert) { {{/list-table}} `); - assert.ok(this.$('.head').length, 'Table head is rendered'); - assert.equal( - this.$('.head').prop('tagName').toLowerCase(), - 'thead', - 'Table head is a thead element' - ); + assert.ok(findAll('.head').length, 'Table head is rendered'); + assert.equal(find('.head').tagName.toLowerCase(), 'thead', 'Table head is a thead element'); }); // tbody @@ -52,22 +51,39 @@ test('component exposes a tbody contextual component', function(assert) { {{/list-table}} `); - assert.ok(this.$('.body').length, 'Table body is rendered'); - assert.equal( - this.$('.body').prop('tagName').toLowerCase(), - 'tbody', - 'Table body is a tbody element' - ); + assert.ok(findAll('.body').length, 'Table body is rendered'); + assert.equal(find('.body').tagName.toLowerCase(), 'tbody', 'Table body is a tbody element'); - assert.equal(this.$('.item').length, this.get('source.length'), 'Each item gets its own row'); + assert.equal(findAll('.item').length, this.get('source.length'), 'Each item gets its own row'); // list-table is not responsible for sorting, only dispatching sort events. The table is still // rendered in index-order. this.get('source').forEach((item, index) => { const $item = this.$(`.item:eq(${index})`); - assert.equal($item.find('td:eq(0)').text().trim(), item.firstName, 'First name'); - assert.equal($item.find('td:eq(1)').text().trim(), item.lastName, 'Last name'); - assert.equal($item.find('td:eq(2)').text().trim(), item.age, 'Age'); + assert.equal( + $item + .find('td:eq(0)') + .text() + .trim(), + item.firstName, + 'First name' + ); + assert.equal( + $item + .find('td:eq(1)') + .text() + .trim(), + item.lastName, + 'Last name' + ); + assert.equal( + $item + .find('td:eq(2)') + .text() + .trim(), + item.age, + 'Age' + ); }); }); From 491f4ff27d1665a260c26d98999964c7e953756e Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Tue, 26 Sep 2017 11:57:46 -0700 Subject: [PATCH 4/4] Fix the links in table rows bug Click events were greedily redirecting to the resource pages instead of first yielding to the anchor tag clicked if an anchor tag was in fact clicked. --- ui/app/components/allocation-row.js | 3 ++- ui/app/components/client-node-row.js | 3 ++- ui/app/components/job-row.js | 3 ++- ui/app/components/server-agent-row.js | 4 +++- ui/app/components/task-group-row.js | 3 ++- ui/app/helpers/lazy-click.js | 19 +++++++++++++++++++ 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 ui/app/helpers/lazy-click.js diff --git a/ui/app/components/allocation-row.js b/ui/app/components/allocation-row.js index c6bf62788..415d5f3fe 100644 --- a/ui/app/components/allocation-row.js +++ b/ui/app/components/allocation-row.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import { lazyClick } from '../helpers/lazy-click'; const { Component } = Ember; @@ -15,6 +16,6 @@ export default Component.extend({ onClick() {}, click(event) { - this.get('onClick')(event); + lazyClick([this.get('onClick'), event]); }, }); diff --git a/ui/app/components/client-node-row.js b/ui/app/components/client-node-row.js index ed4c47dfc..e2f9dd19c 100644 --- a/ui/app/components/client-node-row.js +++ b/ui/app/components/client-node-row.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import { lazyClick } from '../helpers/lazy-click'; const { Component } = Ember; @@ -11,7 +12,7 @@ export default Component.extend({ onClick() {}, click(event) { - this.get('onClick')(event); + lazyClick([this.get('onClick'), event]); }, didReceiveAttrs() { diff --git a/ui/app/components/job-row.js b/ui/app/components/job-row.js index 1ac98b43d..024f8d6c1 100644 --- a/ui/app/components/job-row.js +++ b/ui/app/components/job-row.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import { lazyClick } from '../helpers/lazy-click'; const { Component } = Ember; @@ -11,7 +12,7 @@ export default Component.extend({ onClick() {}, click(event) { - this.get('onClick')(event); + lazyClick([this.get('onClick'), event]); }, didReceiveAttrs() { diff --git a/ui/app/components/server-agent-row.js b/ui/app/components/server-agent-row.js index 5fbb6832f..f853ada96 100644 --- a/ui/app/components/server-agent-row.js +++ b/ui/app/components/server-agent-row.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import { lazyClick } from '../helpers/lazy-click'; const { Component, inject, computed } = Ember; @@ -28,6 +29,7 @@ export default Component.extend({ }), click() { - this.get('router').transitionTo('servers.server', this.get('agent')); + const transition = () => this.get('router').transitionTo('servers.server', this.get('agent')); + lazyClick([transition, event]); }, }); diff --git a/ui/app/components/task-group-row.js b/ui/app/components/task-group-row.js index e23541e9c..4fb239637 100644 --- a/ui/app/components/task-group-row.js +++ b/ui/app/components/task-group-row.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import { lazyClick } from '../helpers/lazy-click'; const { Component } = Ember; @@ -12,6 +13,6 @@ export default Component.extend({ onClick() {}, click(event) { - this.get('onClick')(event); + lazyClick([this.get('onClick'), event]); }, }); diff --git a/ui/app/helpers/lazy-click.js b/ui/app/helpers/lazy-click.js new file mode 100644 index 000000000..f9d12dcd2 --- /dev/null +++ b/ui/app/helpers/lazy-click.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; + +const { Helper, $ } = Ember; + +/** + * Lazy Click Event + * + * Usage: {{lazy-click action}} + * + * Calls the provided action only if the target isn't an anchor + * that should be handled instead. + */ +export function lazyClick([onClick, event]) { + if (!$(event.target).is('a')) { + onClick(event); + } +} + +export default Helper.helper(lazyClick);