Skip to content
This repository was archived by the owner on Aug 8, 2025. It is now read-only.

Commit 30a1f83

Browse files
committed
test: add unit tests (WIP)
1 parent cc093a9 commit 30a1f83

File tree

6 files changed

+1129
-61
lines changed

6 files changed

+1129
-61
lines changed

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<title>oscd-template-generator demo</title>
22
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300&family=Roboto:wght@300;400;500&display=swap">
33
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons&display=block">
4-
<open-scd plugins='{"editor": [{"name": "Template Generator", "translations": {"de": "Template Generator"}, "icon": "add_box", "active": true, "src": "/out-tsc/oscd-template-generator.js"}, {"name": "Template Editor", "translations": {"de": "Template Editor"}, "icon": "edit", "active": true, "src": "https://openscd.github.io/src/editors/Templates.js"}], "menu": [{"name": "Open File", "translations": {"de": "Datei öffnen"}, "icon": "folder_open", "active": true, "src": "https://openscd.github.io/oscd-open/oscd-open.js"}, {"name": "Save File", "translations": {"de": "Datei öffnen"}, "icon": "save", "active": true, "src": "https://openscd.github.io/oscd-save/oscd-save.js"}]}'></open-scd>
4+
<open-scd plugins='{"editor": [{"name": "Template Generator", "translations": {"de": "Template Generator"}, "icon": "add_box", "active": true, "src": "/dist/oscd-template-generator.js"}, {"name": "Template Editor", "translations": {"de": "Template Editor"}, "icon": "edit", "active": true, "src": "https://openscd.github.io/src/editors/Templates.js"}], "menu": [{"name": "Open File", "translations": {"de": "Datei öffnen"}, "icon": "folder_open", "active": true, "src": "https://openscd.github.io/oscd-open/oscd-open.js"}, {"name": "Save File", "translations": {"de": "Datei öffnen"}, "icon": "save", "active": true, "src": "https://openscd.github.io/oscd-save/oscd-save.js"}]}'></open-scd>
55

66
<script type="module">
77
import '@openscd/open-scd-core/open-scd.js';

oscd-template-generator.spec.ts

Lines changed: 133 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,145 @@
11
import { html } from 'lit';
22
import { fixture, expect } from '@open-wc/testing';
3+
import { restore, fake, SinonSpy } from 'sinon';
4+
5+
import { ListItem } from '@material/mwc-list/mwc-list-item.js';
36

47
import TemplateGenerator from './oscd-template-generator.js';
58

6-
customElements.define('oscd-template-generator', TemplateGenerator);
9+
customElements.define('template-generator', TemplateGenerator);
10+
11+
export const sclDocString = `<?xml version="1.0" encoding="UTF-8"?>
12+
<SCL version="2007" revision="B" xmlns="http://www.iec.ch/61850/2003/SCL">
13+
<DataTypeTemplates></DataTypeTemplates>
14+
</SCL>`;
715

