From cfbc4a9496f7bc23a6406d993beb081e10a8af66 Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Mon, 12 Aug 2024 14:56:42 +0100 Subject: [PATCH] metadata-view: added offline data and tests * Added offline data. * Added component tests to cover component display. * Added e2e test to cover opening view, expansion state and data provision. --- cypress/component/info.cy.js | 220 +++++++++++++++++++++++++++ src/services/mock/json/index.cjs | 4 +- src/services/mock/json/infoView.json | 81 ++++++++++ tests/e2e/specs/info.cy.js | 47 ++++++ 4 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 cypress/component/info.cy.js create mode 100644 src/services/mock/json/infoView.json create mode 100644 tests/e2e/specs/info.cy.js 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) + }) +})