Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(setting/nsdoc): Loading NSDoc Files through Custom Event #644

Merged
merged 5 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ export function Logging<TBase extends LitElementConstructor>(Base: TBase) {

this.undo = this.undo.bind(this);
this.redo = this.redo.bind(this);

this.onLog = this.onLog.bind(this);

this.addEventListener('log', this.onLog);
this.addEventListener('issue', this.onIssue);
this.addEventListener('open-doc', this.onLoadHistoryFromDoc);
Expand Down
94 changes: 63 additions & 31 deletions src/Setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ type NsdVersions = {
'IEC 61850-8-1': NsdVersion;
}

/** Represents a document to be opened. */
export interface LoadNsdocDetail {
nsdoc: string;
filename: string;
}
export type LoadNsdocEvent = CustomEvent<LoadNsdocDetail>;
export function newLoadNsdocEvent(
nsdoc: string,
filename: string
): LoadNsdocEvent {
return new CustomEvent<LoadNsdocDetail>('load-nsdoc', {
bubbles: true,
composed: true,
detail: { nsdoc, filename },
});
}

/** Mixin that saves [[`Settings`]] to `localStorage`, reflecting them in the
* `settings` property, setting them through `setSetting(setting, value)`. */
export type SettingElement = Mixin<typeof Setting>;
Expand Down Expand Up @@ -168,7 +185,7 @@ export function Setting<TBase extends LitElementConstructor>(Base: TBase) {
private renderFileSelect(): TemplateResult {
return html `
<input id="nsdoc-file" accept=".nsdoc" type="file" hidden required multiple
@change=${(evt: Event) => this.loadNsdocFile(evt)}}>
@change=${(evt: Event) => this.uploadNsdocFile(evt)}}>
<mwc-button label="${translate('settings.selectFileButton')}"
id="selectFileButton"
@click=${() => {
Expand All @@ -179,50 +196,63 @@ export function Setting<TBase extends LitElementConstructor>(Base: TBase) {
`;
}

private async loadNsdocFile(evt: Event): Promise<void> {
const nsdVersions = await this.nsdVersions();
private async uploadNsdocFile(evt: Event): Promise<void> {
const files = Array.from(
(<HTMLInputElement | null>evt.target)?.files ?? []
);

if (files.length == 0) return;
files.forEach(async file => {
for (const file of files) {
const text = await file.text();
const nsdocElement = this.parseToXmlObject(text).querySelector('NSDoc');
const id = nsdocElement?.getAttribute('id');
if (!id) {
document
document
.querySelector('open-scd')!
.dispatchEvent(
newLogEvent({ kind: 'error', title: get('settings.invalidFileNoIdFound') })
);
return;
}
const nsdVersion = nsdVersions[id as keyof NsdVersions];
const nsdocVersion = {
version: nsdocElement!.getAttribute('version') ?? '',
revision: nsdocElement!.getAttribute('revision') ?? '',
release: nsdocElement!.getAttribute('release') ?? ''
}
newLoadNsdocEvent(text, file.name)
);
}

this.nsdocFileUI.value = '';
this.requestUpdate();
}

private async onLoadNsdoc(event: LoadNsdocEvent) {
const nsdocElement = this.parseToXmlObject(event.detail.nsdoc).querySelector('NSDoc');

if (!this.isEqual(nsdVersion, nsdocVersion)) {
document
const id = nsdocElement?.getAttribute('id');
if (!id) {
document
.querySelector('open-scd')!
.dispatchEvent(
newLogEvent({ kind: 'error', title: get('settings.invalidNsdocVersion', {
newLogEvent({ kind: 'error', title: get('settings.invalidFileNoIdFound', {
filename: event.detail.filename
}) })
);
return;
}

const nsdVersions = await this.nsdVersions();
const nsdVersion = nsdVersions[id as keyof NsdVersions];
const nsdocVersion = {
version: nsdocElement!.getAttribute('version') ?? '',
revision: nsdocElement!.getAttribute('revision') ?? '',
release: nsdocElement!.getAttribute('release') ?? ''
}

if (!this.isEqual(nsdVersion, nsdocVersion)) {
document
.querySelector('open-scd')!
.dispatchEvent(
newLogEvent({ kind: 'error', title: get('settings.invalidNsdocVersion', {
id: id,
filename: event.detail.filename,
nsdVersion: `${nsdVersion.version}${nsdVersion.revision}${nsdVersion.release}`,
nsdocVersion: `${nsdocVersion.version}${nsdocVersion.revision}${nsdocVersion.release}`
}) })
);
return;
}

this.setSetting(id as keyof Settings, text);
})
);
return;
}

this.nsdocFileUI.value = '';
this.requestUpdate();
this.setSetting(id as keyof Settings, event.detail.nsdoc);
}

/**
Expand All @@ -245,7 +275,7 @@ export function Setting<TBase extends LitElementConstructor>(Base: TBase) {
let nsdVersion: string | undefined | null;
let nsdRevision: string | undefined | null;
let nsdRelease: string | undefined | null;

if (nsdSetting) {
const nsdoc = this.parseToXmlObject(nsdSetting)!.querySelector('NSDoc');
nsdVersion = nsdoc?.getAttribute('version');
Expand Down Expand Up @@ -273,6 +303,8 @@ export function Setting<TBase extends LitElementConstructor>(Base: TBase) {

registerTranslateConfig({ loader, empty: key => key });
use(this.settings.language);

(<any>this).addEventListener('load-nsdoc', this.onLoadNsdoc);
}

render(): TemplateResult {
Expand Down
5 changes: 2 additions & 3 deletions src/translations/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ export const de: Translations = {
showieds: 'Zeige IEDs im Substation-Editor',
selectFileButton: 'Datei auswählen',
loadNsdTranslations: 'NSDoc-Dateien hochladen',
invalidFileNoIdFound:
"Ungültiges NSDoc; kein 'id'-Attribut in der Datei gefunden",
invalidFileNoIdFound: "Ungültiges NSDoc ({{ filename }}); kein 'id'-Attribut in der Datei gefunden",
invalidNsdocVersion:
'Die Version {{ id }} NSD ({{ nsdVersion }}) passt nicht zu der geladenen NSDoc ({{ nsdocVersion }})',
'Die Version {{ id }} NSD ({{ nsdVersion }}) passt nicht zu der geladenen NSDoc ({{ filename }}, {{ nsdocVersion }})',
},
menu: {
new: 'Neues projekt',
Expand Down
6 changes: 3 additions & 3 deletions src/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ export const en = {
mode: 'Pro mode',
showieds: 'Show IEDs in substation editor',
selectFileButton: 'Select file',
loadNsdTranslations: 'Uploading NSDoc files',
invalidFileNoIdFound: "Invalid NSDoc; no 'id' attribute found in file",
loadNsdTranslations: 'Uploaded NSDoc files',
invalidFileNoIdFound: "Invalid NSDoc ({{ filename }}); no 'id' attribute found in file",
invalidNsdocVersion:
'The version of {{ id }} NSD ({{ nsdVersion }}) does not correlate with the version of the corresponding NSDoc ({{ nsdocVersion }})',
'The version of {{ id }} NSD ({{ nsdVersion }}) does not correlate with the version of the corresponding NSDoc ({{ filename }}, {{ nsdocVersion }})',
},
menu: {
new: 'New project',
Expand Down
84 changes: 84 additions & 0 deletions test/integration/Setting.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { html, fixture, expect } from '@open-wc/testing';

import '../../src/open-scd.js';

import { OpenSCD } from '../../src/open-scd.js';
import { newLoadNsdocEvent } from "../../src/Setting.js";

describe('Setting', () => {
let element: OpenSCD;

beforeEach(async () => {
localStorage.clear();

element = await fixture(html`
<open-scd></open-scd>

<link href="public/google/fonts/roboto-v27.css" rel="stylesheet" />
<link href="public/google/fonts/roboto-mono-v13.css" rel="stylesheet" />
<link href="public/google/icons/material-icons-outlined.css" rel="stylesheet" />
`);
});

it('opens the log on log menu entry click', async () => {
await (<HTMLElement>(
element.shadowRoot!.querySelector('mwc-list-item[iconid="history"]')!
)).click();
expect(element.logUI).to.have.property('open', true);
});

it('upload .nsdoc file using event and looks like latest snapshot', async () => {
element.settingsUI.show();
await element.settingsUI.updateComplete;

const nsdocFile = await fetch('/test/testfiles/nsdoc/IEC_61850-7-2.nsdoc')
.then(response => response.text())

element.dispatchEvent(
newLoadNsdocEvent(nsdocFile, 'IEC_61850-7-2.nsdoc')
);

await element.requestUpdate();
await element.updateComplete;

expect(localStorage.getItem('IEC 61850-7-2')).to.eql(nsdocFile);
expect(element).shadowDom.to.equalSnapshot();
});

it('upload invalid .nsdoc file using event and log event fired', async () => {
element.settingsUI.show();
await element.settingsUI.updateComplete;

const nsdocFile = await fetch('/test/testfiles/nsdoc/invalid.nsdoc')
.then(response => response.text())

element.dispatchEvent(
newLoadNsdocEvent(nsdocFile, 'invalid.nsdoc')
);

await element.requestUpdate();
await element.updateComplete;

expect(element.history.length).to.be.equal(1);
expect(element.history[0].title).to.be.equal('Invalid NSDoc (invalid.nsdoc); no \'id\' attribute found in file');
});

it('upload .nsdoc file with wrong version using event and log event fired', async () => {
element.settingsUI.show();
await element.settingsUI.updateComplete;

const nsdocFile = await fetch('/test/testfiles/nsdoc/wrong-version.nsdoc')
.then(response => response.text())

element.dispatchEvent(
newLoadNsdocEvent(nsdocFile, 'wrong-version.nsdoc')
);

await element.requestUpdate();
await element.updateComplete;

expect(element.history.length).to.be.equal(1);
expect(element.history[0].title).to.be.equal('The version of IEC 61850-7-3 NSD (2007B3) does not correlate ' +
'with the version of the corresponding NSDoc (wrong-version.nsdoc, 2007B4)');
});
}).timeout(4000);
Loading