Skip to content

Commit bd9c9db

Browse files
authored
Xtriggers and task run mode in info view (#2102)
1 parent ead53a4 commit bd9c9db

File tree

5 files changed

+233
-11
lines changed

5 files changed

+233
-11
lines changed

src/components/cylc/Info.vue

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8484
</v-expansion-panel-text>
8585
</v-expansion-panel>
8686

87+
<v-expansion-panel class="run-mode-panel">
88+
<v-expansion-panel-title color="blue-grey-lighten-2">
89+
Run Mode
90+
</v-expansion-panel-title>
91+
<v-expansion-panel-text>
92+
<v-icon>{{ runModeIcon }}</v-icon> {{ runMode }}
93+
</v-expansion-panel-text>
94+
</v-expansion-panel>
95+
96+
<v-expansion-panel
97+
v-if="xtriggers.length"
98+
class="xtriggers-panel"
99+
>
100+
<v-expansion-panel-title color="blue-grey-lighten-1">
101+
Xtriggers
102+
</v-expansion-panel-title>
103+
<v-expansion-panel-text>
104+
<v-table density="compact">
105+
<thead>
106+
<tr>
107+
<th>Label</th>
108+
<th>ID</th>
109+
<th>Is satisfied</th>
110+
</tr>
111+
</thead>
112+
<tbody>
113+
<tr v-for="xt in xtriggers" :key="xt.id">
114+
<td>{{ xt.label }}</td>
115+
<td>{{ xt.id }}</td>
116+
<td><v-icon>{{ xt.satisfactionIcon }}</v-icon></td>
117+
</tr>
118+
</tbody>
119+
</v-table>
120+
</v-expansion-panel-text>
121+
</v-expansion-panel>
122+
87123
<!-- The prereqs -->
88124
<v-expansion-panel class="prerequisites-panel">
89125
<v-expansion-panel-title color="blue-grey-lighten-2">
@@ -169,6 +205,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
169205
import { useJobTheme } from '@/composables/localStorage'
170206
import GraphNode from '@/components/cylc/GraphNode.vue'
171207
import { formatCompletion } from '@/utils/outputs'
208+
import {
209+
mdiSkipForward,
210+
mdiChatQuestion,
211+
mdiGhostOutline,
212+
mdiPlay,
213+
mdiDramaMasks,
214+
mdiCheckboxOutline,
215+
mdiCheckboxBlankOutline
216+
} from '@mdi/js'
217+
import { cloneDeep } from 'lodash-es'
172218
173219
export default {
174220
name: 'InfoComponent',
@@ -226,6 +272,47 @@ export default {
226272
completion () {
227273
// Task output completion expression stuff.
228274
return this.task?.node?.runtime.completion
275+
},
276+
277+
runModeIcon () {
278+
// Task Run Mode:
279+
if (this.task?.node?.runtime.runMode === 'Skip') {
280+
return mdiSkipForward
281+
} else if (this.task?.node?.runtime.runMode === 'Live') {
282+
return mdiPlay
283+
} else if (this.task?.node?.runtime.runMode === 'Simulation') {
284+
return mdiGhostOutline
285+
} else if (this.task?.node?.runtime.runMode === 'Dummy') {
286+
return mdiDramaMasks
287+
}
288+
return mdiChatQuestion
289+
},
290+
291+
runMode () {
292+
// Task Run Mode:
293+
return this.task?.node?.runtime.runMode
294+
},
295+
296+
xtriggers () {
297+
const xtriggers = this.task?.node?.xtriggers?.map((item) => {
298+
const xtrigger = cloneDeep(item)
299+
xtrigger.satisfactionIcon = xtrigger.satisfied ? mdiCheckboxOutline : mdiCheckboxBlankOutline
300+
// Extract the trigger time from the ID
301+
// Since we've created this date from a Unix timestamp, we can safely assume it is in UTC:
302+
xtrigger.id = xtrigger.id.replace(
303+
/trigger_time=(?<unixTime>[0-9.]+)/,
304+
(match, p1) => `trigger_time=${new Date(p1 * 1000).toISOString().slice(0, -5)}Z`
305+
)
306+
return xtrigger
307+
})
308+
// Sort the xtriggers by label, then by ID:
309+
xtriggers.sort(function (a, b) {
310+
if (a.label === b.label) {
311+
return a.id > b.id ? 1 : -1
312+
}
313+
return a.label > b.label ? 1 : -1
314+
})
315+
return xtriggers
229316
}
230317
231318
},

src/services/mock/json/infoView.json

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,27 @@
7676
],
7777

7878
"runtime": {
79-
"completion": "succeeded"
80-
}
79+
"completion": "succeeded",
80+
"runMode": "Live"
81+
},
82+
83+
"xtriggers": [
84+
{
85+
"label": "xtrigger",
86+
"id": "myxt(foo=42)",
87+
"satisfied": true
88+
},
89+
{
90+
"label": "another xtrigger",
91+
"id": "myxt(foo=41)",
92+
"satisfied": false
93+
},
94+
{
95+
"label": "clock xtrigger",
96+
"id": "wall_clock(trigger_time=440121600)",
97+
"satisfied": true
98+
}
99+
]
81100
}
82101
]
83102
}

