diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ced53105..34574af94 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -708,13 +708,13 @@ jobs: - checkout - restore_cache: keys: - - v1-deps-{{ checksum "ui/yarn.lock" }} - - v1-deps- + - v2-deps-{{ checksum "ui/yarn.lock" }} + - v2-deps- - run: command: cd ui && yarn install name: yarn install - save_cache: - key: v1-deps-{{ checksum "ui/yarn.lock" }} + key: v2-deps-{{ checksum "ui/yarn.lock" }} paths: - ./ui/node_modules - run: diff --git a/.circleci/config/jobs/test-ui.yml b/.circleci/config/jobs/test-ui.yml index 384b4f175..866465fdf 100644 --- a/.circleci/config/jobs/test-ui.yml +++ b/.circleci/config/jobs/test-ui.yml @@ -7,13 +7,13 @@ steps: - checkout - restore_cache: keys: - - v1-deps-{{ checksum "ui/yarn.lock" }} - - v1-deps- + - v2-deps-{{ checksum "ui/yarn.lock" }} + - v2-deps- - run: name: yarn install command: cd ui && yarn install - save_cache: - key: v1-deps-{{ checksum "ui/yarn.lock" }} + key: v2-deps-{{ checksum "ui/yarn.lock" }} paths: - ./ui/node_modules - run: diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 2e69db5bc..51f829062 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -7,12 +7,18 @@ module.exports = { browser: true, es6: true, }, - extends: 'eslint:recommended', + extends: [ + 'eslint:recommended', + 'plugin:ember/recommended', + ], parser: 'babel-eslint', parserOptions: { ecmaVersion: 2018, sourceType: 'module', }, + plugins: [ + 'ember' + ], rules: { indent: ['error', 2, { SwitchCase: 1 }], 'linebreak-style': ['error', 'unix'], diff --git a/ui/app/adapters/application.js b/ui/app/adapters/application.js index 60a7fdacb..7bbf51b02 100644 --- a/ui/app/adapters/application.js +++ b/ui/app/adapters/application.js @@ -24,6 +24,8 @@ export default RESTAdapter.extend({ 'X-Nomad-Token': token, }; } + + return; }), handleResponse(status, headers, payload) { diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 93ff7a9fb..2bc323838 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -3,9 +3,9 @@ import addToPath from 'nomad-ui/utils/add-to-path'; import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; export default Watchable.extend(WithNamespaceIDs, { - relationshipFallbackLinks: { + relationshipFallbackLinks: Object.freeze({ summary: '/summary', - }, + }), fetchRawDefinition(job) { const url = this.urlForFindRecord(job.get('id'), 'job'); diff --git a/ui/app/adapters/plugin.js b/ui/app/adapters/plugin.js index c89779543..836488ad1 100644 --- a/ui/app/adapters/plugin.js +++ b/ui/app/adapters/plugin.js @@ -1,7 +1,7 @@ import Watchable from './watchable'; export default Watchable.extend({ - queryParamsToAttrs: { + queryParamsToAttrs: Object.freeze({ type: 'type', - }, + }), }); diff --git a/ui/app/adapters/volume.js b/ui/app/adapters/volume.js index 551e49126..8f2d8532a 100644 --- a/ui/app/adapters/volume.js +++ b/ui/app/adapters/volume.js @@ -2,8 +2,8 @@ import Watchable from './watchable'; import WithNamespaceIDs from 'nomad-ui/mixins/with-namespace-ids'; export default Watchable.extend(WithNamespaceIDs, { - queryParamsToAttrs: { + queryParamsToAttrs: Object.freeze({ type: 'type', plugin_id: 'plugin.id', - }, + }), }); diff --git a/ui/app/components/allocation-stat.js b/ui/app/components/allocation-stat.js index c8f16a7a4..b1d702a13 100644 --- a/ui/app/components/allocation-stat.js +++ b/ui/app/components/allocation-stat.js @@ -24,6 +24,8 @@ export default Component.extend({ if (metric === 'cpu' || metric === 'memory') { return this[this.metric]; } + + return; }), formattedStat: computed('metric', 'stat.used', function() { @@ -32,13 +34,9 @@ export default Component.extend({ return this.stat.used; }), - formattedReserved: computed( - 'metric', - 'statsTracker.reservedMemory', - 'statsTracker.reservedCPU', - function() { - if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; - if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; - } - ), + formattedReserved: computed('metric', 'statsTracker.{reservedMemory,reservedCPU}', function() { + if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`; + if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`; + return; + }), }); diff --git a/ui/app/components/distribution-bar.js b/ui/app/components/distribution-bar.js index ee672b4ac..743aead63 100644 --- a/ui/app/components/distribution-bar.js +++ b/ui/app/components/distribution-bar.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Component from '@ember/component'; import { computed, observer, set } from '@ember/object'; import { run } from '@ember/runloop'; diff --git a/ui/app/components/drain-popover.js b/ui/app/components/drain-popover.js index c5f2187e4..f964a8081 100644 --- a/ui/app/components/drain-popover.js +++ b/ui/app/components/drain-popover.js @@ -27,14 +27,16 @@ export default Component.extend({ durationIsCustom: equal('selectedDurationQuickOption.value', 'custom'), customDuration: '', - durationQuickOptions: computed(() => [ - { label: '1 Hour', value: '1h' }, - { label: '4 Hours', value: '4h' }, - { label: '8 Hours', value: '8h' }, - { label: '12 Hours', value: '12h' }, - { label: '1 Day', value: '1d' }, - { label: 'Custom', value: 'custom' }, - ]), + durationQuickOptions: computed(function() { + return [ + { label: '1 Hour', value: '1h' }, + { label: '4 Hours', value: '4h' }, + { label: '8 Hours', value: '8h' }, + { label: '12 Hours', value: '12h' }, + { label: '1 Day', value: '1d' }, + { label: 'Custom', value: 'custom' }, + ]; + }), deadline: computed( 'deadlineEnabled', diff --git a/ui/app/components/fs/browser.js b/ui/app/components/fs/browser.js index 9a7e321e2..bf6b30731 100644 --- a/ui/app/components/fs/browser.js +++ b/ui/app/components/fs/browser.js @@ -19,6 +19,8 @@ export default Component.extend({ if (this.model.allocation) { return this.model; } + + return; }), type: computed('taskState', function() { @@ -53,5 +55,4 @@ export default Component.extend({ } } ), - }); diff --git a/ui/app/components/fs/file.js b/ui/app/components/fs/file.js index 36739853c..c8e6a06ec 100644 --- a/ui/app/components/fs/file.js +++ b/ui/app/components/fs/file.js @@ -64,19 +64,15 @@ export default Component.extend({ } else if (this.mode === 'head' || this.mode === 'tail') { return 'readat'; } + + return; }), - fileUrl: computed( - 'allocation.id', - 'allocation.node.httpAddr', - 'fetchMode', - 'useServer', - function() { - const address = this.get('allocation.node.httpAddr'); - const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`; - return this.useServer ? url : `//${address}${url}`; - } - ), + fileUrl: computed('allocation.{id,node.httpAddr}', 'fetchMode', 'useServer', function() { + const address = this.get('allocation.node.httpAddr'); + const url = `/v1/client/fs/${this.fetchMode}/${this.allocation.id}`; + return this.useServer ? url : `//${address}${url}`; + }), fileParams: computed('taskState.name', 'file', 'mode', function() { // The Log class handles encoding query params diff --git a/ui/app/components/lifecycle-chart-row.js b/ui/app/components/lifecycle-chart-row.js index 5a3a5e9dd..8ef057556 100644 --- a/ui/app/components/lifecycle-chart-row.js +++ b/ui/app/components/lifecycle-chart-row.js @@ -8,11 +8,15 @@ export default Component.extend({ if (this.taskState && this.taskState.state === 'running') { return 'is-active'; } + + return; }), finishedClass: computed('taskState.finishedAt', function() { if (this.taskState && this.taskState.finishedAt) { return 'is-finished'; } + + return; }), }); diff --git a/ui/app/components/line-chart.js b/ui/app/components/line-chart.js index a5c1d9258..d0b282db6 100644 --- a/ui/app/components/line-chart.js +++ b/ui/app/components/line-chart.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Component from '@ember/component'; import { computed, observer } from '@ember/object'; import { computed as overridable } from 'ember-overridable-computed'; diff --git a/ui/app/components/list-accordion.js b/ui/app/components/list-accordion.js index 5f83b5ee0..105288f1b 100644 --- a/ui/app/components/list-accordion.js +++ b/ui/app/components/list-accordion.js @@ -25,6 +25,7 @@ export default Component.extend({ }; }); + // eslint-disable-next-line ember/no-side-effects this.set('stateCache', decoratedSource); return decoratedSource; }), diff --git a/ui/app/components/multi-select-dropdown.js b/ui/app/components/multi-select-dropdown.js index 3bd9febcc..53e438d95 100644 --- a/ui/app/components/multi-select-dropdown.js +++ b/ui/app/components/multi-select-dropdown.js @@ -29,12 +29,14 @@ export default Component.extend({ didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { - run.scheduleOnce('afterRender', () => { - dropdown.actions.reposition(); - }); + run.scheduleOnce('afterRender', this, this.repositionDropdown); } }, + repositionDropdown() { + this.dropdown.actions.reposition(); + }, + actions: { toggle({ key }) { const newSelection = this.selection.slice(); diff --git a/ui/app/components/popover-menu.js b/ui/app/components/popover-menu.js index 92824e5ef..20e5fc638 100644 --- a/ui/app/components/popover-menu.js +++ b/ui/app/components/popover-menu.js @@ -12,7 +12,7 @@ const FOCUSABLE = [ ].join(', '); export default Component.extend({ - classnames: ['popover'], + classNames: ['popover'], triggerClass: '', isOpen: false, @@ -31,12 +31,14 @@ export default Component.extend({ didReceiveAttrs() { const dropdown = this.dropdown; if (this.isOpen && dropdown) { - run.scheduleOnce('afterRender', () => { - dropdown.actions.reposition(); - }); + run.scheduleOnce('afterRender', this, this.repositionDropdown); } }, + repositionDropdown() { + this.dropdown.actions.reposition(); + }, + actions: { openOnArrowDown(dropdown, e) { if (!this.isOpen && e.keyCode === ARROW_DOWN) { diff --git a/ui/app/components/streaming-file.js b/ui/app/components/streaming-file.js index 5885c2bc8..b35963574 100644 --- a/ui/app/components/streaming-file.js +++ b/ui/app/components/streaming-file.js @@ -17,23 +17,25 @@ export default Component.extend(WindowResizable, { return; } - run.scheduleOnce('actions', () => { - switch (this.mode) { - case 'head': - this.head.perform(); - break; - case 'tail': - this.tail.perform(); - break; - case 'streaming': - if (this.isStreaming) { - this.stream.perform(); - } else { - this.logger.stop(); - } - break; - } - }); + run.scheduleOnce('actions', this, this.performTask); + }, + + performTask() { + switch (this.mode) { + case 'head': + this.head.perform(); + break; + case 'tail': + this.tail.perform(); + break; + case 'streaming': + if (this.isStreaming) { + this.stream.perform(); + } else { + this.logger.stop(); + } + break; + } }, didInsertElement() { @@ -54,17 +56,16 @@ export default Component.extend(WindowResizable, { head: task(function*() { yield this.get('logger.gotoHead').perform(); - run.scheduleOnce('afterRender', () => { - this.element.scrollTop = 0; - }); + run.scheduleOnce('afterRender', this, this.scrollToTop); }), + scrollToTop() { + this.element.scrollTop = 0; + }, + tail: task(function*() { yield this.get('logger.gotoTail').perform(); - run.scheduleOnce('afterRender', () => { - const cliWindow = this.element; - cliWindow.scrollTop = cliWindow.scrollHeight; - }); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); }), synchronizeScrollPosition(force = false) { @@ -78,7 +79,7 @@ export default Component.extend(WindowResizable, { stream: task(function*() { // Force the scroll position to the bottom of the window when starting streaming this.logger.one('tick', () => { - run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition(true)); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition, [true]); }); // Follow the log if the scroll position is near the bottom of the cli window @@ -89,7 +90,7 @@ export default Component.extend(WindowResizable, { }), scheduleScrollSynchronization() { - run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition()); + run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition); }, willDestroy() { diff --git a/ui/app/components/task-log.js b/ui/app/components/task-log.js index 108a9b300..deaef2c89 100644 --- a/ui/app/components/task-log.js +++ b/ui/app/components/task-log.js @@ -28,7 +28,7 @@ export default Component.extend({ mode: 'stdout', - logUrl: computed('allocation.id', 'allocation.node.httpAddr', 'useServer', function() { + logUrl: computed('allocation.{id,node.httpAddr}', 'useServer', function() { const address = this.get('allocation.node.httpAddr'); const allocation = this.get('allocation.id'); diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js index 393a0fd93..4901d1742 100644 --- a/ui/app/components/task-row.js +++ b/ui/app/components/task-row.js @@ -19,7 +19,9 @@ export default Component.extend({ // Internal state statsError: false, - enablePolling: computed(() => !Ember.testing), + enablePolling: computed(function() { + return !Ember.testing; + }), // Since all tasks for an allocation share the same tracker, use the registry stats: computed('task', 'task.isRunning', function() { diff --git a/ui/app/controllers/allocations/allocation/index.js b/ui/app/controllers/allocations/allocation/index.js index caec2d8ae..ebccdfa75 100644 --- a/ui/app/controllers/allocations/allocation/index.js +++ b/ui/app/controllers/allocations/allocation/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { computed, observer } from '@ember/object'; diff --git a/ui/app/controllers/allocations/allocation/task/index.js b/ui/app/controllers/allocations/allocation/task/index.js index 8c26f7854..84a62c675 100644 --- a/ui/app/controllers/allocations/allocation/task/index.js +++ b/ui/app/controllers/allocations/allocation/task/index.js @@ -15,7 +15,7 @@ export default Controller.extend({ }), network: alias('model.resources.networks.firstObject'), - ports: computed('network.reservedPorts.[]', 'network.dynamicPorts.[]', function() { + ports: computed('network.{reservedPorts.[],dynamicPorts.[]}', function() { return (this.get('network.reservedPorts') || []) .map(port => ({ name: port.Label, diff --git a/ui/app/controllers/application.js b/ui/app/controllers/application.js index 7a2934fab..0a1597503 100644 --- a/ui/app/controllers/application.js +++ b/ui/app/controllers/application.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; import { run } from '@ember/runloop'; diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index f7ca0daca..260fdc4fa 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-observers */ import { alias } from '@ember/object/computed'; import Controller from '@ember/controller'; import { computed, observer } from '@ember/object'; @@ -24,7 +25,9 @@ export default Controller.extend(Sortable, Searchable, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['shortId', 'name']), + searchProps: computed(function() { + return ['shortId', 'name']; + }), onlyPreemptions: false, diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index 2c1463c41..78de1aa35 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ import { alias, readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import Controller, { inject as controller } from '@ember/controller'; @@ -35,7 +36,9 @@ export default Controller.extend( sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['id', 'name', 'datacenter']), + searchProps: computed(function() { + return ['id', 'name', 'datacenter']; + }), qpClass: '', qpState: '', @@ -54,25 +57,29 @@ export default Controller.extend( // Remove any invalid node classes from the query param/selection scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set('qpClass', serialize(intersection(classes, this.selectionClass))); }); return classes.sort().map(dc => ({ key: dc, label: dc })); }), - optionsState: computed(() => [ - { key: 'initializing', label: 'Initializing' }, - { key: 'ready', label: 'Ready' }, - { key: 'down', label: 'Down' }, - { key: 'ineligible', label: 'Ineligible' }, - { key: 'draining', label: 'Draining' }, - ]), + optionsState: computed(function() { + return [ + { key: 'initializing', label: 'Initializing' }, + { key: 'ready', label: 'Ready' }, + { key: 'down', label: 'Down' }, + { key: 'ineligible', label: 'Ineligible' }, + { key: 'draining', label: 'Draining' }, + ]; + }), optionsDatacenter: computed('nodes.[]', function() { const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact(); // Remove any invalid datacenters from the query param/selection scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter))); }); @@ -86,6 +93,7 @@ export default Controller.extend( const volumes = Array.from(new Set(allVolumes.mapBy('name'))); scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set('qpVolume', serialize(intersection(volumes, this.selectionVolume))); }); diff --git a/ui/app/controllers/csi/plugins/index.js b/ui/app/controllers/csi/plugins/index.js index ba62315b9..66a888416 100644 --- a/ui/app/controllers/csi/plugins/index.js +++ b/ui/app/controllers/csi/plugins/index.js @@ -30,8 +30,12 @@ export default Controller.extend( currentPage: 1, pageSize: readOnly('userSettings.pageSize'), - searchProps: computed(() => ['id']), - fuzzySearchProps: computed(() => ['id']), + searchProps: computed(function() { + return ['id']; + }), + fuzzySearchProps: computed(function() { + return ['id']; + }), sortProperty: 'id', sortDescending: false, diff --git a/ui/app/controllers/csi/plugins/plugin/allocations.js b/ui/app/controllers/csi/plugins/plugin/allocations.js index 948599c97..941235693 100644 --- a/ui/app/controllers/csi/plugins/plugin/allocations.js +++ b/ui/app/controllers/csi/plugins/plugin/allocations.js @@ -29,24 +29,21 @@ export default Controller.extend(SortableFactory(['updateTime', 'healthy']), { selectionType: selection('qpType'), selectionHealth: selection('qpHealth'), - optionsType: computed(() => [ - { key: 'controller', label: 'Controller' }, - { key: 'node', label: 'Node' }, - ]), + optionsType: computed(function() { + return [{ key: 'controller', label: 'Controller' }, { key: 'node', label: 'Node' }]; + }), - optionsHealth: computed(() => [ - { key: 'true', label: 'Healthy' }, - { key: 'false', label: 'Unhealthy' }, - ]), + optionsHealth: computed(function() { + return [{ key: 'true', label: 'Healthy' }, { key: 'false', label: 'Unhealthy' }]; + }), - combinedAllocations: computed('model.controllers.[]', 'model.nodes.[]', function() { + combinedAllocations: computed('model.{controllers.[],nodes.[]}', function() { return this.model.controllers.toArray().concat(this.model.nodes.toArray()); }), filteredAllocations: computed( 'combinedAllocations.[]', - 'model.controllers.[]', - 'model.nodes.[]', + 'model.{controllers.[],nodes.[]}', 'selectionType', 'selectionHealth', function() { diff --git a/ui/app/controllers/csi/volumes/index.js b/ui/app/controllers/csi/volumes/index.js index 20aad2406..37a17234a 100644 --- a/ui/app/controllers/csi/volumes/index.js +++ b/ui/app/controllers/csi/volumes/index.js @@ -35,14 +35,18 @@ export default Controller.extend( sortProperty: 'id', sortDescending: false, - searchProps: computed(() => ['name']), - fuzzySearchProps: computed(() => ['name']), + searchProps: computed(function() { + return ['name']; + }), + fuzzySearchProps: computed(function() { + return ['name']; + }), fuzzySearchEnabled: true, /** Visible volumes are those that match the selected namespace */ - visibleVolumes: computed('model.[]', 'model.@each.parent', function() { + visibleVolumes: computed('model.{[],@each.parent}', function() { if (!this.model) return []; // Namespace related properties are ommitted from the dependent keys diff --git a/ui/app/controllers/exec.js b/ui/app/controllers/exec.js index a30144772..d6ea565fe 100644 --- a/ui/app/controllers/exec.js +++ b/ui/app/controllers/exec.js @@ -43,14 +43,12 @@ export default Controller.extend({ allocations: alias('model.allocations'), taskState: computed( - 'allocations.[]', + 'allocations.{[],@each.isActive}', 'allocationShortId', - 'allocations.@each.isActive', 'taskName', 'taskGroupName', 'allocation', - 'allocation.states.@each.name', - 'allocation.states.@each.isRunning', + 'allocation.states.@each.{name,isRunning}', function() { if (!this.allocations) { return false; @@ -72,6 +70,8 @@ export default Controller.extend({ if (allocation) { return allocation.states.find(state => state.name === this.taskName); } + + return; } ), diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js index 7052e773a..a3c1bbad7 100644 --- a/ui/app/controllers/jobs/index.js +++ b/ui/app/controllers/jobs/index.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ import { inject as service } from '@ember/service'; import { alias, readOnly } from '@ember/object/computed'; import Controller, { inject as controller } from '@ember/controller'; @@ -32,8 +33,12 @@ export default Controller.extend(Sortable, Searchable, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['id', 'name']), - fuzzySearchProps: computed(() => ['name']), + searchProps: computed(function() { + return ['id', 'name']; + }), + fuzzySearchProps: computed(function() { + return ['name']; + }), fuzzySearchEnabled: true, qpType: '', @@ -46,19 +51,23 @@ export default Controller.extend(Sortable, Searchable, { selectionDatacenter: selection('qpDatacenter'), selectionPrefix: selection('qpPrefix'), - optionsType: computed(() => [ - { key: 'batch', label: 'Batch' }, - { key: 'parameterized', label: 'Parameterized' }, - { key: 'periodic', label: 'Periodic' }, - { key: 'service', label: 'Service' }, - { key: 'system', label: 'System' }, - ]), + optionsType: computed(function() { + return [ + { key: 'batch', label: 'Batch' }, + { key: 'parameterized', label: 'Parameterized' }, + { key: 'periodic', label: 'Periodic' }, + { key: 'service', label: 'Service' }, + { key: 'system', label: 'System' }, + ]; + }), - optionsStatus: computed(() => [ - { key: 'pending', label: 'Pending' }, - { key: 'running', label: 'Running' }, - { key: 'dead', label: 'Dead' }, - ]), + optionsStatus: computed(function() { + return [ + { key: 'pending', label: 'Pending' }, + { key: 'running', label: 'Running' }, + { key: 'dead', label: 'Dead' }, + ]; + }), optionsDatacenter: computed('visibleJobs.[]', function() { const flatten = (acc, val) => acc.concat(val); @@ -67,6 +76,7 @@ export default Controller.extend(Sortable, Searchable, { // Remove any invalid datacenters from the query param/selection const availableDatacenters = Array.from(allDatacenters).compact(); scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set( 'qpDatacenter', serialize(intersection(availableDatacenters, this.selectionDatacenter)) @@ -103,6 +113,7 @@ export default Controller.extend(Sortable, Searchable, { // Remove any invalid prefixes from the query param/selection const availablePrefixes = prefixes.mapBy('prefix'); scheduleOnce('actions', () => { + // eslint-disable-next-line ember/no-side-effects this.set('qpPrefix', serialize(intersection(availablePrefixes, this.selectionPrefix))); }); @@ -117,7 +128,7 @@ export default Controller.extend(Sortable, Searchable, { Visible jobs are those that match the selected namespace and aren't children of periodic or parameterized jobs. */ - visibleJobs: computed('model.[]', 'model.@each.parent', function() { + visibleJobs: computed('model.{[],@each.parent}', function() { // Namespace related properties are ommitted from the dependent keys // due to a prop invalidation bug caused by region switching. const hasNamespaces = this.get('system.namespaces.length'); diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js index 1ec7a40a2..0de958725 100644 --- a/ui/app/controllers/jobs/job/allocations.js +++ b/ui/app/controllers/jobs/job/allocations.js @@ -21,7 +21,9 @@ export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { job: alias('model'), - searchProps: computed(() => ['shortId', 'name', 'taskGroupName']), + searchProps: computed(function() { + return ['shortId', 'name', 'taskGroupName']; + }), allocations: computed('model.allocations.[]', function() { return this.get('model.allocations') || []; diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index 3e1a5d9c7..95127c4ed 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -22,7 +22,9 @@ export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { sortProperty: 'modifyIndex', sortDescending: true, - searchProps: computed(() => ['shortId', 'name']), + searchProps: computed(function() { + return ['shortId', 'name']; + }), allocations: computed('model.allocations.[]', function() { return this.get('model.allocations') || []; diff --git a/ui/app/mixins/searchable.js b/ui/app/mixins/searchable.js index cf39e758b..193679b32 100644 --- a/ui/app/mixins/searchable.js +++ b/ui/app/mixins/searchable.js @@ -22,9 +22,12 @@ import Fuse from 'fuse.js'; Properties provided: - listSearched: a subset of listToSearch of items that meet the search criteria */ +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ searchTerm: '', - listToSearch: computed(() => []), + listToSearch: computed(function() { + return []; + }), searchProps: null, exactMatchSearchProps: reads('searchProps'), @@ -83,11 +86,7 @@ export default Mixin.create({ if (this.exactMatchEnabled) { results.push( - ...exactMatchSearch( - searchTerm, - this.listToSearch, - this.exactMatchSearchProps - ) + ...exactMatchSearch(searchTerm, this.listToSearch, this.exactMatchSearchProps) ); } @@ -96,9 +95,7 @@ export default Mixin.create({ } if (this.regexEnabled) { - results.push( - ...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps) - ); + results.push(...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps)); } return results.uniq(); diff --git a/ui/app/mixins/sortable-factory.js b/ui/app/mixins/sortable-factory.js index e65d18f9b..c341b61ec 100644 --- a/ui/app/mixins/sortable-factory.js +++ b/ui/app/mixins/sortable-factory.js @@ -21,11 +21,14 @@ import { warn } from '@ember/debug'; export default function sortableFactory(properties, fromSortableMixin) { const eachProperties = properties.map(property => `listToSort.@each.${property}`); + // eslint-disable-next-line ember/no-new-mixins return Mixin.create({ // Override in mixin consumer sortProperty: null, sortDescending: true, - listToSort: computed(() => []), + listToSort: computed(function() { + return []; + }), _sortableFactoryWarningPrinted: false, @@ -44,6 +47,7 @@ export default function sortableFactory(properties, fromSortableMixin) { } warn(message, properties.length > 0, { id: 'nomad.no-sortable-properties' }); + // eslint-disable-next-line ember/no-side-effects this.set('_sortableFactoryWarningPrinted', true); } diff --git a/ui/app/mixins/window-resizable.js b/ui/app/mixins/window-resizable.js index a2daf1f85..d5d2eb159 100644 --- a/ui/app/mixins/window-resizable.js +++ b/ui/app/mixins/window-resizable.js @@ -3,18 +3,21 @@ import { run } from '@ember/runloop'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ windowResizeHandler() { assert('windowResizeHandler needs to be overridden in the Component', false); }, setupWindowResize: on('didInsertElement', function() { - run.scheduleOnce('afterRender', this, () => { - this.set('_windowResizeHandler', this.windowResizeHandler.bind(this)); - window.addEventListener('resize', this._windowResizeHandler); - }); + run.scheduleOnce('afterRender', this, this.addResizeListener); }), + addResizeListener() { + this.set('_windowResizeHandler', this.windowResizeHandler.bind(this)); + window.addEventListener('resize', this._windowResizeHandler); + }, + removeWindowResize: on('willDestroyElement', function() { window.removeEventListener('resize', this._windowResizeHandler); }), diff --git a/ui/app/mixins/with-component-visibility-detection.js b/ui/app/mixins/with-component-visibility-detection.js index 2645ed2c8..57c341930 100644 --- a/ui/app/mixins/with-component-visibility-detection.js +++ b/ui/app/mixins/with-component-visibility-detection.js @@ -3,6 +3,7 @@ import Mixin from '@ember/object/mixin'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ visibilityHandler() { assert('visibilityHandler needs to be overridden in the Component', false); diff --git a/ui/app/mixins/with-forbidden-state.js b/ui/app/mixins/with-forbidden-state.js index 64b8db81e..1e7b4e8fa 100644 --- a/ui/app/mixins/with-forbidden-state.js +++ b/ui/app/mixins/with-forbidden-state.js @@ -1,5 +1,6 @@ import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ setupController(controller) { if (this.isForbidden) { diff --git a/ui/app/mixins/with-model-error-handling.js b/ui/app/mixins/with-model-error-handling.js index 484a80148..248806a05 100644 --- a/ui/app/mixins/with-model-error-handling.js +++ b/ui/app/mixins/with-model-error-handling.js @@ -1,6 +1,7 @@ import Mixin from '@ember/object/mixin'; import notifyError from 'nomad-ui/utils/notify-error'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ model() { return this._super(...arguments).catch(notifyError(this)); diff --git a/ui/app/mixins/with-namespace-ids.js b/ui/app/mixins/with-namespace-ids.js index 3ba221751..bdbdb9969 100644 --- a/ui/app/mixins/with-namespace-ids.js +++ b/ui/app/mixins/with-namespace-ids.js @@ -1,6 +1,7 @@ import { inject as service } from '@ember/service'; import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ system: service(), diff --git a/ui/app/mixins/with-namespace-resetting.js b/ui/app/mixins/with-namespace-resetting.js index ab57f3e3e..04469ef1b 100644 --- a/ui/app/mixins/with-namespace-resetting.js +++ b/ui/app/mixins/with-namespace-resetting.js @@ -2,6 +2,7 @@ import { inject as controller } from '@ember/controller'; import { inject as service } from '@ember/service'; import Mixin from '@ember/object/mixin'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ system: service(), jobsController: controller('jobs'), diff --git a/ui/app/mixins/with-route-visibility-detection.js b/ui/app/mixins/with-route-visibility-detection.js index 8f58102c6..707146df8 100644 --- a/ui/app/mixins/with-route-visibility-detection.js +++ b/ui/app/mixins/with-route-visibility-detection.js @@ -3,6 +3,7 @@ import Mixin from '@ember/object/mixin'; import { assert } from '@ember/debug'; import { on } from '@ember/object/evented'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ visibilityHandler() { assert('visibilityHandler needs to be overridden in the Route', false); diff --git a/ui/app/mixins/with-watchers.js b/ui/app/mixins/with-watchers.js index ce89ec526..811a2c792 100644 --- a/ui/app/mixins/with-watchers.js +++ b/ui/app/mixins/with-watchers.js @@ -3,8 +3,11 @@ import { computed } from '@ember/object'; import { assert } from '@ember/debug'; import WithVisibilityDetection from './with-route-visibility-detection'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create(WithVisibilityDetection, { - watchers: computed(() => []), + watchers: computed(function() { + return []; + }), cancelAllWatchers() { this.watchers.forEach(watcher => { diff --git a/ui/app/models/job.js b/ui/app/models/job.js index f3afec1dc..5226b6bf9 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -1,4 +1,4 @@ -import { alias, equal, or, and } from '@ember/object/computed'; +import { alias, equal, or, and, mapBy } from '@ember/object/computed'; import { computed } from '@ember/object'; import Model from 'ember-data/model'; import attr from 'ember-data/attr'; @@ -61,8 +61,7 @@ export default Model.extend({ 'type', 'periodic', 'parameterized', - 'parent.periodic', - 'parent.parameterized', + 'parent.{periodic,parameterized}', function() { const type = this.type; @@ -131,9 +130,11 @@ export default Model.extend({ .uniq(); }), + allocationsUnhealthyDrivers: mapBy('allocations', 'unhealthyDrivers'), + // Getting all unhealthy drivers for a job can be incredibly expensive if the job // has many allocations. This can lead to making an API request for many nodes. - unhealthyDrivers: computed('allocations.@each.unhealthyDrivers.[]', function() { + unhealthyDrivers: computed('allocationsUnhealthyDrivers.[]', function() { return this.allocations .mapBy('unhealthyDrivers') .reduce((all, drivers) => { @@ -149,7 +150,7 @@ export default Model.extend({ hasPlacementFailures: and('latestFailureEvaluation', 'hasBlockedEvaluation'), - latestEvaluation: computed('evaluations.@each.modifyIndex', 'evaluations.isPending', function() { + latestEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { const evaluations = this.evaluations; if (!evaluations || evaluations.get('isPending')) { return null; @@ -157,21 +158,19 @@ export default Model.extend({ return evaluations.sortBy('modifyIndex').get('lastObject'); }), - latestFailureEvaluation: computed( - 'evaluations.@each.modifyIndex', - 'evaluations.isPending', - function() { - const evaluations = this.evaluations; - if (!evaluations || evaluations.get('isPending')) { - return null; - } - - const failureEvaluations = evaluations.filterBy('hasPlacementFailures'); - if (failureEvaluations) { - return failureEvaluations.sortBy('modifyIndex').get('lastObject'); - } + latestFailureEvaluation: computed('evaluations.{@each.modifyIndex,isPending}', function() { + const evaluations = this.evaluations; + if (!evaluations || evaluations.get('isPending')) { + return null; } - ), + + const failureEvaluations = evaluations.filterBy('hasPlacementFailures'); + if (failureEvaluations) { + return failureEvaluations.sortBy('modifyIndex').get('lastObject'); + } + + return; + }), supportsDeployments: equal('type', 'service'), @@ -180,6 +179,7 @@ export default Model.extend({ runningDeployment: computed('latestDeployment', 'latestDeployment.isRunning', function() { const latest = this.latestDeployment; if (latest.get('isRunning')) return latest; + return; }), fetchRawDefinition() { diff --git a/ui/app/models/node.js b/ui/app/models/node.js index eab3c1ff9..d272c7629 100644 --- a/ui/app/models/node.js +++ b/ui/app/models/node.js @@ -62,6 +62,8 @@ export default Model.extend({ if (allocation) { return allocation.modifyTime; } + + return; }), drivers: fragmentArray('node-driver'), diff --git a/ui/app/routes/clients.js b/ui/app/routes/clients.js index 42791cfb5..23c387284 100644 --- a/ui/app/routes/clients.js +++ b/ui/app/routes/clients.js @@ -8,12 +8,12 @@ export default Route.extend(WithForbiddenState, { store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Clients', args: ['clients.index'], }, - ], + ]), beforeModel() { return this.get('system.leader'); diff --git a/ui/app/routes/csi/plugins.js b/ui/app/routes/csi/plugins.js index 9aaf2d516..30b181220 100644 --- a/ui/app/routes/csi/plugins.js +++ b/ui/app/routes/csi/plugins.js @@ -6,12 +6,12 @@ import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; export default Route.extend(WithForbiddenState, { store: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Storage', args: ['csi.index'], }, - ], + ]), model() { return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this)); diff --git a/ui/app/routes/csi/volumes.js b/ui/app/routes/csi/volumes.js index c870dc03a..fe49f7639 100644 --- a/ui/app/routes/csi/volumes.js +++ b/ui/app/routes/csi/volumes.js @@ -7,12 +7,12 @@ export default Route.extend(WithForbiddenState, { system: service(), store: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Storage', args: ['csi.index'], }, - ], + ]), queryParams: { volumeNamespace: { diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js index 08bbb26f9..42b2e1cb5 100644 --- a/ui/app/routes/jobs.js +++ b/ui/app/routes/jobs.js @@ -7,12 +7,12 @@ export default Route.extend(WithForbiddenState, { system: service(), store: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Jobs', args: ['jobs.index'], }, - ], + ]), queryParams: { jobNamespace: { diff --git a/ui/app/routes/jobs/run.js b/ui/app/routes/jobs/run.js index 7bda3b103..3f45bf006 100644 --- a/ui/app/routes/jobs/run.js +++ b/ui/app/routes/jobs/run.js @@ -6,12 +6,12 @@ export default Route.extend({ store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Run', args: ['jobs.run'], }, - ], + ]), beforeModel() { if (this.can.cannot('run job')) { diff --git a/ui/app/routes/servers.js b/ui/app/routes/servers.js index 6f1949334..62ee43b89 100644 --- a/ui/app/routes/servers.js +++ b/ui/app/routes/servers.js @@ -8,12 +8,12 @@ export default Route.extend(WithForbiddenState, { store: service(), system: service(), - breadcrumbs: [ + breadcrumbs: Object.freeze([ { label: 'Servers', args: ['servers.index'], }, - ], + ]), beforeModel() { return this.get('system.leader'); diff --git a/ui/app/serializers/volume.js b/ui/app/serializers/volume.js index e545d42ac..78e5a49b7 100644 --- a/ui/app/serializers/volume.js +++ b/ui/app/serializers/volume.js @@ -6,7 +6,7 @@ export default ApplicationSerializer.extend({ externalId: 'ExternalID', }, - embeddedRelationships: ['writeAllocations', 'readAllocations'], + embeddedRelationships: Object.freeze(['writeAllocations', 'readAllocations']), // Volumes treat Allocations as embedded records. Ember has an // EmbeddedRecords mixin, but it assumes an application is using diff --git a/ui/app/services/breadcrumbs.js b/ui/app/services/breadcrumbs.js index c820aee56..f01a7f680 100644 --- a/ui/app/services/breadcrumbs.js +++ b/ui/app/services/breadcrumbs.js @@ -9,7 +9,7 @@ export default Service.extend({ // currentRouteName has all information necessary to compute breadcrumbs, // but it doesn't change when a transition to the same route with a different // model occurs. - breadcrumbs: computed('router.currentURL', 'router.currentRouteName', function() { + breadcrumbs: computed('router.{currentURL,currentRouteName}', function() { const owner = getOwner(this); const allRoutes = (this.get('router.currentRouteName') || '') .split('.') diff --git a/ui/app/services/stats-trackers-registry.js b/ui/app/services/stats-trackers-registry.js index 5a603d016..751ac1d45 100644 --- a/ui/app/services/stats-trackers-registry.js +++ b/ui/app/services/stats-trackers-registry.js @@ -28,7 +28,9 @@ export default Service.extend({ // A read-only way of getting a reference to the registry. // Since this could be overwritten by a bad actor, it isn't // used in getTracker - registryRef: computed(() => registry), + registryRef: computed(function() { + return registry; + }), getTracker(resource) { if (!resource) return; diff --git a/ui/app/services/system.js b/ui/app/services/system.js index 24aea8291..bf71e4b3e 100644 --- a/ui/app/services/system.js +++ b/ui/app/services/system.js @@ -59,6 +59,7 @@ export default Service.extend({ set(key, value) { if (value == null) { window.localStorage.removeItem('nomadActiveRegion'); + return; } else { // All localStorage values are strings. Stringify first so // the return value is consistent with what is persisted. @@ -110,6 +111,7 @@ export default Service.extend({ set(key, value) { if (value == null) { window.localStorage.removeItem('nomadActiveNamespace'); + return; } else if (typeof value === 'string') { window.localStorage.nomadActiveNamespace = value; return this.namespaces.findBy('id', value); diff --git a/ui/app/services/token.js b/ui/app/services/token.js index 256c2646f..5e6dc70e6 100644 --- a/ui/app/services/token.js +++ b/ui/app/services/token.js @@ -43,6 +43,7 @@ export default Service.extend({ selfToken: computed('secret', 'fetchSelfToken.lastSuccessful.value', function() { if (this.secret) return this.get('fetchSelfToken.lastSuccessful.value'); + return; }), fetchSelfTokenPolicies: task(function*() { diff --git a/ui/app/services/watch-list.js b/ui/app/services/watch-list.js index 964736c8b..ab068650d 100644 --- a/ui/app/services/watch-list.js +++ b/ui/app/services/watch-list.js @@ -6,7 +6,9 @@ import Service from '@ember/service'; let list = {}; export default Service.extend({ - _list: computed(() => copy(list, true)), + _list: computed(function() { + return copy(list, true); + }), list: readOnly('_list'), diff --git a/ui/app/utils/classes/abstract-logger.js b/ui/app/utils/classes/abstract-logger.js index d90b785c1..8dc78ff1c 100644 --- a/ui/app/utils/classes/abstract-logger.js +++ b/ui/app/utils/classes/abstract-logger.js @@ -7,6 +7,7 @@ import queryString from 'query-string'; const MAX_OUTPUT_LENGTH = 50000; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ url: '', params: overridable(() => ({})), diff --git a/ui/app/utils/classes/abstract-stats-tracker.js b/ui/app/utils/classes/abstract-stats-tracker.js index ce39bfd79..dd51c11f4 100644 --- a/ui/app/utils/classes/abstract-stats-tracker.js +++ b/ui/app/utils/classes/abstract-stats-tracker.js @@ -4,6 +4,7 @@ import { assert } from '@ember/debug'; import { task, timeout } from 'ember-concurrency'; import jsonWithDefault from 'nomad-ui/utils/json-with-default'; +// eslint-disable-next-line ember/no-new-mixins export default Mixin.create({ url: '', diff --git a/ui/app/utils/classes/stream-logger.js b/ui/app/utils/classes/stream-logger.js index 557f696fa..98e4fb4f3 100644 --- a/ui/app/utils/classes/stream-logger.js +++ b/ui/app/utils/classes/stream-logger.js @@ -8,9 +8,11 @@ import { fetchFailure } from './log'; export default EmberObject.extend(AbstractLogger, { reader: null, - additionalParams: computed(() => ({ - follow: true, - })), + additionalParams: computed(function() { + return { + follow: true, + }; + }), start() { return this.poll.perform(); diff --git a/ui/app/utils/fetch.js b/ui/app/utils/fetch.js index 460055137..0a4252434 100644 --- a/ui/app/utils/fetch.js +++ b/ui/app/utils/fetch.js @@ -1,4 +1,3 @@ -import Ember from 'ember'; import fetch from 'fetch'; import config from '../config/environment'; @@ -9,6 +8,6 @@ const mirageEnabled = config['ember-cli-mirage'] && config['ember-cli-mirage'].enabled !== false; -const fetchToUse = Ember.testing || mirageEnabled ? fetch : window.fetch || fetch; +const fetchToUse = mirageEnabled ? fetch : window.fetch || fetch; export default fetchToUse; diff --git a/ui/package.json b/ui/package.json index 32dd3eb6b..19488169e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -95,6 +95,7 @@ "ember-test-selectors": "^2.1.0", "ember-truth-helpers": "^2.0.0", "eslint": "^5.16.0", + "eslint-plugin-ember": "^6.2.0", "eslint-plugin-node": "^9.0.1", "faker": "^4.1.0", "flat": "^4.0.0", diff --git a/ui/stories/charts/distribution-bar.stories.js b/ui/stories/charts/distribution-bar.stories.js index 5695d5c87..e84aff747 100644 --- a/ui/stories/charts/distribution-bar.stories.js +++ b/ui/stories/charts/distribution-bar.stories.js @@ -111,7 +111,7 @@ export let LiveUpdating = () => { clearInterval(this.timer); }, - distributionBarDataRotating: computed('timerTicks', () => { + distributionBarDataRotating: computed('timerTicks', function() { return [ { label: 'one', value: Math.round(Math.random() * 50) }, { label: 'two', value: Math.round(Math.random() * 50) }, diff --git a/ui/stories/charts/line-chart.stories.js b/ui/stories/charts/line-chart.stories.js index 7fa793a3a..4493a6b21 100644 --- a/ui/stories/charts/line-chart.stories.js +++ b/ui/stories/charts/line-chart.stories.js @@ -92,6 +92,8 @@ export let LiveData = () => { context: { controller: EmberObject.extend({ startTimer: on('init', function() { + this.lineChartLive = []; + this.set( 'timer', setInterval(() => { @@ -110,8 +112,6 @@ export let LiveData = () => { clearInterval(this.timer); }, - lineChartLive: [], - secondsFormat() { return date => moment(date).format('HH:mm:ss'); }, diff --git a/ui/stories/charts/stats-time-series.stories.js b/ui/stories/charts/stats-time-series.stories.js index 4976b9ce5..f78fab82f 100644 --- a/ui/stories/charts/stats-time-series.stories.js +++ b/ui/stories/charts/stats-time-series.stories.js @@ -102,11 +102,11 @@ export let HighLowComparison = () => { clearInterval(this.timer); }, - metricsHigh: computed(() => { + metricsHigh: computed(function() { return []; }), - metricsLow: computed(() => { + metricsLow: computed(function() { return []; }), diff --git a/ui/stories/components/table.stories.js b/ui/stories/components/table.stories.js index 662bfaeba..170257909 100644 --- a/ui/stories/components/table.stories.js +++ b/ui/stories/components/table.stories.js @@ -240,7 +240,7 @@ export let SortableColumns = () => { }) ), - sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { + sortedShortList: computed('controller.{sortProperty,sortDescending}', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), @@ -278,7 +278,7 @@ export let MultiRow = () => { }) ), - sortedShortList: computed('controller.sortProperty', 'controller.sortDescending', function() { + sortedShortList: computed('controller.{sortProperty,sortDescending}', function() { let sorted = productMetadata.sortBy(this.get('controller.sortProperty') || 'name'); return this.get('controller.sortDescending') ? sorted.reverse() : sorted; }), diff --git a/ui/tests/integration/app-breadcrumbs-test.js b/ui/tests/integration/app-breadcrumbs-test.js index 32985d0f8..20d0fd725 100644 --- a/ui/tests/integration/app-breadcrumbs-test.js +++ b/ui/tests/integration/app-breadcrumbs-test.js @@ -11,7 +11,10 @@ module('Integration | Component | app breadcrumbs', function(hooks) { hooks.beforeEach(function() { const mockBreadcrumbs = Service.extend({ - breadcrumbs: [], + init() { + this._super(...arguments); + this.breadcrumbs = []; + }, }); this.owner.register('service:breadcrumbs', mockBreadcrumbs); diff --git a/ui/tests/integration/primary-metric-test.js b/ui/tests/integration/primary-metric-test.js index d454d4096..2e96436bf 100644 --- a/ui/tests/integration/primary-metric-test.js +++ b/ui/tests/integration/primary-metric-test.js @@ -31,8 +31,12 @@ module('Integration | Component | primary metric', function(hooks) { yield trackerSignalPauseSpy(); }), - cpu: computed(() => []), - memory: computed(() => []), + cpu: computed(function() { + return []; + }), + memory: computed(function() { + return []; + }), }); const mockStatsTrackersRegistry = Service.extend({ diff --git a/ui/tests/unit/abilities/allocation-test.js b/ui/tests/unit/abilities/allocation-test.js index 1190bfc84..174db388c 100644 --- a/ui/tests/unit/abilities/allocation-test.js +++ b/ui/tests/unit/abilities/allocation-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/abilities/client-test.js b/ui/tests/unit/abilities/client-test.js index d037fb281..9b0ebd59d 100644 --- a/ui/tests/unit/abilities/client-test.js +++ b/ui/tests/unit/abilities/client-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/abilities/job-test.js b/ui/tests/unit/abilities/job-test.js index e1e661d0c..bdc4ef5c6 100644 --- a/ui/tests/unit/abilities/job-test.js +++ b/ui/tests/unit/abilities/job-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import Service from '@ember/service'; diff --git a/ui/tests/unit/mixins/searchable-test.js b/ui/tests/unit/mixins/searchable-test.js index 57bf662d3..6596d0b90 100644 --- a/ui/tests/unit/mixins/searchable-test.js +++ b/ui/tests/unit/mixins/searchable-test.js @@ -9,9 +9,12 @@ module('Unit | Mixin | Searchable', function(hooks) { hooks.beforeEach(function() { this.subject = function() { + // eslint-disable-next-line ember/no-new-mixins const SearchableObject = EmberObject.extend(Searchable, { source: null, - searchProps: computed(() => ['id', 'name']), + searchProps: computed(function() { + return ['id', 'name']; + }), listToSearch: alias('source'), }); @@ -193,9 +196,12 @@ module('Unit | Mixin | Searchable (with pagination)', function(hooks) { hooks.beforeEach(function() { this.subject = function() { + // eslint-disable-next-line ember/no-new-mixins const SearchablePaginatedObject = EmberObject.extend(Searchable, { source: null, - searchProps: computed(() => ['id', 'name']), + searchProps: computed(function() { + return ['id', 'name']; + }), listToSearch: alias('source'), currentPage: 1, }); diff --git a/ui/tests/unit/utils/log-test.js b/ui/tests/unit/utils/log-test.js index e69b8c105..072067d0e 100644 --- a/ui/tests/unit/utils/log-test.js +++ b/ui/tests/unit/utils/log-test.js @@ -10,11 +10,11 @@ import { settled } from '@ember/test-helpers'; let startSpy, stopSpy, initSpy, fetchSpy; const MockStreamer = EmberObject.extend({ - poll: { - isRunning: false, - }, - init() { + this.poll = { + isRunning: false, + }; + initSpy(...arguments); }, diff --git a/ui/yarn.lock b/ui/yarn.lock index e5bf949e8..b3a5edbe5 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1279,6 +1279,11 @@ ember-cli-typescript "^2.0.2" inflection "1.12.0" +"@ember-data/rfc395-data@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843" + integrity sha512-tGRdvgC9/QMQSuSuJV45xoyhI0Pzjm7A9o/MVVA3HakXIImJbbzx/k/6dO9CUEQXIyS2y0fW6C1XaYOG7rY0FQ== + "@ember-data/serializer@3.12.4": version "3.12.4" resolved "https://registry.yarnpkg.com/@ember-data/serializer/-/serializer-3.12.4.tgz#e3ae7143f0cd736722daa3a640e11dc07c8aef5a" @@ -7427,6 +7432,11 @@ ember-responsive@^3.0.4: dependencies: ember-cli-babel "^6.6.0" +ember-rfc176-data@^0.3.11: + version "0.3.13" + resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.13.tgz#ed1712a26e65fec703655f35410414aa1982cf3b" + integrity sha512-m9JbwQlT6PjY7x/T8HslnXP7Sz9bx/pz3FrNfNi2NesJnbNISly0Lix6NV1fhfo46572cpq4jrM+/6yYlMefTQ== + ember-rfc176-data@^0.3.12, ember-rfc176-data@^0.3.9: version "0.3.12" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.12.tgz#90d82878e69e2ac9a5438e8ce14d12c6031c5bd2" @@ -7714,6 +7724,15 @@ escodegen@^1.11.0: optionalDependencies: source-map "~0.6.1" +eslint-plugin-ember@^6.2.0: + version "6.10.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-6.10.1.tgz#ca7a5cc28b91a247c31b1686421a66281467f238" + integrity sha512-RZI0+UoR4xeD6UE3KQCUwbN2nZOIIPaFCCXqBIRXDr0rFuwvknAHqYtDPJVZicvTzNHa4TEZvAKqfbE8t7SztQ== + dependencies: + "@ember-data/rfc395-data" "^0.0.4" + ember-rfc176-data "^0.3.11" + snake-case "^2.1.0" + eslint-plugin-es@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" @@ -13960,6 +13979,13 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"