Skip to content

Commit 63bdcc2

Browse files
Merge pull request #1312 from MetRonnie/table-view
Fix incorrect latest job in table view
2 parents 9a49c59 + 706c565 commit 63bdcc2

File tree

10 files changed

+158
-86
lines changed

10 files changed

+158
-86
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ ones in. -->
1818
[#1269](https://github.com/cylc/cylc-ui/pull/1269) -
1919
Upgraded Vue and Vuetify frameworks to v3.
2020

21+
### Fixes
22+
23+
[#1312](https://github.com/cylc/cylc-ui/pull/1312) -
24+
Fix incorrect latest job info in table view.
25+
2126
-------------------------------------------------------------------------------
2227
## __cylc-ui-1.6.0 (<span actions:bind='release-date'>Released 2023-04-27</span>)__
2328

src/components/cylc/table/Table.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6060
<Task
6161
v-cylc-object="item.value.task"
6262
:task="item.value.task.node"
63-
:startTime="((item.value.latestJob || {}).node || {}).startedTime"
63+
:startTime="item.value.latestJob?.node?.startedTime"
6464
/>
6565
</div>
6666
<div class="mr-1">
6767
<Job
6868
v-cylc-object="item.value.task"
6969
:status="item.value.task.node.state"
70-
:previous-state="((item.value.previousJob || {}).node || {}).state"
70+
:previous-state="item.value.previousJob?.node?.state"
7171
/>
7272
</div>
7373
<div>{{ item.value.task.name }}</div>

src/store/workflows.module.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const getters = {
8080
while (stack.length) {
8181
item = stack.pop()
8282
if (parentNodeTypes.includes(item.type)) {
83-
// this is above "nodeTyoe" in the tree, look through its child nodes
83+
// this is above "nodeType" in the tree, look through its child nodes
8484
stack.push(...item.children)
8585
} else if (
8686
item.type === nodeType &&
@@ -99,12 +99,11 @@ function createTree (state) {
9999
if (state.cylcTree) {
100100
return
101101
}
102-
const tree = {
102+
state.cylcTree = {
103103
$index: {},
104-
id: '$root'
104+
id: '$root',
105+
children: [],
105106
}
106-
tree.children = []
107-
state.cylcTree = tree
108107
// console.log('@@')
109108
}
110109

src/utils/tasks.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { TASK_OUTPUT_NAMES } from '@/model/TaskOutput.model'
2020

2121
/**
2222
* States used when the parent is stopped.
23-
* @type {Array<TaskState>}
23+
* @type {TaskState[]}
2424
*/
2525
const isStoppedOrderedStates = [
2626
TaskState.SUBMIT_FAILED,
@@ -35,9 +35,9 @@ const isStoppedOrderedStates = [
3535

3636
/**
3737
* Gives a single state, based on a list of states of children nodes.
38-
* @param childStates {Array<TaskState>} children nodes
39-
* @param isStopped {boolean} whether the parent node is stopped or not
40-
* @returns {string} a valid Task State name, or null if not found
38+
* @param {TaskState[]} childStates children nodes
39+
* @param {boolean} isStopped whether the parent node is stopped or not
40+
* @returns {string} a valid Task State name, or empty string if not found
4141
* @link @see https://github.com/cylc/cylc-flow/blob/d66ae5c3ce8c749c8178d1cd53cb8c81d1560346/lib/cylc/task_state_prop.py
4242
*/
4343
function extractGroupState (childStates, isStopped = false) {
@@ -51,10 +51,7 @@ function extractGroupState (childStates, isStopped = false) {
5151
}
5252

5353
function latestJob (taskProxy) {
54-
if (taskProxy && taskProxy.children && taskProxy.children.length > 0) {
55-
return taskProxy.children[0].node
56-
}
57-
return null
54+
return taskProxy?.children?.[0]?.node
5855
}
5956

6057
/** Returns an array of task messages and custom outputs for a job node.

src/views/Table.vue

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,13 @@ export default {
6565
},
6666
tasks () {
6767
const ret = []
68-
let latestJob
69-
let previousJob
7068
for (const workflow of this.workflows) {
7169
for (const cycle of workflow.children) {
7270
for (const task of cycle.children) {
73-
latestJob = null
74-
previousJob = null
75-
if (task.children.length) {
76-
latestJob = task.children.slice(-1)[0]
77-
if (task.children.length > 1) {
78-
previousJob = task.children.slice(-2)[0]
79-
}
80-
}
8171
ret.push({
8272
task,
83-
latestJob,
84-
previousJob
73+
latestJob: task.children[0],
74+
previousJob: task.children[1],
8575
})
8676
}
8777
}

tests/unit/components/cylc/table/table.vue.spec.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,18 @@
1717

1818
import { mount } from '@vue/test-utils'
1919
import { createVuetify } from 'vuetify'
20+
import sinon from 'sinon'
2021
import { simpleTableTasks } from './table.data'
2122
import TaskState from '@/model/TaskState.model'
2223
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
2324
import Table from '@/components/cylc/table/Table.vue'
2425
import { VDataTable, VDataTableFooter } from 'vuetify/labs/VDataTable'
26+
import WorkflowService from '@/services/workflow.service'
2527

2628
const $eventBus = {
2729
emit () {}
2830
}
29-
const $workflowService = {
30-
register () {},
31-
unregister () {},
32-
subscribe () {},
33-
introspection: Promise.resolve({
34-
mutations: [
35-
{ args: [] }
36-
],
37-
types: []
38-
})
39-
}
31+
const $workflowService = sinon.createStubInstance(WorkflowService)
4032

4133
const vuetify = createVuetify({
4234
components: { VDataTable, VDataTableFooter }

tests/unit/components/cylc/tree/tree.vue.spec.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,17 @@
1919
import { nextTick } from 'vue'
2020
import { mount } from '@vue/test-utils'
2121
import { createVuetify } from 'vuetify'
22+
import sinon from 'sinon'
2223
import Tree from '@/components/cylc/tree/Tree.vue'
2324
import { simpleWorkflowTree4Nodes } from './tree.data'
2425
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
2526
import cloneDeep from 'lodash/cloneDeep'
27+
import WorkflowService from '@/services/workflow.service'
2628

2729
const $eventBus = {
2830
emit () {}
2931
}
30-
const $workflowService = {
31-
register () {},
32-
unregister () {},
33-
subscribe () {},
34-
introspection: Promise.resolve({
35-
mutations: [
36-
{ args: [] }
37-
],
38-
types: []
39-
})
40-
}
32+
const $workflowService = sinon.createStubInstance(WorkflowService)
4133
const vuetify = createVuetify()
4234

4335
describe('Tree component', () => {

tests/unit/components/cylc/tree/treeitem.vue.spec.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import { mount } from '@vue/test-utils'
2020
import { Assertion } from 'chai'
2121
import { createVuetify } from 'vuetify'
22+
import sinon from 'sinon'
2223
import TreeItem from '@/components/cylc/tree/TreeItem.vue'
2324
import {
2425
simpleWorkflowNode,
2526
simpleCyclepointNode,
2627
simpleTaskNode
2728
} from './tree.data'
2829
import CylcObjectPlugin from '@/components/cylc/cylcObject/plugin'
30+
import WorkflowService from '@/services/workflow.service'
2931

3032
/**
3133
* Helper function for expecting TreeItem to be expanded.
@@ -50,12 +52,7 @@ Assertion.addMethod('expanded', function () {
5052
)
5153
})
5254

53-
const $workflowService = {
54-
introspection: Promise.resolve({
55-
mutations: [],
56-
types: []
57-
})
58-
}
55+
const $workflowService = sinon.createStubInstance(WorkflowService)
5956
const $eventBus = {
6057
emit: () => {}
6158
}

tests/unit/utils/tasks.spec.js

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -56,37 +56,33 @@ describe('tasks', () => {
5656
expect(extractGroupState([])).to.equal('')
5757
})
5858
})
59-
describe('latestJob', () => {
60-
it('should return the correct value for latestJob', () => {
61-
const tests = [
62-
{
63-
taskProxy: null,
64-
expected: null
65-
},
66-
{
67-
taskProxy: {},
68-
expected: null
69-
},
70-
{
71-
taskProxy: {
72-
children: []
73-
},
74-
expected: null
75-
},
76-
{
77-
taskProxy: {
78-
children: [
79-
{
80-
node: 1
81-
}
82-
]
83-
},
84-
expected: 1
85-
}
86-
]
87-
tests.forEach(test => {
88-
expect(latestJob(test.taskProxy)).to.equal(test.expected)
89-
})
59+
describe.each([
60+
{
61+
taskProxy: null,
62+
expected: undefined
63+
},
64+
{
65+
taskProxy: {},
66+
expected: undefined
67+
},
68+
{
69+
taskProxy: {
70+
children: []
71+
},
72+
expected: undefined
73+
},
74+
{
75+
taskProxy: {
76+
children: [
77+
{ node: 'foo' },
78+
{ node: 'bar' },
79+
]
80+
},
81+
expected: 'foo'
82+
}
83+
])('latestJob($taskProxy)', ({ taskProxy, expected }) => {
84+
it(`returns ${expected}`, () => {
85+
expect(latestJob(taskProxy)).to.equal(expected)
9086
})
9187
})
9288
describe('dtMean', () => {

tests/unit/views/table.vue.spec.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import { mount } from '@vue/test-utils'
19+
import { createStore } from 'vuex'
20+
import sinon from 'sinon'
21+
import storeOptions from '@/store/options'
22+
import Table from '@/views/Table.vue'
23+
import WorkflowService from '@/services/workflow.service'
24+
import User from '@/model/User.model'
25+
26+
chai.config.truncateThreshold = 0
27+
28+
const workflows = [
29+
{
30+
id: '~user/one',
31+
children: [
32+
{
33+
id: '~user/one//1',
34+
children: [
35+
{
36+
id: '~user/one//1/eventually_succeeded',
37+
children: [
38+
{
39+
id: '~user/one//1/eventually_succeeded/3',
40+
children: [],
41+
},
42+
{
43+
id: '~user/one//1/eventually_succeeded/2',
44+
children: [],
45+
},
46+
{
47+
id: '~user/one//1/eventually_succeeded/1',
48+
children: [],
49+
},
50+
],
51+
},
52+
{
53+
id: '~user/one//1/failed',
54+
children: [
55+
{
56+
id: '~user/one//1/failed/1',
57+
children: [],
58+
},
59+
],
60+
},
61+
]
62+
}
63+
]
64+
},
65+
]
66+
67+
describe('Table view', () => {
68+
let store, $workflowService
69+
beforeEach(() => {
70+
store = createStore(storeOptions)
71+
const user = new User('cylc', [], new Date(), true, 'localhost', 'owner')
72+
store.commit('user/SET_USER', user)
73+
$workflowService = sinon.createStubInstance(WorkflowService)
74+
})
75+
76+
it('computes tasks', async () => {
77+
const wrapper = mount(Table, {
78+
shallow: true,
79+
global: {
80+
plugins: [store],
81+
mocks: { $workflowService }
82+
},
83+
props: {
84+
workflowName: 'one',
85+
},
86+
data: () => ({
87+
// Override computed property
88+
workflows
89+
})
90+
})
91+
92+
expect(wrapper.vm.tasks).toMatchObject([
93+
{
94+
task: { id: '~user/one//1/eventually_succeeded' },
95+
latestJob: { id: '~user/one//1/eventually_succeeded/3' },
96+
previousJob: { id: '~user/one//1/eventually_succeeded/2' }
97+
},
98+
{
99+
task: { id: '~user/one//1/failed' },
100+
latestJob: { id: '~user/one//1/failed/1' },
101+
}
102+
])
103+
})
104+
})

0 commit comments

Comments
 (0)