src/views/Info.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ fragment TaskProxyData on TaskProxy {
9797
9898
runtime {
9999
completion
100+
runMode
101+
}
102+
103+
xtriggers {
104+
label
105+
id
106+
satisfied
100107
}
101108
}
102109

tests/component/specs/info.cy.js

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,26 @@ const TASK = {
105105
}
106106
],
107107
runtime: {
108-
completion: '(succeeded and x) or failed'
109-
}
108+
completion: '(succeeded and x) or failed',
109+
runMode: 'Live',
110+
},
111+
xtriggers: [
112+
{
113+
label: 'xtrigger',
114+
id: 'my-xtrigger(foo=99)',
115+
satisfied: false
116+
},
117+
{
118+
label: 'xtrigger',
119+
id: 'my-xtrigger(foo=42)',
120+
satisfied: false
121+
},
122+
{
123+
label: 'wallclock-xtrigger',
124+
id: 'wall_clock(trigger_time=634737600)',
125+
satisfied: true
126+
}
127+
]
110128
},
111129
children: [
112130
{
@@ -135,7 +153,7 @@ describe('Info component', () => {
135153
task: TASK,
136154
class: 'job_theme--default',
137155
// NOTE: expand all sections by default
138-
panelExpansion: [0, 1, 2, 3],
156+
panelExpansion: [0, 1, 2, 3, 4, 5],
139157
}
140158
})
141159

@@ -156,6 +174,21 @@ describe('Info component', () => {
156174
.should('have.attr', 'href', 'https://cylc.org')
157175
.contains(/^https:\/\/cylc.org$/)
158176

177+
// the run mode panel:
178+
cy.get('.run-mode-panel.v-expansion-panel--active').should('be.visible')
179+
.contains('Live')
180+
181+
// the xtriggers panel
182+
cy.get('.xtriggers-panel.v-expansion-panel--active').should('be.visible')
183+
.get('table')
184+
.get('tbody tr')
185+
.children()
186+
.then((selector) => {
187+
expect(selector[0].innerText).to.equal('wallclock-xtrigger')
188+
expect(selector[4].innerText).to.equal('my-xtrigger(foo=42)')
189+
expect(selector[7].innerText).to.equal('my-xtrigger(foo=99)')
190+
})
191+
159192
// the prerequisites panel
160193
cy.get('.prerequisites-panel.v-expansion-panel--active').should('be.visible')
161194
.find('.prerequisite-alias.condition')
@@ -246,7 +279,7 @@ describe('Info component', () => {
246279
.get('@wrapper').then(({ wrapper }) => {
247280
expect(
248281
wrapper.emitted('update:panelExpansion')[0][0]
249-
).to.deep.equal([0, 1])
282+
).to.deep.equal([0, 3])
250283
})
251284
})
252285

@@ -266,6 +299,8 @@ describe('Info component', () => {
266299
},
267300
prerequisites: [],
268301
outputs: [],
302+
runtime: {},
303+
xtriggers: []
269304
},
270305
children: [],
271306
}
@@ -275,8 +310,45 @@ describe('Info component', () => {
275310
task,
276311
class: 'job_theme--default',
277312
// NOTE: expand all sections by default
278-
panelExpansion: [0, 1, 2],
313+
panelExpansion: [0, 1, 2, 3],
279314
}
280315
})
281316
})
317+
318+
for (const mode of ['Skip', 'Simulation', 'Dummy']) {
319+
it('should display ' + mode + ' mode', () => {
320+
// ensure the component can be mounted without errors for empty states
321+
// i.e. no metadata, prerequisites, outputs or jobs
322+
const tokens = new Tokens('~user/workflow//1234/foo')
323+
const task = {
324+
id: tokens.id,
325+
name: tokens.task,
326+
tokens,
327+
node: {
328+
task: {
329+
meta: {
330+
customMeta: {}
331+
}
332+
},
333+
prerequisites: [],
334+
outputs: [],
335+
runtime: { runMode: mode },
336+
xtriggers: []
337+
},
338+
children: [],
339+
}
340+
341+
cy.vmount(InfoComponent, {
342+
props: {
343+
task,
344+
class: 'job_theme--default',
345+
// Expand just the run mode panel
346+
panelExpansion: [1],
347+
}
348+
})
349+
350+
cy.get('.run-mode-panel.v-expansion-panel--active').should('be.visible')
351+
.contains(mode)
352+
})
353+
}
282354
})

