@@ -5,7 +5,7 @@ import { isRemove, isUpdate } from "@openscd/oscd-api/utils.js";
55
66import { handleEdit } from "../foundation/helpers.test.js" ;
77
8- import { scl } from "./removeIED.testfile.js" ;
8+ import { scl , sclDuplicateLNodes } from "./removeIED.testfile.js" ;
99
1010import { removeIED } from "./removeIED.js" ;
1111
@@ -47,10 +47,10 @@ describe("Function to an remove the IED and its referenced elements", () => {
4747 expect ( removeIED ( { node : publi } ) . length ) . to . equal ( 0 ) ;
4848 } ) ;
4949
50- it ( "updates LNode iedName attributes to None as well " , ( ) => {
50+ it ( "removes all bound LNodes " , ( ) => {
5151 const edits = removeIED ( { node : subscriber1 } ) ;
5252
53- expect ( numberUpdates ( edits , "LNode" ) ) . to . equal ( 1 ) ;
53+ expect ( numberRemoves ( edits , "LNode" ) ) . to . equal ( 1 ) ;
5454 } ) ;
5555
5656 it ( "removes ConnectedAPs as well" , ( ) => {
@@ -126,4 +126,136 @@ describe("Function to an remove the IED and its referenced elements", () => {
126126 // 1 supervised control block is not subscribed so is not removed
127127 expect ( after . length ) . to . equal ( 1 ) ;
128128 } ) ;
129+
130+ describe ( "referenced LNode's" , ( ) => {
131+ /*
132+ * Here we need to test:
133+ * - Delete all LNode references found with matching iedName, BUT only inside the substation section (but not inside Private sections).
134+ * - Find all LNode references with matching iedName and either set them to None, or delete them if setting them to None would result in duplicate LNode keys within the same scope.
135+ * The scope is defined as the nearest Bay, VL or Substation parent.
136+ */
137+ describe ( "without 'preservveNodes' set (default)" , ( ) => {
138+ //TODO consider changing this into a forEach (Substation, VL and Bay) array test.
139+ [ "Bay" , "VoltageLevel" , "Substation" ] . forEach ( ( scope ) => {
140+ it ( `deletes all LNodes found directly within a ${ scope } ` , ( ) => {
141+ const sclDom = new DOMParser ( ) . parseFromString (
142+ sclDuplicateLNodes ,
143+ "application/xml" ,
144+ ) ;
145+ const iedA = sclDom . querySelector ( 'IED[name="IED_A"]' ) ! ;
146+ const beforeSpec_LNodeCount = (
147+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='None']` ) ?? [ ]
148+ ) . length ;
149+
150+ const edits = removeIED ( { node : iedA } ) ;
151+ handleEdit ( edits ) ;
152+ const after_iedA_lNodes = Array . from (
153+ sclDom . querySelectorAll ( `${ scope } LNode[iedName="IED_A"]` ) ,
154+ ) . length ;
155+ const after_spec_LNodeCount = (
156+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='None']` ) ?? [ ]
157+ ) . length ;
158+ expect ( after_iedA_lNodes ) . to . equal ( 0 ) ;
159+ // The number of LNodes set to None should not have changed.
160+ expect ( after_spec_LNodeCount ) . to . equal ( beforeSpec_LNodeCount ) ;
161+
162+ //
163+ } ) ;
164+ } ) ;
165+ } ) ;
166+
167+ describe . only ( "with preserveLNodes set" , ( ) => {
168+ // Broke this into 3 separate tests, so the scope of the failure "might" be narrower.
169+ // Do keep in mind however, the subject SCL has 2 of everything. E.g. S1 & S2
170+ [ "Bay" , "VoltageLevel" , "Substation" ] . forEach ( ( scope ) => {
171+ it ( `Within a ${ scope } , it sets all bound LNodes to None` , ( ) => {
172+ //we're using the "duplicates" test file, but by only deleting 1 IED, no duplicates occur (yet).
173+ const sclDom = new DOMParser ( ) . parseFromString (
174+ sclDuplicateLNodes ,
175+ "application/xml" ,
176+ ) ;
177+ const iedA = sclDom . querySelector ( 'IED[name="IED_A"]' ) ! ;
178+ const beforeSpec_LNodeCount = (
179+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='None']` ) ?? [ ]
180+ ) . length ;
181+ const beforeIedA_LNodeCount = (
182+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='IED_A']` ) ?? [ ]
183+ ) . length ;
184+
185+ const edits = removeIED ( { node : iedA } , { preserveLNodes : true } ) ;
186+ handleEdit ( edits ) ;
187+ const lNodes = Array . from (
188+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName="None"]` ) ,
189+ ) ;
190+ expect ( lNodes . length ) . to . equal (
191+ beforeSpec_LNodeCount + beforeIedA_LNodeCount ,
192+ ) ;
193+ //
194+ } ) ;
195+ } ) ;
196+
197+ [ "Bay" , "VoltageLevel" , "Substation" ] . forEach ( ( scope ) => {
198+ it ( `Within a ${ scope } , it removes 'would-be' duplicates` , ( ) => {
199+ //we're using the "duplicates" test file, but by only deleting 1 IED, no duplicates occur.
200+ const sclDom = new DOMParser ( ) . parseFromString (
201+ sclDuplicateLNodes ,
202+ "application/xml" ,
203+ ) ;
204+ const iedA = sclDom . querySelector ( 'IED[name="IED_A"]' ) ! ;
205+ const iedB = sclDom . querySelector ( 'IED[name="IED_B"]' ) ! ;
206+
207+ handleEdit ( removeIED ( { node : iedA } , { preserveLNodes : true } ) ) ;
208+ const beforeSpec_LNodeCount = (
209+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='None']` ) ?? [ ]
210+ ) . length ;
211+ // After the first wave of deletions the SCL already has LNodes(iedName=None),
212+ // which exactly match the LNodes we're about to remove.
213+ // So when IED_B is removed (with preserveLNodes set), the LNodes(None)
214+ // should not have changed.
215+ handleEdit ( removeIED ( { node : iedB } , { preserveLNodes : true } ) ) ;
216+ const iedB_lNodesCount = Array . from (
217+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName="IED_B"]` ) ,
218+ ) . length ;
219+ expect ( iedB_lNodesCount ) . to . equal ( 0 ) ;
220+ // Although we've removed IED_A and IED_B, the count should remain unchanged after
221+ // removing IED_A, because both IED's are bound exactly the same.
222+ const afterSpec_LNodeCount = (
223+ sclDom . querySelectorAll ( `${ scope } > LNode[iedName='None']` ) ?? [ ]
224+ ) . length ;
225+ expect ( afterSpec_LNodeCount ) . to . equal ( beforeSpec_LNodeCount ) ;
226+ } ) ;
227+ } ) ;
228+
229+ it ( "does not create duplicate LNode keys when removing both IEDs" , ( ) => {
230+ const sclDom = new DOMParser ( ) . parseFromString (
231+ sclDuplicateLNodes ,
232+ "application/xml" ,
233+ ) ;
234+ const iedA = sclDom . querySelector ( 'IED[name="IED_A"]' ) ! ;
235+ const iedB = sclDom . querySelector ( 'IED[name="IED_B"]' ) ! ;
236+
237+ handleEdit ( removeIED ( { node : iedA } ) ) ;
238+ handleEdit ( removeIED ( { node : iedB } ) ) ;
239+
240+ const ce = sclDom . querySelector ( 'ConductingEquipment[name="QA1"]' ) ! ;
241+ const lNodes = Array . from ( ce . querySelectorAll ( ":scope > LNode" ) ) ;
242+ const keys = lNodes . map (
243+ ( ln ) =>
244+ `${ ln . getAttribute ( "ldInst" ) } |${ ln . getAttribute (
245+ "lnClass" ,
246+ ) } |${ ln . getAttribute ( "lnInst" ) } |${ ln . getAttribute (
247+ "prefix" ,
248+ ) } |${ ln . getAttribute ( "iedName" ) } `,
249+ ) ;
250+ const uniqueKeys = new Set ( keys ) ;
251+
252+ expect ( keys . length ) . to . equal (
253+ uniqueKeys . size ,
254+ `Duplicate LNode keys found: ${ keys
255+ . filter ( ( k , i ) => keys . indexOf ( k ) !== i )
256+ . join ( ", " ) } `,
257+ ) ;
258+ } ) ;
259+ } ) ;
260+ } ) ;
129261} ) ;
0 commit comments