Skip to content

Commit a58f305

Browse files
author
Rob Tjalma
authored
feat(plugins/SampledValues): Small bug fix + Added Integration tests
1 parent edc133c commit a58f305

File tree

4 files changed

+1311
-29
lines changed

4 files changed

+1311
-29
lines changed

src/editors/sampledvalues/subscriber-ied-list.ts

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
Create,
1919
createElement,
2020
Delete,
21+
identity,
2122
newActionEvent,
23+
selector,
2224
} from '../../foundation.js';
2325
import {
2426
SampledValuesSelectEvent,
@@ -50,6 +52,21 @@ const fcdaReferences = [
5052
'daName',
5153
];
5254

55+
/**
56+
* Get all the FCDA attributes containing values from a specific element.
57+
* @param elementContainingFcdaReferences - The element to use
58+
* @returns FCDA references
59+
*/
60+
function getFcdaReferences(elementContainingFcdaReferences: Element): string {
61+
return fcdaReferences
62+
.map(fcdaRef =>
63+
elementContainingFcdaReferences.getAttribute(fcdaRef)
64+
? `[${fcdaRef}="${elementContainingFcdaReferences.getAttribute(fcdaRef)}"]`
65+
: ''
66+
)
67+
.join('');
68+
}
69+
5370
/**
5471
* Internal persistent state, so it's not lost when
5572
* subscribing / unsubscribing.
@@ -92,13 +109,13 @@ export class SubscriberIEDList extends LitElement {
92109
this.onSampledValuesDataSetEvent = this.onSampledValuesDataSetEvent.bind(this);
93110
this.onIEDSubscriptionEvent = this.onIEDSubscriptionEvent.bind(this);
94111

95-
const openScdElement = document.querySelector('open-scd');
96-
if (openScdElement) {
97-
openScdElement.addEventListener(
112+
const parentDiv = this.closest('div[id="containerTemplates"]');
113+
if (parentDiv) {
114+
parentDiv.addEventListener(
98115
'sampled-values-select',
99116
this.onSampledValuesDataSetEvent
100117
);
101-
openScdElement.addEventListener(
118+
parentDiv.addEventListener(
102119
'ied-smv-subscription',
103120
this.onIEDSubscriptionEvent
104121
);
@@ -143,13 +160,7 @@ export class SubscriberIEDList extends LitElement {
143160
if (
144161
inputs.querySelector(
145162
`ExtRef[iedName=${localState.currentSampledValuesIEDName}]` +
146-
`${fcdaReferences
147-
.map(fcdaRef =>
148-
fcda.getAttribute(fcdaRef)
149-
? `[${fcdaRef}="${fcda.getAttribute(fcdaRef)}"]`
150-
: ''
151-
)
152-
.join('')}`
163+
`${getFcdaReferences(fcda)}`
153164
)
154165
) {
155166
numberOfLinkedExtRefs++;
@@ -167,7 +178,7 @@ export class SubscriberIEDList extends LitElement {
167178
}
168179

169180
if (
170-
numberOfLinkedExtRefs ==
181+
numberOfLinkedExtRefs >=
171182
localState.currentDataset!.querySelectorAll('FCDA').length
172183
) {
173184
localState.subscribedIeds.push({ element: ied });
@@ -216,13 +227,7 @@ export class SubscriberIEDList extends LitElement {
216227
if (
217228
!inputsElement!.querySelector(
218229
`ExtRef[iedName=${localState.currentSampledValuesIEDName}]` +
219-
`${fcdaReferences
220-
.map(fcdaRef =>
221-
fcda.getAttribute(fcdaRef)
222-
? `[${fcdaRef}="${fcda.getAttribute(fcdaRef)}"]`
223-
: ''
224-
)
225-
.join('')}`
230+
`${getFcdaReferences(fcda)}`
226231
)
227232
) {
228233
const extRef = createElement(ied.ownerDocument, 'ExtRef', {
@@ -271,13 +276,7 @@ export class SubscriberIEDList extends LitElement {
271276
localState.currentDataset!.querySelectorAll('FCDA').forEach(fcda => {
272277
const extRef = inputs.querySelector(
273278
`ExtRef[iedName=${localState.currentSampledValuesIEDName}]` +
274-
`${fcdaReferences
275-
.map(fcdaRef =>
276-
fcda.getAttribute(fcdaRef)
277-
? `[${fcdaRef}="${fcda.getAttribute(fcdaRef)}"]`
278-
: ''
279-
)
280-
.join('')}`
279+
`${getFcdaReferences(fcda)}`
281280
);
282281

283282
if (extRef) actions.push({ old: { parent: inputs, element: extRef } });
@@ -287,7 +286,7 @@ export class SubscriberIEDList extends LitElement {
287286
this.dispatchEvent(
288287
newActionEvent({
289288
title: 'Disconnect',
290-
actions,
289+
actions: this.extendDeleteActions(actions),
291290
})
292291
);
293292

@@ -299,6 +298,48 @@ export class SubscriberIEDList extends LitElement {
299298
);
300299
}
301300

301+
/**
302+
* Creating Delete actions in case Inputs elements are empty.
303+
* @param extRefDeleteActions - All Delete actions for ExtRefs.
304+
* @returns Possible delete actions for empty Inputs elements.
305+
*/
306+
private extendDeleteActions(extRefDeleteActions: Delete[]): Delete[] {
307+
if (!extRefDeleteActions.length) return [];
308+
309+
// Initialize with the already existing ExtRef Delete actions.
310+
const extendedDeleteActions: Delete[] = extRefDeleteActions;
311+
const inputsMap: Record<string, Element> = {};
312+
313+
for (const extRefDeleteAction of extRefDeleteActions) {
314+
const extRef = <Element>extRefDeleteAction.old.element;
315+
const inputsElement = <Element>extRefDeleteAction.old.parent;
316+
317+
const id = identity(inputsElement);
318+
if (!inputsMap[id]) inputsMap[id] = <Element>(inputsElement.cloneNode(true));
319+
320+
const linkedExtRef = inputsMap[id].querySelector(`ExtRef[iedName=${extRef.getAttribute('iedName')}]` +
321+
`${getFcdaReferences(extRef)}`);
322+
323+
if (linkedExtRef) inputsMap[id].removeChild(linkedExtRef);
324+
}
325+
326+
// create delete action for each empty inputs
327+
Object.entries(inputsMap).forEach(([key, value]) => {
328+
if (value.children.length ! == 0) {
329+
const doc = extRefDeleteActions[0].old.parent.ownerDocument!;
330+
const inputs = doc.querySelector(selector('Inputs', key));
331+
332+
if (inputs && inputs.parentElement) {
333+
extendedDeleteActions.push({
334+
old: { parent: inputs.parentElement, element: inputs },
335+
});
336+
}
337+
}
338+
});
339+
340+
return extendedDeleteActions;
341+
}
342+
302343
protected updated(): void {
303344
if (this.subscriberWrapper) {
304345
this.subscriberWrapper.scrollTo(0, 0);
@@ -309,6 +350,9 @@ export class SubscriberIEDList extends LitElement {
309350
const partialSubscribedIeds = localState.availableIeds.filter(
310351
ied => ied.partial
311352
);
353+
const availableIeds = localState.availableIeds.filter(
354+
ied => !ied.partial
355+
);
312356
const smvControlName =
313357
localState.currentSampledValuesControl?.getAttribute('name') ?? undefined;
314358

@@ -372,8 +416,8 @@ export class SubscriberIEDList extends LitElement {
372416
>
373417
</mwc-list-item>
374418
<li divider role="separator"></li>
375-
${localState.availableIeds.length > 0
376-
? localState.availableIeds.map(
419+
${availableIeds.length > 0
420+
? availableIeds.map(
377421
ied =>
378422
html`<ied-element
379423
.status=${SubscribeStatus.None}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { html, fixture, expect } from '@open-wc/testing';
2+
3+
import '../../../mock-wizard.js';
4+
5+
import SampledValues from '../../../../src/editors/SampledValues.js';
6+
import { Editing } from '../../../../src/Editing.js';
7+
import { Wizarding } from '../../../../src/Wizarding.js';
8+
9+
describe('Sampled Values Plugin', () => {
10+
customElements.define('smv-plugin', Wizarding(Editing(SampledValues)));
11+
let element: SampledValues;
12+
let doc: XMLDocument;
13+
14+
beforeEach(async () => {
15+
doc = await fetch('/test/testfiles/valid2007B4ForSampledValues.scd')
16+
.then(response => response.text())
17+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
18+
19+
element = await fixture(html`<smv-plugin .doc=${doc} ></smv-plugin>`);
20+
});
21+
22+
describe('initially', () => {
23+
it('the Sampled Values list looks like the latest snapshot', async () => {
24+
await expect(element.shadowRoot?.querySelector('sampled-values-list')).shadowDom.to.equalSnapshot();
25+
});
26+
27+
it('the IED list looks like the latest snapshot', async () => {
28+
await expect(element.shadowRoot?.querySelector('subscriber-ied-list')).shadowDom.to.equalSnapshot();
29+
});
30+
});
31+
32+
describe('when selecting a Sampled Values message', () => {
33+
beforeEach(async () => {
34+
const smvMsg = element.shadowRoot?.querySelector('sampled-values-list')
35+
?.shadowRoot?.querySelectorAll('sampled-values-message')[0].shadowRoot?.querySelector('mwc-list-item');
36+
37+
(<HTMLElement>(smvMsg)).click();
38+
});
39+
40+
it('the list on the right will initially show the subscribed / partially subscribed / not subscribed IEDs', async () => {
41+
await element.updateComplete;
42+
expect(element.shadowRoot?.querySelector('subscriber-ied-list')).shadowDom.to.equalSnapshot();
43+
});
44+
45+
describe('and you subscribe a non-subscribed IED', () => {
46+
it('it looks like the latest snapshot', async () => {
47+
const ied = element.shadowRoot?.querySelector('subscriber-ied-list')
48+
?.shadowRoot?.querySelectorAll('ied-element')[2].shadowRoot?.querySelector('mwc-list-item');
49+
50+
(<HTMLElement>(ied)).click();
51+
await element.updateComplete;
52+
expect(element.shadowRoot?.querySelector('subscriber-ied-list')).shadowDom.to.equalSnapshot();
53+
});
54+
});
55+
56+
describe('and you unsubscribe a subscribed IED', () => {
57+
it('it looks like the latest snapshot', async () => {
58+
const ied = element.shadowRoot?.querySelector('subscriber-ied-list')
59+
?.shadowRoot?.querySelectorAll('ied-element')[0].shadowRoot?.querySelector('mwc-list-item');
60+
61+
(<HTMLElement>(ied)).click();
62+
await element.updateComplete;
63+
expect(element.shadowRoot?.querySelector('subscriber-ied-list')).shadowDom.to.equalSnapshot();
64+
});
65+
});
66+
67+
describe('and you subscribe a partially subscribed IED', () => {
68+
it('it looks like the latest snapshot', async () => {
69+
const ied = element.shadowRoot?.querySelector('subscriber-ied-list')
70+
?.shadowRoot?.querySelectorAll('ied-element')[1].shadowRoot?.querySelector('mwc-list-item');
71+
72+
(<HTMLElement>(ied)).click();
73+
await element.updateComplete;
74+
expect(element.shadowRoot?.querySelector('subscriber-ied-list')).shadowDom.to.equalSnapshot();
75+
});
76+
});
77+
});
78+
});

0 commit comments

Comments
 (0)