From 5f8b707122fa67599882eba07e1368cfd8ade272 Mon Sep 17 00:00:00 2001
From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
Date: Tue, 16 May 2023 12:30:18 +0100
Subject: [PATCH 1/3] Fix table view showing wrong latest job
---
src/views/Table.vue | 14 +---
tests/unit/views/table.vue.spec.js | 104 +++++++++++++++++++++++++++++
2 files changed, 106 insertions(+), 12 deletions(-)
create mode 100644 tests/unit/views/table.vue.spec.js
diff --git a/src/views/Table.vue b/src/views/Table.vue
index 6243ae458..5af13316a 100644
--- a/src/views/Table.vue
+++ b/src/views/Table.vue
@@ -73,23 +73,13 @@ export default {
},
tasks () {
const ret = []
- let latestJob
- let previousJob
for (const workflow of this.workflows) {
for (const cycle of workflow.children) {
for (const task of cycle.children) {
- latestJob = null
- previousJob = null
- if (task.children.length) {
- latestJob = task.children.slice(-1)[0]
- if (task.children.length > 1) {
- previousJob = task.children.slice(-2)[0]
- }
- }
ret.push({
task,
- latestJob,
- previousJob
+ latestJob: task.children[0],
+ previousJob: task.children[1],
})
}
}
diff --git a/tests/unit/views/table.vue.spec.js b/tests/unit/views/table.vue.spec.js
new file mode 100644
index 000000000..77bee88a5
--- /dev/null
+++ b/tests/unit/views/table.vue.spec.js
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) NIWA & British Crown (Met Office) & Contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import { mount } from '@vue/test-utils'
+import { createStore } from 'vuex'
+import sinon from 'sinon'
+import storeOptions from '@/store/options'
+import Table from '@/views/Table.vue'
+import WorkflowService from '@/services/workflow.service'
+import User from '@/model/User.model'
+
+chai.config.truncateThreshold = 0
+
+const workflows = [
+ {
+ id: '~user/one',
+ children: [
+ {
+ id: '~user/one//1',
+ children: [
+ {
+ id: '~user/one//1/eventually_succeeded',
+ children: [
+ {
+ id: '~user/one//1/eventually_succeeded/3',
+ children: [],
+ },
+ {
+ id: '~user/one//1/eventually_succeeded/2',
+ children: [],
+ },
+ {
+ id: '~user/one//1/eventually_succeeded/1',
+ children: [],
+ },
+ ],
+ },
+ {
+ id: '~user/one//1/failed',
+ children: [
+ {
+ id: '~user/one//1/failed/1',
+ children: [],
+ },
+ ],
+ },
+ ]
+ }
+ ]
+ },
+]
+
+describe('Table view', () => {
+ let store, $workflowService
+ beforeEach(() => {
+ store = createStore(storeOptions)
+ const user = new User('cylc', [], new Date(), true, 'localhost', 'owner')
+ store.commit('user/SET_USER', user)
+ $workflowService = sinon.createStubInstance(WorkflowService)
+ })
+
+ it('computes tasks', async () => {
+ const wrapper = mount(Table, {
+ shallow: true,
+ global: {
+ plugins: [store],
+ mocks: { $workflowService }
+ },
+ props: {
+ workflowName: 'one',
+ },
+ data: () => ({
+ // Override computed property
+ workflows
+ })
+ })
+
+ expect(wrapper.vm.tasks).toMatchObject([
+ {
+ task: { id: '~user/one//1/eventually_succeeded' },
+ latestJob: { id: '~user/one//1/eventually_succeeded/3' },
+ previousJob: { id: '~user/one//1/eventually_succeeded/2' }
+ },
+ {
+ task: { id: '~user/one//1/failed' },
+ latestJob: { id: '~user/one//1/failed/1' },
+ }
+ ])
+ })
+})
From 3a7f696fbbebbd71d1c79ed0dd321e6b4e34055b Mon Sep 17 00:00:00 2001
From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
Date: Tue, 16 May 2023 12:30:23 +0100
Subject: [PATCH 2/3] Tidy
---
src/components/cylc/table/Table.vue | 4 +-
src/store/workflows.module.js | 9 ++-
src/utils/tasks.js | 13 ++---
.../components/cylc/table/table.vue.spec.js | 14 +----
.../components/cylc/tree/tree.vue.spec.js | 14 +----
.../components/cylc/tree/treeitem.vue.spec.js | 9 +--
tests/unit/utils/tasks.spec.js | 58 +++++++++----------
7 files changed, 47 insertions(+), 74 deletions(-)
diff --git a/src/components/cylc/table/Table.vue b/src/components/cylc/table/Table.vue
index 2f0df1f7a..b4539ff57 100644
--- a/src/components/cylc/table/Table.vue
+++ b/src/components/cylc/table/Table.vue
@@ -60,14 +60,14 @@ along with this program. If not, see .
{{ item.value.task.name }}
diff --git a/src/store/workflows.module.js b/src/store/workflows.module.js
index 1bd61b2ab..a3472c9ce 100644
--- a/src/store/workflows.module.js
+++ b/src/store/workflows.module.js
@@ -80,7 +80,7 @@ const getters = {
while (stack.length) {
item = stack.pop()
if (parentNodeTypes.includes(item.type)) {
- // this is above "nodeTyoe" in the tree, look through its child nodes
+ // this is above "nodeType" in the tree, look through its child nodes
stack.push(...item.children)
} else if (
item.type === nodeType &&
@@ -99,12 +99,11 @@ function createTree (state) {
if (state.cylcTree) {
return
}
- const tree = {
+ state.cylcTree = {
$index: {},
- id: '$root'
+ id: '$root',
+ children: [],
}
- tree.children = []
- state.cylcTree = tree
// console.log('@@')
}
diff --git a/src/utils/tasks.js b/src/utils/tasks.js
index b177c592e..f7bb0016c 100644
--- a/src/utils/tasks.js
+++ b/src/utils/tasks.js
@@ -20,7 +20,7 @@ import { TASK_OUTPUT_NAMES } from '@/model/TaskOutput.model'
/**
* States used when the parent is stopped.
- * @type {Array}
+ * @type {TaskState[]}
*/
const isStoppedOrderedStates = [
TaskState.SUBMIT_FAILED,
@@ -35,9 +35,9 @@ const isStoppedOrderedStates = [
/**
* Gives a single state, based on a list of states of children nodes.
- * @param childStates {Array} children nodes
- * @param isStopped {boolean} whether the parent node is stopped or not
- * @returns {string} a valid Task State name, or null if not found
+ * @param {TaskState[]} childStates children nodes
+ * @param {boolean} isStopped whether the parent node is stopped or not
+ * @returns {string} a valid Task State name, or empty string if not found
* @link @see https://github.com/cylc/cylc-flow/blob/d66ae5c3ce8c749c8178d1cd53cb8c81d1560346/lib/cylc/task_state_prop.py
*/
function extractGroupState (childStates, isStopped = false) {
@@ -51,10 +51,7 @@ function extractGroupState (childStates, isStopped = false) {
}
function latestJob (taskProxy) {
- if (taskProxy && taskProxy.children && taskProxy.children.length > 0) {
- return taskProxy.children[0].node
- }
- return null
+ return taskProxy?.children?.[0]?.node
}
/** Returns an array of task messages and custom outputs for a job node.
diff --git a/tests/unit/components/cylc/table/table.vue.spec.js b/tests/unit/components/cylc/table/table.vue.spec.js
index a404efdd2..a24a70239 100644
--- a/tests/unit/components/cylc/table/table.vue.spec.js
+++ b/tests/unit/components/cylc/table/table.vue.spec.js
@@ -17,26 +17,18 @@
import { mount } from '@vue/test-utils'
import { createVuetify } from 'vuetify'
+import sinon from 'sinon'
import { simpleTableTasks } from './table.data'
import TaskState from '@/model/TaskState.model'
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
import Table from '@/components/cylc/table/Table.vue'
import { VDataTable, VDataTableFooter } from 'vuetify/labs/VDataTable'
+import WorkflowService from '@/services/workflow.service'
const $eventBus = {
emit () {}
}
-const $workflowService = {
- register () {},
- unregister () {},
- subscribe () {},
- introspection: Promise.resolve({
- mutations: [
- { args: [] }
- ],
- types: []
- })
-}
+const $workflowService = sinon.createStubInstance(WorkflowService)
const vuetify = createVuetify({
components: { VDataTable, VDataTableFooter }
diff --git a/tests/unit/components/cylc/tree/tree.vue.spec.js b/tests/unit/components/cylc/tree/tree.vue.spec.js
index 4e1a26df9..7ed3f3da9 100644
--- a/tests/unit/components/cylc/tree/tree.vue.spec.js
+++ b/tests/unit/components/cylc/tree/tree.vue.spec.js
@@ -19,25 +19,17 @@
import { nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { createVuetify } from 'vuetify'
+import sinon from 'sinon'
import Tree from '@/components/cylc/tree/Tree.vue'
import { simpleWorkflowTree4Nodes } from './tree.data'
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
import cloneDeep from 'lodash/cloneDeep'
+import WorkflowService from '@/services/workflow.service'
const $eventBus = {
emit () {}
}
-const $workflowService = {
- register () {},
- unregister () {},
- subscribe () {},
- introspection: Promise.resolve({
- mutations: [
- { args: [] }
- ],
- types: []
- })
-}
+const $workflowService = sinon.createStubInstance(WorkflowService)
const vuetify = createVuetify()
describe('Tree component', () => {
diff --git a/tests/unit/components/cylc/tree/treeitem.vue.spec.js b/tests/unit/components/cylc/tree/treeitem.vue.spec.js
index e3c5a7e74..3dfb55b02 100644
--- a/tests/unit/components/cylc/tree/treeitem.vue.spec.js
+++ b/tests/unit/components/cylc/tree/treeitem.vue.spec.js
@@ -19,6 +19,7 @@
import { mount } from '@vue/test-utils'
import { Assertion } from 'chai'
import { createVuetify } from 'vuetify'
+import sinon from 'sinon'
import TreeItem from '@/components/cylc/tree/TreeItem.vue'
import {
simpleWorkflowNode,
@@ -26,6 +27,7 @@ import {
simpleTaskNode
} from './tree.data'
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
+import WorkflowService from '@/services/workflow.service'
/**
* Helper function for expecting TreeItem to be expanded.
@@ -50,12 +52,7 @@ Assertion.addMethod('expanded', function () {
)
})
-const $workflowService = {
- introspection: Promise.resolve({
- mutations: [],
- types: []
- })
-}
+const $workflowService = sinon.createStubInstance(WorkflowService)
const $eventBus = {
emit: () => {}
}
diff --git a/tests/unit/utils/tasks.spec.js b/tests/unit/utils/tasks.spec.js
index 504e82d2f..27af497db 100644
--- a/tests/unit/utils/tasks.spec.js
+++ b/tests/unit/utils/tasks.spec.js
@@ -56,37 +56,33 @@ describe('tasks', () => {
expect(extractGroupState([])).to.equal('')
})
})
- describe('latestJob', () => {
- it('should return the correct value for latestJob', () => {
- const tests = [
- {
- taskProxy: null,
- expected: null
- },
- {
- taskProxy: {},
- expected: null
- },
- {
- taskProxy: {
- children: []
- },
- expected: null
- },
- {
- taskProxy: {
- children: [
- {
- node: 1
- }
- ]
- },
- expected: 1
- }
- ]
- tests.forEach(test => {
- expect(latestJob(test.taskProxy)).to.equal(test.expected)
- })
+ describe.each([
+ {
+ taskProxy: null,
+ expected: undefined
+ },
+ {
+ taskProxy: {},
+ expected: undefined
+ },
+ {
+ taskProxy: {
+ children: []
+ },
+ expected: undefined
+ },
+ {
+ taskProxy: {
+ children: [
+ { node: 'foo' },
+ { node: 'bar' },
+ ]
+ },
+ expected: 'foo'
+ }
+ ])('latestJob($taskProxy)', ({ taskProxy, expected }) => {
+ it(`returns ${expected}`, () => {
+ expect(latestJob(taskProxy)).to.equal(expected)
})
})
describe('dtMean', () => {
From 706c5658e07d006d6fbd3512d443b38a402093e7 Mon Sep 17 00:00:00 2001
From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
Date: Mon, 5 Jun 2023 17:17:32 +0100
Subject: [PATCH 3/3] Update CHANGES.md
---
CHANGES.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index 773e7cdec..7dfb834cd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -18,6 +18,11 @@ ones in. -->
[#1269](https://github.com/cylc/cylc-ui/pull/1269) -
Upgraded Vue and Vuetify frameworks to v3.
+### Fixes
+
+[#1312](https://github.com/cylc/cylc-ui/pull/1312) -
+Fix incorrect latest job info in table view.
+
-------------------------------------------------------------------------------
## __cylc-ui-1.6.0 (Upcoming)__