Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.

Commit d48c201

Browse files
committed
add unit tests
1 parent 61e6aab commit d48c201

9 files changed

Lines changed: 337 additions & 4 deletions

File tree

packages/plugins/src/editors/ied/add-ldevice-dialog.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ export interface LDeviceData {
2424
/** Dialog for adding a new LDevice to a Server. */
2525
@customElement('add-ldevice-dialog')
2626
export class AddLDeviceDialog extends LitElement {
27-
@property({ type: Boolean })
28-
open = false;
29-
3027
@property()
3128
server!: Element;
3229

packages/plugins/src/editors/ied/add-ln-dialog.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export class AddLnDialog extends LitElement {
148148
label=${translate('iededitor.addLnDialog.amount')}
149149
type="number"
150150
min="1"
151+
data-testid="amount"
151152
.value=${this.amount}
152153
@input=${(e: Event) => {
153154
e.stopPropagation();
@@ -170,6 +171,7 @@ export class AddLnDialog extends LitElement {
170171
slot="primaryAction"
171172
icon="add"
172173
trailingIcon
174+
data-testid="add-ln-button"
173175
@click=${this.handleCreate}
174176
?disabled=${!this.lnType || this.amount < 1 || this.amount % 1 != 0}
175177
>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
3+
<Header id="project"/>
4+
<IED name="test" manufacturer="OpenSCD">
5+
<AccessPoint name="AP1">
6+
<Server>
7+
<Authentication/>
8+
<LDevice inst="LD1">
9+
<LN0 lnClass="LLN0" inst="" lnType="PlaceholderLLN0"/>
10+
</LDevice>
11+
</Server>
12+
</AccessPoint>
13+
<AccessPoint name="AP2">
14+
<ServerAt apName="AP1"/>
15+
</AccessPoint>
16+
</IED>
17+
<DataTypeTemplates>
18+
<LNodeType lnClass="LLN0" id="PlaceholderLLN0">
19+
<DO name="Beh" type="Beh$oscd$_c6ed035c8137b35a"/>
20+
</LNodeType>
21+
<DOType cdc="ENS" id="Beh$oscd$_c6ed035c8137b35a">
22+
<DA name="stVal" fc="ST" dchg="true" dupd="true" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
23+
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
24+
<DA name="t" fc="ST" bType="Timestamp"/>
25+
</DOType>
26+
<EnumType id="stVal$oscd$_48ba16345b8e7f5b">
27+
<EnumVal ord="1">on</EnumVal>
28+
<EnumVal ord="2">blocked</EnumVal>
29+
<EnumVal ord="3">test</EnumVal>
30+
<EnumVal ord="4">test/blocked</EnumVal>
31+
<EnumVal ord="5">off</EnumVal>
32+
</EnumType>
33+
</DataTypeTemplates>
34+
</SCL>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* @web/test-runner snapshot v1 */
2+
export const snapshots = {};
3+
4+
snapshots["add-access-point-dialog looks like the latest snapshot"] =
5+
`<mwc-dialog
6+
heading="[iededitor.addAccessPointDialog.title]"
7+
id="createAccessPointDialog"
8+
open=""
9+
>
10+
<div class="dialog-content">
11+
<mwc-textfield
12+
autovalidate=""
13+
dialoginitialfocus=""
14+
helper="[iededitor.addAccessPointDialog.apName]"
15+
id="apName"
16+
label="[iededitor.addAccessPointDialog.apName]"
17+
maxlength="32"
18+
pattern="[A-Za-z0-9][0-9A-Za-z_]*"
19+
required=""
20+
style="width: 100%; margin-bottom: 16px;"
21+
>
22+
</mwc-textfield>
23+
<mwc-formfield label="[iededitor.addAccessPointDialog.createServerAt]">
24+
<mwc-switch>
25+
</mwc-switch>
26+
</mwc-formfield>
27+
</div>
28+
<mwc-button
29+
slot="secondaryAction"
30+
style="--mdc-theme-primary: var(--mdc-theme-error)"
31+
>
32+
[close]
33+
</mwc-button>
34+
<mwc-button
35+
data-testid="add-access-point-button"
36+
disabled=""
37+
icon="add"
38+
slot="primaryAction"
39+
trailingicon=""
40+
>
41+
[add]
42+
</mwc-button>
43+
</mwc-dialog>
44+
`;
45+
/* end snapshot add-access-point-dialog looks like the latest snapshot */
46+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* @web/test-runner snapshot v1 */
2+
export const snapshots = {};
3+
4+
snapshots["add-ldevice-dialog looks like the latest snapshot"] =
5+
`<mwc-dialog
6+
heading="[iededitor.addLDeviceDialog.title]"
7+
id="addLDeviceDialog"
8+
open=""
9+
>
10+
<div class="dialog-content">
11+
<mwc-textfield
12+
autovalidate=""
13+
dialoginitialfocus=""
14+
helper="[iededitor.addLDeviceDialog.inst]"
15+
label="[iededitor.addLDeviceDialog.inst]"
16+
maxlength="64"
17+
pattern="[A-Za-z0-9][0-9A-Za-z_]*"
18+
required=""
19+
style="width: 100%;"
20+
>
21+
</mwc-textfield>
22+
</div>
23+
<mwc-button
24+
slot="secondaryAction"
25+
style="--mdc-theme-primary: var(--mdc-theme-error)"
26+
>
27+
[close]
28+
</mwc-button>
29+
<mwc-button
30+
icon="add"
31+
slot="primaryAction"
32+
trailingicon=""
33+
>
34+
[add]
35+
</mwc-button>
36+
</mwc-dialog>
37+
`;
38+
/* end snapshot add-ldevice-dialog looks like the latest snapshot */
39+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* @web/test-runner snapshot v1 */
2+
export const snapshots = {};
3+
4+
snapshots["add-ln-dialog looks like the latest snapshot"] =
5+
`<mwc-dialog
6+
heading="[iededitor.addLnDialog.title]"
7+
id="addLnDialog"
8+
open=""
9+
>
10+
<div class="dialog-content">
11+
<div class="ln-list-container">
12+
<mwc-textfield
13+
icon="search"
14+
label="[iededitor.addLnDialog.filter]"
15+
style="margin-bottom: 8px; width: 100%;"
16+
type="text"
17+
>
18+
</mwc-textfield>
19+
<div class="ln-list-scroll">
20+
<mwc-list style="width: 100%;">
21+
<mwc-list-item
22+
aria-disabled="false"
23+
dialogaction="none"
24+
mwc-list-item=""
25+
style="cursor: pointer;"
26+
tabindex="0"
27+
type="button"
28+
value="PlaceholderLLN0"
29+
>
30+
<span class="ln-list-id">
31+
PlaceholderLLN0
32+
</span>
33+
<span class="ln-list-desc">
34+
</span>
35+
</mwc-list-item>
36+
</mwc-list>
37+
</div>
38+
</div>
39+
<mwc-textfield
40+
data-testid="amount"
41+
label="[iededitor.addLnDialog.amount]"
42+
min="1"
43+
style="width: 100%; margin-top: 12px;"
44+
type="number"
45+
>
46+
</mwc-textfield>
47+
</div>
48+
<mwc-button
49+
slot="secondaryAction"
50+
style="--mdc-theme-primary: var(--mdc-theme-error)"
51+
>
52+
[close]
53+
</mwc-button>
54+
<mwc-button
55+
data-testid="add-ln-button"
56+
disabled=""
57+
icon="add"
58+
slot="primaryAction"
59+
trailingicon=""
60+
>
61+
[add]
62+
</mwc-button>
63+
</mwc-dialog>
64+
`;
65+
/* end snapshot add-ln-dialog looks like the latest snapshot */
66+

packages/plugins/test/unit/editors/ied/add-access-point-dialog.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ describe('add-access-point-dialog', () => {
2828
);
2929
});
3030

31-
it('should show and hide dialog', async () => {
31+
it('looks like the latest snapshot', async () => {
32+
element.show();
3233
await element.updateComplete;
34+
expect(element).shadowDom.to.equalSnapshot();
35+
});
36+
37+
it('should show and hide dialog', async () => {
3338
element.show();
3439
await element.updateComplete;
3540
expect(element.dialog.open).to.be.true;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { expect, fixture, html } from '@open-wc/testing';
2+
import { SinonSpy, spy } from 'sinon';
3+
import { AddLDeviceDialog } from '../../../../src/editors/ied/add-ldevice-dialog';
4+
import '../../../../src/editors/ied/add-ldevice-dialog.js';
5+
6+
describe('add-ldevice-dialog', () => {
7+
let element: AddLDeviceDialog;
8+
let doc: XMLDocument;
9+
let server: Element;
10+
let onConfirmSpy: SinonSpy;
11+
12+
beforeEach(async () => {
13+
doc = await fetch('/test/testfiles/editors/minimalVirtualIED.scd')
14+
.then(response => response.text())
15+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
16+
server = doc.querySelector('IED > AccessPoint > Server')!;
17+
onConfirmSpy = spy();
18+
element = await fixture(
19+
html`<add-ldevice-dialog
20+
.server=${server}
21+
.onConfirm=${onConfirmSpy}
22+
></add-ldevice-dialog>`
23+
);
24+
});
25+
26+
it('looks like the latest snapshot', async () => {
27+
element.show();
28+
await element.updateComplete;
29+
expect(element).shadowDom.to.equalSnapshot();
30+
});
31+
32+
it('should show and hide dialog', async () => {
33+
element.show();
34+
await element.updateComplete;
35+
expect(element.dialog.open).to.be.true;
36+
37+
element['close']();
38+
await element.updateComplete;
39+
expect(element.dialog.open).to.be.false;
40+
});
41+
42+
describe('LDevice inst validation', () => {
43+
it('should reject empty names', () => {
44+
expect(element['getInstError']('')).to.equal('');
45+
expect(element['getInstError'](' ')).to.equal('');
46+
});
47+
48+
it('should reject names with wrong pattern', () => {
49+
expect(element['getInstError']('invalid name')).to.equal(
50+
'[iededitor.addLDeviceDialog.instFormatError]'
51+
);
52+
});
53+
54+
it('should reject existing names', () => {
55+
expect(element['getInstError']('LD1')).to.equal(
56+
'[iededitor.addLDeviceDialog.instUniqueError]'
57+
);
58+
});
59+
});
60+
});
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { expect, fixture, html } from '@open-wc/testing';
2+
import { SinonSpy, spy } from 'sinon';
3+
import { AddLnDialog } from '../../../../src/editors/ied/add-ln-dialog';
4+
import '../../../../src/editors/ied/add-ln-dialog.js';
5+
6+
describe('add-ln-dialog', () => {
7+
let element: AddLnDialog;
8+
let doc: XMLDocument;
9+
let onConfirmSpy: SinonSpy;
10+
11+
beforeEach(async () => {
12+
doc = await fetch('/test/testfiles/editors/minimalVirtualIED.scd')
13+
.then(response => response.text())
14+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
15+
onConfirmSpy = spy();
16+
element = await fixture(
17+
html`<add-ln-dialog
18+
.doc=${doc}
19+
.onConfirm=${onConfirmSpy}
20+
></add-ln-dialog>`
21+
);
22+
});
23+
24+
it('looks like the latest snapshot', async () => {
25+
element.show();
26+
await element.updateComplete;
27+
expect(element).shadowDom.to.equalSnapshot();
28+
});
29+
30+
it('should show and hide dialog', async () => {
31+
element.show();
32+
await element.updateComplete;
33+
expect(element.dialog.open).to.be.true;
34+
35+
element['close']();
36+
await element.updateComplete;
37+
expect(element.dialog.open).to.be.false;
38+
});
39+
40+
it('displays filtered LN types', async () => {
41+
element.show();
42+
await element.updateComplete;
43+
expect(element.filterText).to.equal('');
44+
expect(element['filteredLNodeTypes'].length).to.equal(1);
45+
46+
element.filterText = 'NonExistingFilter';
47+
await element.updateComplete;
48+
expect(element['filteredLNodeTypes'].length).to.equal(0);
49+
});
50+
51+
it('should create LN data on confirm', async () => {
52+
element.show();
53+
await element.updateComplete;
54+
const listItems = element.shadowRoot?.querySelectorAll('mwc-list-item');
55+
const targetItem = listItems
56+
? Array.from(listItems).find(item => item.value === 'PlaceholderLLN0')
57+
: undefined;
58+
if (targetItem) {
59+
targetItem.click();
60+
await element.updateComplete;
61+
}
62+
63+
const amountInput = element.shadowRoot?.querySelector(
64+
'[data-testid="amount"]'
65+
);
66+
(amountInput as HTMLInputElement).value = '3';
67+
(amountInput as HTMLInputElement).dispatchEvent(
68+
new Event('input', { bubbles: true, composed: true })
69+
);
70+
await element.updateComplete;
71+
const addButton = element.shadowRoot?.querySelector(
72+
'[data-testid="add-ln-button"]'
73+
);
74+
(addButton as HTMLElement)?.click();
75+
await element.updateComplete;
76+
77+
expect(onConfirmSpy.calledOnce).to.be.true;
78+
expect(onConfirmSpy.firstCall.args[0]).to.deep.include({
79+
lnType: 'PlaceholderLLN0',
80+
lnClass: 'LLN0',
81+
amount: 3,
82+
});
83+
});
84+
});

0 commit comments

Comments
 (0)