From 5e34cb271e4e3dd5eb4d4eea2d5129911954438f Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Fri, 23 Sep 2022 20:27:19 +0200 Subject: [PATCH] fix(menu/importieds): allow import to new projects --- src/menu/ImportIEDs.ts | 76 ++++++++------- .../triggered/ImportIedsPlugin.test.ts | 92 +++++++++++++++++++ test/testfiles/importieds/emptyproject.scd | 4 + 3 files changed, 137 insertions(+), 35 deletions(-) create mode 100644 test/testfiles/importieds/emptyproject.scd diff --git a/src/menu/ImportIEDs.ts b/src/menu/ImportIEDs.ts index 63a55e9f6..f1ec23ae0 100644 --- a/src/menu/ImportIEDs.ts +++ b/src/menu/ImportIEDs.ts @@ -178,10 +178,10 @@ function hasConnectionToIed(type: Element, ied: Element): boolean { function addEnumType( ied: Element, enumType: Element, - doc: Document + parent: Element ): SimpleAction | undefined { - const existEnumType = doc.querySelector( - `:root > DataTypeTemplates > EnumType[id="${enumType.getAttribute('id')}"]` + const existEnumType = parent.querySelector( + `EnumType[id="${enumType.getAttribute('id')}"]` ); if (existEnumType && enumType.isEqualNode(existEnumType)) return; @@ -204,7 +204,7 @@ function addEnumType( return { new: { - parent: doc.querySelector(':root > DataTypeTemplates')!, + parent, element: enumType, }, }; @@ -213,10 +213,10 @@ function addEnumType( function addDAType( ied: Element, daType: Element, - doc: Document + parent: Element ): SimpleAction | undefined { - const existDAType = doc.querySelector( - `:root > DataTypeTemplates > DAType[id="${daType.getAttribute('id')}"]` + const existDAType = parent.querySelector( + `DAType[id="${daType.getAttribute('id')}"]` ); if (existDAType && daType.isEqualNode(existDAType)) return; @@ -239,7 +239,7 @@ function addDAType( return { new: { - parent: doc.querySelector(':root > DataTypeTemplates')!, + parent, element: daType, }, }; @@ -248,10 +248,10 @@ function addDAType( function addDOType( ied: Element, doType: Element, - doc: Document + parent: Element ): SimpleAction | undefined { - const existDOType = doc.querySelector( - `:root > DataTypeTemplates > DOType[id="${doType.getAttribute('id')}"]` + const existDOType = parent.querySelector( + `DOType[id="${doType.getAttribute('id')}"]` ); if (existDOType && doType.isEqualNode(existDOType)) return; @@ -274,7 +274,7 @@ function addDOType( return { new: { - parent: doc.querySelector(':root > DataTypeTemplates')!, + parent, element: doType, }, }; @@ -283,12 +283,10 @@ function addDOType( function addLNodeType( ied: Element, lNodeType: Element, - doc: Document + parent: Element ): SimpleAction | undefined { - const existLNodeType = doc.querySelector( - `:root > DataTypeTemplates > LNodeType[id="${lNodeType.getAttribute( - 'id' - )}"]` + const existLNodeType = parent.querySelector( + `LNodeType[id="${lNodeType.getAttribute('id')}"]` ); if (existLNodeType && lNodeType.isEqualNode(existLNodeType)) return; @@ -310,7 +308,7 @@ function addLNodeType( return { new: { - parent: doc.querySelector(':root > DataTypeTemplates')!, + parent, element: lNodeType, }, }; @@ -319,21 +317,42 @@ function addLNodeType( function addDataTypeTemplates(ied: Element, doc: XMLDocument): SimpleAction[] { const actions: (SimpleAction | undefined)[] = []; + const dataTypeTemplates = doc.querySelector(':root > DataTypeTemplates') + ? doc.querySelector(':root > DataTypeTemplates')! + : createElement(doc, 'DataTypeTemplates', {}); + + if (!dataTypeTemplates.parentElement) { + actions.push({ + new: { + parent: doc.querySelector('SCL')!, + element: dataTypeTemplates, + }, + }); + } + ied.ownerDocument .querySelectorAll(':root > DataTypeTemplates > LNodeType') - .forEach(lNodeType => actions.push(addLNodeType(ied, lNodeType, doc))); + .forEach(lNodeType => + actions.push(addLNodeType(ied, lNodeType, dataTypeTemplates!)) + ); ied.ownerDocument .querySelectorAll(':root > DataTypeTemplates > DOType') - .forEach(doType => actions.push(addDOType(ied, doType, doc))); + .forEach(doType => + actions.push(addDOType(ied, doType, dataTypeTemplates!)) + ); ied.ownerDocument .querySelectorAll(':root > DataTypeTemplates > DAType') - .forEach(daType => actions.push(addDAType(ied, daType, doc))); + .forEach(daType => + actions.push(addDAType(ied, daType, dataTypeTemplates!)) + ); ied.ownerDocument .querySelectorAll(':root > DataTypeTemplates > EnumType') - .forEach(enumType => actions.push(addEnumType(ied, enumType, doc))); + .forEach(enumType => + actions.push(addEnumType(ied, enumType, dataTypeTemplates!)) + ); return actions.filter(item => item !== undefined); } @@ -459,19 +478,6 @@ export default class ImportingIedPlugin extends LitElement { return; } - if (!doc.querySelector(':root > DataTypeTemplates')) { - const element = createElement(doc, 'DataTypeTemplates', {}); - - this.parent.dispatchEvent( - newActionEvent({ - new: { - parent: doc.documentElement, - element, - }, - }) - ); - } - if (ieds.length === 1) { importIED(ieds[0], doc, this.parent); return; diff --git a/test/integration/editors/triggered/ImportIedsPlugin.test.ts b/test/integration/editors/triggered/ImportIedsPlugin.test.ts index 67631a1bc..a11d00754 100644 --- a/test/integration/editors/triggered/ImportIedsPlugin.test.ts +++ b/test/integration/editors/triggered/ImportIedsPlugin.test.ts @@ -13,6 +13,92 @@ import ImportingIedPlugin from '../../../../src/menu/ImportIEDs.js'; describe('ImportIedsPlugin', () => { customElements.define('import-ieds-plugin', ImportingIedPlugin); + describe('imports valid ied elements to empty projects', () => { + let doc: XMLDocument; + let importDoc: XMLDocument; + + let parent: MockWizardEditor; + let element: ImportingIedPlugin; + + beforeEach(async () => { + parent = await fixture( + html`` + ); + + element = parent.querySelector('import-ieds-plugin')!; + + doc = await fetch('/test/testfiles/importieds/emptyproject.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element.doc = doc; + await element.updateComplete; + + importDoc = await fetch('/test/testfiles/importieds/valid.iid') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + it('loads ied element to the project', async () => { + expect(element.doc?.querySelector(':root > IED[name="TestImportIED"]')).to + .not.exist; + element.prepareImport(importDoc, doc); + await element.updateComplete; + expect(element.doc?.querySelector(':root > IED[name="TestImportIED"]')).to + .exist; + }); + + it('adds the connectedap of the imported ied', async () => { + element.prepareImport(importDoc, doc); + await element.updateComplete; + expect( + element.doc.querySelector( + 'SubNetwork[name="NewSubNetwork"] > ConnectedAP[iedName="TestImportIED"]' + ) + ).to.exist; + }); + + it('creates new subnetwork if not present in the doc', () => { + expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to + .not.exist; + element.prepareImport(importDoc, doc); + expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to + .exist; + }); + + it('allows multiple import of TEMPLATE IEDs', async () => { + const templateIED1 = await fetch( + '/test/testfiles/importieds/template.icd' + ) + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element.prepareImport(templateIED1, doc); + + const templateIED2 = await fetch( + '/test/testfiles/importieds/template.icd' + ) + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element.prepareImport(templateIED2, doc); + + expect(element.doc.querySelector('IED[name="TEMPLATE_IED1"]')).to.exist; + expect(element.doc.querySelector('IED[name="TEMPLATE_IED2"]')).to.exist; + }); + + it('loads unique lnodetypes to the project', () => { + expect( + element.doc?.querySelectorAll(':root > DataTypeTemplates > LNodeType') + .length + ).to.equal(0); + element.prepareImport(importDoc, doc); + expect( + element.doc?.querySelectorAll(':root > DataTypeTemplates > LNodeType') + .length + ).to.equal(5); + }); + }); + describe('imports valid ied elements', () => { let doc: XMLDocument; let importDoc: XMLDocument; @@ -83,6 +169,7 @@ describe('ImportIedsPlugin', () => { .length ).to.equal(11); }); + it('loads unique enumtypes to the project', () => { expect( element.doc?.querySelectorAll(':root > DataTypeTemplates > EnumType') @@ -94,6 +181,7 @@ describe('ImportIedsPlugin', () => { .length ).to.equal(10); }); + it('adds the connectedap of the imported ied', () => { expect(element.doc.querySelector('ConnectedAP[iedName="TestImportIED"]')) .to.not.exist; @@ -105,6 +193,7 @@ describe('ImportIedsPlugin', () => { ?.parentElement ).to.equal(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')); }); + it('creates new subnetwork if not present in the doc', () => { expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to .not.exist; @@ -112,6 +201,7 @@ describe('ImportIedsPlugin', () => { expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to .exist; }); + it('allows multiple import of TEMPLATE IEDs', async () => { expect(element.doc.querySelectorAll('IED').length).to.equal(3); @@ -132,6 +222,7 @@ describe('ImportIedsPlugin', () => { expect(element.doc.querySelector('IED[name="TEMPLATE_IED1"]')).to.exist; expect(element.doc.querySelector('IED[name="TEMPLATE_IED2"]')).to.exist; }); + it('renders wizard for files containing more than one IED', async () => { const multipleIedDoc = await fetch( '/test/testfiles/importieds/multipleied.scd' @@ -147,6 +238,7 @@ describe('ImportIedsPlugin', () => { parent.wizardUI.dialog?.querySelectorAll('mwc-check-list-item').length ).to.equal(3); }); + it('imports selected IED from Import IED wizard', async () => { const multipleIedDoc = await fetch( '/test/testfiles/importieds/multipleied.scd' diff --git a/test/testfiles/importieds/emptyproject.scd b/test/testfiles/importieds/emptyproject.scd new file mode 100644 index 000000000..bb86b29f3 --- /dev/null +++ b/test/testfiles/importieds/emptyproject.scd @@ -0,0 +1,4 @@ + + +
+