From fd6b42d479854025ccb83f9d35a8389fe5992a02 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 30 Mar 2022 13:42:05 +0200 Subject: [PATCH] refactor(wizards/reportcontrol): change iedsPicker use filtered-list --- src/wizards/reportcontrol.ts | 82 +++++++++--- .../reportcontrol-wizarding-editing.test.ts | 83 ++++++++---- .../__snapshots__/reportcontrol.test.snap.js | 124 ++++++++++++++++++ test/unit/wizards/reportcontrol.test.ts | 35 +++-- 4 files changed, 261 insertions(+), 63 deletions(-) diff --git a/src/wizards/reportcontrol.ts b/src/wizards/reportcontrol.ts index 20c236db3a..ee611fdea5 100644 --- a/src/wizards/reportcontrol.ts +++ b/src/wizards/reportcontrol.ts @@ -3,6 +3,7 @@ import { get, translate } from 'lit-translate'; import '@material/mwc-button'; import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-list/mwc-check-list-item'; import { List } from '@material/mwc-list'; import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; import { SingleSelectedEvent } from '@material/mwc-list/mwc-list-foundation'; @@ -32,12 +33,9 @@ import { WizardAction, MenuAction, } from '../foundation.js'; +import { FilteredList } from '../filtered-list.js'; import { FinderList } from '../finder-list.js'; -import { - dataAttributePicker, - iEDPicker, - iEDsPicker, -} from './foundation/finder.js'; +import { dataAttributePicker, iEDPicker } from './foundation/finder.js'; import { maxLength, patterns } from './foundation/limits.js'; import { editDataSetWizard } from './dataset.js'; import { newFCDA } from './fcda.js'; @@ -409,17 +407,13 @@ function copyReportControlActions(element: Element): WizardActor { return (_: WizardInputElement[], wizard: Element) => { const doc = element.ownerDocument; - const finder = wizard.shadowRoot?.querySelector('finder-list'); - const paths = finder?.paths ?? []; - - if (paths.length === 0) return []; + const iedItems = ( + wizard.shadowRoot?.querySelector('filtered-list')?.selected + ); const complexActions: ComplexAction[] = []; - paths.forEach(path => { - const [tagName, id] = path.pop()!.split(': '); - if (tagName !== 'IED') return; - - const ied = doc.querySelector(selector(tagName, id)); + iedItems.forEach(iedItem => { + const ied = doc.querySelector(selector('IED', iedItem.value)); if (!ied) return; const sinkLn0 = ied.querySelector('LN0'); @@ -468,9 +462,57 @@ function copyReportControlActions(element: Element): WizardActor { }; } -export function reportControlCopyToIedSelector(element: Element): Wizard { - const doc = element.ownerDocument; +function renderIedListItem(sourceCb: Element, ied: Element): TemplateResult { + const sourceDataSet = sourceCb.parentElement?.querySelector( + `DataSet[name="${sourceCb.getAttribute('datSet')}"]` + ); + const isSourceIed = + sourceCb.closest('IED')?.getAttribute('name') === ied.getAttribute('name'); + const ln0 = ied.querySelector('AccessPoint > Server > LDevice > LN0'); + const hasCbNameConflict = ln0?.querySelector( + `ReportControl[name="${sourceCb.getAttribute('name')}"]` + ) + ? true + : false; + const hasDataSetConflict = ln0?.querySelector( + `DataSet[name="${sourceDataSet?.getAttribute('name')}"]` + ) + ? true + : false; + + // clone DataSet and make sure that FCDA is valid in ied + const sinkDataSet = sourceDataSet?.cloneNode(true); + Array.from(sinkDataSet.querySelectorAll('FCDA')).forEach(fcda => { + if (!existFcdaReference(fcda, ied)) sinkDataSet.removeChild(fcda); + }); + const hasDataMatch = sinkDataSet.children.length > 0; + + const primSpan = ied.getAttribute('name'); + let secondSpan = ''; + if (isSourceIed) secondSpan = 'Source IED'; + else if (!ln0) secondSpan = 'Not a Server'; + else if (hasDataSetConflict && !isSourceIed) + secondSpan = 'ReportControl already exist'; + else if (hasCbNameConflict && !isSourceIed) + secondSpan = 'ReportControl already exist'; + else if (!hasDataMatch) secondSpan = 'No matching data'; + else secondSpan = 'Can be copied'; + + return html`${primSpan}${secondSpan}`; +} + +export function reportControlCopyToIedSelector(element: Element): Wizard { return [ { title: get('report.wizard.location'), @@ -479,7 +521,13 @@ export function reportControlCopyToIedSelector(element: Element): Wizard { label: get('save'), action: copyReportControlActions(element), }, - content: [iEDsPicker(doc)], + content: [ + html`${Array.from(element.ownerDocument.querySelectorAll('IED')).map( + ied => renderIedListItem(element, ied) + )}`, + ], }, ]; } diff --git a/test/integration/wizards/reportcontrol-wizarding-editing.test.ts b/test/integration/wizards/reportcontrol-wizarding-editing.test.ts index 2ea408127b..8be891645e 100644 --- a/test/integration/wizards/reportcontrol-wizarding-editing.test.ts +++ b/test/integration/wizards/reportcontrol-wizarding-editing.test.ts @@ -14,7 +14,7 @@ import { selectReportControlWizard, } from '../../../src/wizards/reportcontrol.js'; import { FinderList } from '../../../src/finder-list.js'; -import { execPath } from 'process'; +import { CheckListItem } from '@material/mwc-list/mwc-check-list-item'; describe('Wizards for SCL element ReportControl', () => { let doc: XMLDocument; @@ -349,7 +349,7 @@ describe('Wizards for SCL element ReportControl', () => { await new Promise(resolve => setTimeout(resolve, 100)); // await animation const iedsPicker = - element.wizardUI.dialog?.querySelector('finder-list'); + element.wizardUI.dialog?.querySelector('filtered-list'); expect(iedsPicker).to.exist; expect(iedsPicker!.multi).to.be.true; @@ -412,7 +412,8 @@ describe('Wizards for SCL element ReportControl', () => { }); describe('defines a selector wizard to select ReportControl copy to sink', () => { - let iedsPicker: FinderList; + let iedsPicker: FilteredList; + let listItem: CheckListItem; beforeEach(async () => { const sourceReportControl = doc.querySelector( @@ -422,8 +423,8 @@ describe('Wizards for SCL element ReportControl', () => { element.workflow.push(() => wizard); await element.requestUpdate(); - iedsPicker = ( - element.wizardUI.dialog?.querySelector('finder-list') + iedsPicker = ( + element.wizardUI.dialog?.querySelector('filtered-list') ); primaryAction = ( @@ -438,32 +439,49 @@ describe('Wizards for SCL element ReportControl', () => { describe('with a sink IED not meeting any of the data references', () => { beforeEach(async () => { - iedsPicker.paths = [['IED: IED4']]; - primaryAction.click(); - + listItem = ( + iedsPicker.items.find(item => item.value.includes('IED4'))! + ); await element.requestUpdate(); }); - it('does not copy the control block ', () => - expect(doc.querySelector('IED[name="IED4"] ReportControl')).to.not - .exist); + it('disables the list item', () => expect(listItem.disabled).to.be.true); - it('does not close the wizard', async () => - expect(element.wizardUI.dialog).to.exist); + it('does not copy the control block ', async () => { + listItem.selected = true; + await listItem.requestUpdate(); + primaryAction.click(); + expect(doc.querySelector('IED[name="IED4"] ReportControl')).to.not + .exist; + }); }); - describe('with a sink IED not meeting partially the data references', () => { + describe('with a sink IED meeting partially the data references', () => { beforeEach(async () => { - iedsPicker.paths = [['IED: IED5']]; - primaryAction.click(); + listItem = ( + iedsPicker.items.find(item => item.value.includes('IED5'))! + ); await element.requestUpdate(); + await listItem.requestUpdate(); + }); + + it('list item is selectable', () => + expect(listItem.disabled).to.be.false); + + it('does copy the control block ', async () => { + listItem.selected = true; + await listItem.requestUpdate(); + primaryAction.click(); + + expect(doc.querySelector('IED[name="IED5"] ReportControl')).to.exist; }); - it('does copy the control block ', () => - expect(doc.querySelector('IED[name="IED5"] ReportControl')).to.exist); + it('removes non referenced data from the DataSet the control block ', async () => { + listItem.selected = true; + await listItem.requestUpdate(); + primaryAction.click(); - it('removes non referenced data from the DataSet the control block ', () => { const rpControl = doc.querySelector('IED[name="IED5"] ReportControl')!; const dataSet = doc.querySelector( `IED[name="IED5"] DataSet[name="${rpControl.getAttribute('datSet')}"]` @@ -471,20 +489,24 @@ describe('Wizards for SCL element ReportControl', () => { expect(dataSet).to.exist; expect(dataSet!.children).to.have.lengthOf(2); }); - - it('does close the wizard', () => - expect(element.wizardUI.dialog).to.not.exist); }); describe('with a sink IED already containing ReportControl', () => { beforeEach(async () => { - iedsPicker.paths = [['IED: IED6']]; - primaryAction.click(); + listItem = ( + iedsPicker.items.find(item => item.value.includes('IED4'))! + ); await element.requestUpdate(); }); - it('does not copy report control block nor DataSet ', () => { + it('list item is disabled', () => expect(listItem.disabled).to.be.true); + + it('does not copy report control block nor DataSet ', async () => { + listItem.selected = true; + await listItem.requestUpdate(); + primaryAction.click(); + const rpControl = doc.querySelector('IED[name="IED6"] ReportControl')!; expect(rpControl.getAttribute('datSet')).to.not.exist; @@ -495,13 +517,18 @@ describe('Wizards for SCL element ReportControl', () => { describe('with a sink IED already containing DataSet', () => { beforeEach(async () => { - iedsPicker.paths = [['IED: IED7']]; - primaryAction.click(); + listItem = ( + iedsPicker.items.find(item => item.value.includes('IED7'))! + ); await element.requestUpdate(); }); - it('does not copy report control block nor DataSet ', () => { + it('does not copy report control block nor DataSet ', async () => { + listItem.selected = true; + await listItem.requestUpdate(); + primaryAction.click(); + const rpControl = doc.querySelector('IED[name="IED7"] ReportControl')!; expect(rpControl).to.not.exist; diff --git a/test/unit/wizards/__snapshots__/reportcontrol.test.snap.js b/test/unit/wizards/__snapshots__/reportcontrol.test.snap.js index a200e5e23b..6aa65b21ce 100644 --- a/test/unit/wizards/__snapshots__/reportcontrol.test.snap.js +++ b/test/unit/wizards/__snapshots__/reportcontrol.test.snap.js @@ -735,3 +735,127 @@ snapshots["Wizards for SCL ReportControl element define a wizard to select the c `; /* end snapshot Wizards for SCL ReportControl element define a wizard to select the control block reference looks like the latest snapshot */ +snapshots["Wizards for SCL ReportControl element define copy to other IED selector looks like the latest snapshot"] = +` +
+ + + + IED2 + + + Source IED + + + + + IED3 + + + ReportControl already exist + + + + + IED4 + + + No matching data + + + + + IED5 + + + Can be copied + + + + + IED6 + + + ReportControl already exist + + + + + IED7 + + + ReportControl already exist + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL ReportControl element define copy to other IED selector looks like the latest snapshot */ + diff --git a/test/unit/wizards/reportcontrol.test.ts b/test/unit/wizards/reportcontrol.test.ts index 60760adbcf..f3303de239 100644 --- a/test/unit/wizards/reportcontrol.test.ts +++ b/test/unit/wizards/reportcontrol.test.ts @@ -26,6 +26,8 @@ import { } from '../../../src/wizards/reportcontrol.js'; import { inverseRegExp, regExp, regexString } from '../../foundation.js'; import { FinderList } from '../../../src/finder-list.js'; +import { FilteredList } from '../../../src/filtered-list.js'; +import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; describe('Wizards for SCL ReportControl element', () => { let doc: XMLDocument; @@ -690,7 +692,8 @@ describe('Wizards for SCL ReportControl element', () => { }); describe('define copy to other IED selector', () => { - let iedsPicker: FinderList; + let iedsPicker: FilteredList; + let listItem: ListItemBase; beforeEach(async () => { const sourceReportControl = doc.querySelector( @@ -700,8 +703,8 @@ describe('Wizards for SCL ReportControl element', () => { element.workflow.push(() => wizard); await element.requestUpdate(); - iedsPicker = ( - element.wizardUI.dialog?.querySelector('finder-list') + iedsPicker = ( + element.wizardUI.dialog?.querySelector('filtered-list') ); primaryAction = ( @@ -711,31 +714,27 @@ describe('Wizards for SCL ReportControl element', () => { ); }); + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }).timeout(5000); + it('allows to copy to multiple IED at once', () => expect(iedsPicker.multi).to.be.true); - describe('with missing sink IED', () => { + describe('with a sink IED not meeting partially the data references', () => { beforeEach(async () => { - iedsPicker.paths = [['IED: IED20']]; - primaryAction.click(); - + listItem = iedsPicker.items.find(item => item.value.includes('IED5'))!; await element.requestUpdate(); }); - it('does not copy the control block ', () => - expect(actionEvent).to.not.have.been.called); - }); + it('disabled the list item', () => + expect(listItem.disabled).to.not.be.true); - describe('with a sink IED not meeting partially the data references', () => { - beforeEach(async () => { - iedsPicker.paths = [['IED: IED5']]; + it('does copy the control block ', () => { + listItem.click(); primaryAction.click(); - - await element.requestUpdate(); + expect(actionEvent).to.have.been.called; }); - - it('does copy the control block ', () => - expect(actionEvent).to.have.been.called); }); }); });