816
describe('TemplateGenerator', () => {
917
let element: TemplateGenerator;
1018
beforeEach(async () => {
11-
element = await fixture(
12-
html`<oscd-template-generator></oscd-template-generator>`
13-
);
19+
element = await fixture(html`<template-generator></template-generator>`);
20+
});
21+
22+
it('displays no action button', () =>
23+
expect(element.shadowRoot?.querySelector('mwc-fab')).to.not.exist);
24+
25+
it('starts with LPHD selected', () => {
26+
expect(element).to.have.property('lNodeType', 'LPHD');
27+
expect(element).shadowDom.to.equalSnapshot();
1428
});
1529

16-
it('exists', () => expect(element).to.exist);
30+
describe('given a loaded document', () => {
31+
let listener: SinonSpy;
32+
afterEach(restore);
33+
beforeEach(async () => {
34+
listener = fake();
35+
element.addEventListener('oscd-edit', listener);
36+
element.doc = new DOMParser().parseFromString(
37+
sclDocString,
38+
'application/xml'
39+
);
40+
await element.updateComplete;
41+
});
42+
43+
it('displays an action button', () =>
44+
expect(element.shadowRoot?.querySelector('mwc-fab')).to.exist);
45+
46+
it('adds Templates on action button click', () => {
47+
element.shadowRoot?.querySelector('mwc-fab')?.click();
48+
49+
/* expect five calls for
50+
- LPHD and its mandatory DOTypes
51+
- PhyHealth and its mandatory EnumType
52+
- stVal
53+
- PhyNam
54+
- Proxy
55+
*/
56+
expect(listener).property('args').to.have.lengthOf(5);
57+
listener.args.forEach(args => {
58+
expect(args[0])
59+
.property('detail')
60+
.to.have.property(
61+
'parent',
62+
element.doc?.querySelector('DataTypeTemplates')
63+
);
64+
expect(args[0]).property('detail').to.have.property('node');
65+
});
66+
});
67+
68+
it('adds missing DataTypeTemplates section on action button click', () => {
69+
element.doc?.querySelector('DataTypeTemplates')?.remove();
70+
element.shadowRoot?.querySelector('mwc-fab')?.click();
71+
72+
// expect one more call for the DTT section
73+
expect(listener).property('args').to.have.lengthOf(6);
74+
expect(listener.args[0][0])
75+
.property('detail')
76+
.to.have.property('parent', element.doc?.documentElement);
77+
expect(listener.args[0][0])
78+
.property('detail')
79+
.property('node')
80+
.to.have.property('tagName', 'DataTypeTemplates');
81+
});
82+
83+
it('adds LNodeTypes, DOTypes, DATypes, and EnumTypes as requested', async () => {
84+
element.lNodeType = 'LLN0';
85+
await element.lNodeTypeUI?.updateComplete;
86+
await element.updateComplete;
87+
88+
async function selectAll(column: number) {
89+
const item = element.treeUI.shadowRoot?.querySelector<ListItem>(
90+
`mwc-list:nth-of-type(${column + 1}) > mwc-list-item:first-of-type`
91+
);
92+
item?.click();
93+
await element.treeUI.updateComplete;
94+
await element.updateComplete;
95+
}
96+
97+
await selectAll(1);
98+
await selectAll(2);
99+
await selectAll(3);
100+
await selectAll(4);
101+
await selectAll(5);
102+
103+
element.shadowRoot?.querySelector('mwc-fab')?.click();
104+
105+
/* expect 30 calls for
106+
LNodeType LLN0
107+
DOType Beh
108+
Diag
109+
GrRef
110+
Health
111+
InRef
112+
LEDRs
113+
Loc
114+
LocKey
115+
LocSta
116+
MltLev
117+
Mod
118+
NamPlt
119+
SwModKey
120+
DAType origin
121+
pulseConfig
122+
SBOw
123+
Oper
124+
Cancel
125+
SBOw
126+
Oper
127+
Cancel
128+
EnumType stVal
129+
subVal
130+
orCat
131+
cmdQual
132+
ctlModel
133+
sboClass
134+
stVal
135+
subVal
136+
*/
137+
expect(listener).property('args').to.have.lengthOf(30);
138+
const elms = listener.args.map(args => args[0].detail.node);
139+
expect(elms.filter(e => e.tagName === 'LNodeType')).to.have.lengthOf(1);
140+
expect(elms.filter(e => e.tagName === 'DOType')).to.have.lengthOf(13);
141+
expect(elms.filter(e => e.tagName === 'DAType')).to.have.lengthOf(8);
142+
expect(elms.filter(e => e.tagName === 'EnumType')).to.have.lengthOf(8);
143+
}).timeout(10000); // selecting 550 paths for a full LLN0 is rather slow.
144+
});
17145
});

oscd-template-generator.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,17 @@ const dataTypeTemplates = new DOMParser()
4444
)
4545
.querySelector('DataTypeTemplates')!;
4646

47-
function getDTTReference(parent: Element, tag: string) {
48-
const children = Array.from(parent.children);
47+
const tags = ['LNodeType', 'DOType', 'DAType', 'EnumType'] as const;
48+
type Tag = (typeof tags)[number];
4949

50-
const sequence = ['LNodeType', 'DOType', 'DAType', 'EnumType'];
51-
let index = sequence.findIndex(element => element === tag);
50+
function getDTTReference(parent: Element, tag: Tag) {
51+
const children = Array.from(parent.children);
5252

53-
if (index < 0) return null;
53+
let index = tags.findIndex(element => element === tag);
5454

5555
let nextSibling;
56-
while (index < sequence.length && !nextSibling) {
57-
nextSibling = children.find(child => child.tagName === sequence[index]);
56+
while (index < tags.length && !nextSibling) {
57+
nextSibling = children.find(child => child.tagName === tags[index]);
5858
index++;
5959
}
6060

@@ -144,7 +144,7 @@ export default class TemplateGenerator extends LitElement {
144144

145145
[...LNodeType, ...DOType, ...DAType, ...EnumType].forEach(element => {
146146
if (!this.doc?.querySelector(`${element.tagName}[id="${element.id}"]`)) {
147-
const reference = getDTTReference(templates, element.tagName);
147+
const reference = getDTTReference(templates, element.tagName as Tag);
148148
this.dispatchEvent(
149149
newEditEvent({ parent: templates, node: element, reference })
150150
);

0 commit comments

Comments
 (0)