diff --git a/cypress/component/info.cy.js b/cypress/component/info.cy.js
new file mode 100644
index 000000000..8ff105b38
--- /dev/null
+++ b/cypress/component/info.cy.js
@@ -0,0 +1,220 @@
+/**
+ * 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 InfoComponent from '@/components/cylc/Info.vue'
+import { Tokens } from '@/utils/uid'
+
+const DESCRIPTION = `Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi.
+`
+const TOKENS = new Tokens('~user/workflow//1234/foo')
+const TASK = {
+ id: TOKENS.id,
+ name: TOKENS.task,
+ tokens: TOKENS,
+ node: {
+ state: 'running',
+ task: {
+ meta: {
+ title: 'My Foo',
+ description: DESCRIPTION,
+ URL: 'https://cylc.org',
+ customMeta: {
+ answer: '42',
+ question: 'mutually exclusive',
+ }
+ }
+ },
+ prerequisites: [
+ {
+ satisfied: false,
+ expression: '(c0 & c1) | c2',
+ conditions: [
+ {
+ taskId: 'a',
+ message: 'succeeded',
+ reqState: 'succeeded',
+ exprAlias: 'c0',
+ satisfied: true,
+ },
+ {
+ taskId: 'b',
+ message: 'custom message',
+ reqState: 'custom_output',
+ exprAlias: 'c1',
+ satisfied: false,
+ },
+ {
+ taskId: 'a',
+ message: 'expired',
+ reqState: 'expired',
+ exprAlias: 'c2',
+ satisfied: false,
+ },
+ ],
+ },
+ {
+ satisfied: true,
+ expression: 'c0',
+ conditions: [
+ {
+ taskId: 'x',
+ message: 'succeeded',
+ reqState: 'succeeded',
+ exprAlias: 'c0',
+ satisfied: true,
+ },
+ ],
+ },
+ ],
+ outputs: [
+ {
+ label: 'started',
+ message: 'started',
+ satisfied: true,
+ },
+ {
+ label: 'succeeded',
+ message: 'succeeded',
+ satisfied: false,
+ },
+ {
+ label: 'failed',
+ message: 'failed',
+ satisfied: false,
+ },
+ ]
+ },
+ children: [
+ {
+ id: TOKENS.clone({ job: '01' }).id,
+ tokens: TOKENS.clone({ job: '01' }),
+ name: '01',
+ node: {
+ state: 'failed'
+ }
+ },
+ {
+ id: TOKENS.clone({ job: '02' }).id,
+ tokens: TOKENS.clone({ job: '02' }),
+ name: '02',
+ node: {
+ state: 'succeeded'
+ }
+ },
+ ],
+}
+
+describe('Info component', () => {
+ it('displays task information', () => {
+ cy.vmount(InfoComponent, {
+ props: {
+ task: TASK,
+ class: 'job_theme--default',
+ // NOTE: expand all sections by default
+ panelExpansion: [0, 1, 2],
+ }
+ })
+
+ // there should be a task icon (running)
+ cy.get('.c-graph-node .c8-task.running').should('be.visible')
+
+ // and two job icons (succeeded & failed)
+ cy.get('.c-graph-node .c-job').should('have.length', 2)
+ .get('.c-graph-node .c-job .failed').should('be.visible')
+ .get('.c-graph-node .c-job .succeeded').should('be.visible')
+
+ // the metadata panel
+ cy.get('.metadata-panel.v-expansion-panel--active').should('be.visible')
+ .contains('My Foo')
+ .get('.metadata-panel') // the description should be displayed
+ .contains(/Lorem ipsum dolor sit amet.*/)
+ .get('.metadata-panel a:first') // the URL should be an anchor
+ .should('have.attr', 'href', 'https://cylc.org')
+ .contains(/^https:\/\/cylc.org$/)
+
+ // the prerequisites panel
+ cy.get('.prerequisites-panel.v-expansion-panel--active').should('be.visible')
+ .find('.prerequisite-alias.condition')
+ .should('have.length', 6)
+ .then((selector) => {
+ expect(selector[0]).to.contain('(c0 & c1) | c2')
+ expect(selector[0].classList.toString()).to.equal('prerequisite-alias condition')
+
+ expect(selector[0]).to.contain('c0')
+ expect(selector[1].classList.toString()).to.equal('prerequisite-alias condition satisfied')
+
+ expect(selector[0]).to.contain('c1')
+ expect(selector[2].classList.toString()).to.equal('prerequisite-alias condition')
+
+ expect(selector[0]).to.contain('c2')
+ expect(selector[3].classList.toString()).to.equal('prerequisite-alias condition')
+
+ expect(selector[0]).to.contain('c0')
+ expect(selector[4].classList.toString()).to.equal('prerequisite-alias condition satisfied')
+
+ expect(selector[0]).to.contain('c0')
+ expect(selector[5].classList.toString()).to.equal('prerequisite-alias condition satisfied')
+ })
+
+ // the outputs panel
+ cy.get('.outputs-panel.v-expansion-panel--active').should('be.visible')
+ .find('.condition')
+ .should('have.length', 3)
+ .then((selector) => {
+ expect(selector[0]).to.contain('started')
+ expect(selector[0].classList.toString()).to.equal('condition satisfied')
+
+ expect(selector[1]).to.contain('succeeded')
+ expect(selector[1].classList.toString()).to.equal('condition')
+
+ expect(selector[2]).to.contain('failed')
+ expect(selector[2].classList.toString()).to.equal('condition')
+ })
+ })
+
+ it('should expand sections as intended', () => {
+ const spy = cy.spy()
+ cy.vmount(InfoComponent, {
+ props: {
+ task: TASK,
+ class: 'job_theme--default'
+ },
+ listeners: {
+ 'update:panelExpansion': spy,
+ }
+ }).as('wrapper')
+
+ // ONLY the metadata panel should be expanded by default
+ cy.get('.v-expansion-panel--active')
+ .should('have.length', 1)
+ .should('have.class', 'metadata-panel')
+
+ // the update:panelExpansion event should be emitted when a panel is
+ // expanded/collapsed
+ cy.get('.prerequisites-panel')
+ .find('button')
+ .should('be.visible')
+ .click({ force: true })
+ .get('@wrapper').then(({ wrapper }) => {
+ expect(
+ wrapper.emitted('update:panelExpansion')[0][0]
+ ).to.deep.equal([0, 1])
+ })
+ })
+})
diff --git a/src/services/mock/json/index.cjs b/src/services/mock/json/index.cjs
index 522885114..8f6a3f766 100644
--- a/src/services/mock/json/index.cjs
+++ b/src/services/mock/json/index.cjs
@@ -24,6 +24,7 @@ const { LogData } = require('./logData.cjs')
const { LogFiles } = require('./logFiles.cjs')
const analysisQuery = require('./analysisQuery.json')
const ganttQuery = require('./ganttQuery.json')
+const InfoViewSubscription = require('./infoView.json')
const workflows = [workflowOne, ...workflowsMulti]
const analysisTaskQuery = analysisQuery.taskQuery
@@ -43,5 +44,6 @@ module.exports = {
analysisTaskQuery,
analysisJobQuery,
analysisQuery,
- ganttQuery
+ ganttQuery,
+ InfoViewSubscription
}
diff --git a/src/services/mock/json/infoView.json b/src/services/mock/json/infoView.json
new file mode 100644
index 000000000..45c2c74c0
--- /dev/null
+++ b/src/services/mock/json/infoView.json
@@ -0,0 +1,81 @@
+{
+ "deltas": {
+ "id": "~user/one",
+ "added": {
+ "taskProxies": [
+ {
+ "id": "~user/one//20000102T0000Z/failed",
+ "state": "failed",
+ "isHeld": false,
+ "isQueued": false,
+ "isRunahead": false,
+
+ "task": {
+ "meanElapsedTime": "10",
+ "meta": {
+ "title": "Failed Task",
+ "description": "A task that always fails!",
+ "URL": "https://cylc.org",
+ "userDefined": {
+ "my custom field": "My custom value!"
+ }
+ }
+ },
+
+ "jobs": [
+ {
+ "id": "~user/one//20000102T0000Z/failed/01",
+ "jobId": "1234",
+ "startedTime": 0,
+ "state": "failed"
+ }
+ ],
+
+ "prerequisites": [
+ {
+ "satisfied": true,
+ "expression": "c0 & c1",
+ "conditions": [
+ {
+ "taskId": "20000102T0000Z/succeeded",
+ "reqState": "succeeded",
+ "expreAlias": "c0",
+ "satisfied": true
+ },
+ {
+ "taskId": "20000102T0000Z/eventually_succeeded",
+ "reqState": "succeeded",
+ "expreAlias": "c1",
+ "satisfied": true
+ }
+ ]
+ }
+ ],
+
+ "outputs": [
+ {
+ "label": "submitted",
+ "satisfied": true
+ },
+ {
+ "label": "started",
+ "satisfied": true
+ },
+ {
+ "label": "succeeded",
+ "satisfied": false
+ },
+ {
+ "label": "failed",
+ "satisfied": true
+ },
+ {
+ "label": "expired",
+ "satisfied": false
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/e2e/specs/info.cy.js b/tests/e2e/specs/info.cy.js
new file mode 100644
index 000000000..29a637869
--- /dev/null
+++ b/tests/e2e/specs/info.cy.js
@@ -0,0 +1,47 @@
+/**
+ * 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 .
+ */
+
+describe('Info View', () => {
+ it('works', () => {
+ // test opening the "Info View" from a task in the "Tree View"
+ cy.visit('/#/workspace/one')
+ // click on task 20000102T0000Z/failed
+ .get('.c-treeitem .c-treeitem .c-treeitem:first')
+ .find('.c-task')
+ .click({ force: true })
+
+ // from the menu select the "Info" psudo-mutation
+ .get('.v-list > :nth-child(6)')
+ .contains('Info')
+ .click({ force: true })
+
+ // the info view should open
+ .get('.c-info').should('be.visible')
+
+ // the metadata panel should be expanded by default
+ .find('.v-expansion-panel--active')
+ .should('have.length', 1)
+ .should('have.class', 'metadata-panel')
+
+ // other panels should expand when clicked
+ .get('.c-info .v-expansion-panel:nth-child(2)')
+ .find('button')
+ .click({ force: true })
+ .get('.v-expansion-panel--active')
+ .should('have.length', 2)
+ })
+})