tests/e2e/specs/info.cy.js

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,52 @@ describe('Info View', () => {
3232
.get('.c-info').should('be.visible')
3333

3434
// the metadata panel should be expanded by default
35-
.find('.v-expansion-panel--active')
35+
.find('.metadata-panel')
3636
.should('have.length', 1)
3737
.should('have.class', 'metadata-panel')
3838

3939
// other panels should expand when clicked
40-
.get('.c-info .v-expansion-panel:nth-child(2)')
40+
.get('.run-mode-panel')
4141
.find('button')
4242
.click({ force: true })
43-
.get('.v-expansion-panel--active')
44-
.should('have.length', 2)
43+
.get('.run-mode-panel')
44+
.should('have.length', 1)
45+
.and('contain', 'Live')
46+
47+
.get('.xtriggers-panel')
48+
.find('button')
49+
.click({ force: true })
50+
.get('.xtriggers-panel')
51+
.get('table')
52+
.should('contain', 'xtrigger')
53+
.and('contain', 'myxt(foo=42)')
54+
.should('contain', 'another xtrigger')
55+
.and('contain', 'myxt(foo=41)')
56+
57+
.get('.prerequisites-panel')
58+
.find('button')
59+
.click({ force: true })
60+
.get('.prerequisites-panel')
61+
.should('contain', '0 & 1')
62+
63+
.get('.outputs-panel')
64+
.find('button')
65+
.click({ force: true })
66+
// Outputs panel should have three satisfied outputs:
67+
.get('.outputs-panel')
68+
.find('li .satisfied')
69+
.should('contain', 'submitted')
70+
.and('contain', 'started')
71+
.and('contain', 'failed')
72+
// and two unsatisfied outputs:
73+
.get('.outputs-panel')
74+
.find('li')
75+
.should('have.length', 5)
76+
77+
.get('.completion-panel')
78+
.find('button')
79+
.click({ force: true })
80+
.get('.completion-panel')
81+
.should('have.length', 1)
4582
})
4683
})

0 commit comments

Comments
 (0)