From e2362043aedc3045c78d7a29c141ffe6e8ca0db1 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Mon, 12 Feb 2024 16:21:19 +0100 Subject: [PATCH 01/11] Feat: Added Waiter addon --- packages/open-scd/.eslintrc.cjs | 23 + packages/open-scd/src/addons/Waiter.ts | 45 + packages/open-scd/src/open-scd.ts | 15 +- .../src/wizards/foundation/references.ts | 2 +- .../__snapshots__/open-scd.test.snap.js | 2649 ++++++++--------- .../test/integration/open-scd.test.ts | 11 +- packages/open-scd/test/unit/Plugging.test.ts | 2 +- packages/open-scd/test/unit/Waiting.test.ts | 11 +- packages/open-scd/test/unit/mock-waiter.ts | 5 - 9 files changed, 1419 insertions(+), 1344 deletions(-) create mode 100644 packages/open-scd/.eslintrc.cjs create mode 100644 packages/open-scd/src/addons/Waiter.ts delete mode 100644 packages/open-scd/test/unit/mock-waiter.ts diff --git a/packages/open-scd/.eslintrc.cjs b/packages/open-scd/.eslintrc.cjs new file mode 100644 index 0000000000..6cae923ce6 --- /dev/null +++ b/packages/open-scd/.eslintrc.cjs @@ -0,0 +1,23 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc', 'import', 'html'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + ], + rules: { + // disable the rule for all files + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + 'import/named': 'off', + 'import/no-unresolved': 'off', + 'import/extensions': ['error', 'always', { ignorePackages: true }], + 'import/no-duplicates': 'off', + 'no-duplicate-imports': 'off', + 'tsdoc/syntax': 'warn' + }, +}; diff --git a/packages/open-scd/src/addons/Waiter.ts b/packages/open-scd/src/addons/Waiter.ts new file mode 100644 index 0000000000..7297cc4dab --- /dev/null +++ b/packages/open-scd/src/addons/Waiter.ts @@ -0,0 +1,45 @@ +import { + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; + +import '@material/mwc-linear-progress'; + +import { PendingStateDetail } from '../foundation.js'; + +@customElement('oscd-waiter') +export class OscdWaiter extends LitElement { + /** Whether the element is currently waiting for some async work. */ + @property({ type: Boolean }) + waiting = false; + + private work: Set> = new Set(); + /** A promise which resolves once all currently pending work is done. */ + workDone = Promise.allSettled(this.work); + + private async onPendingState(e: CustomEvent) { + this.waiting = true; + this.work.add(e.detail.promise); + this.workDone = Promise.allSettled(this.work); + await e.detail.promise.catch(reason => console.warn(reason)); + this.work.delete(e.detail.promise); + this.waiting = this.work.size > 0; + } + + constructor() { + super(); + this.onPendingState = this.onPendingState.bind(this); + this.addEventListener('pending-state', this.onPendingState); + } + + render(): TemplateResult { + return html` + `; + } +} diff --git a/packages/open-scd/src/open-scd.ts b/packages/open-scd/src/open-scd.ts index aa463098c7..70f6f2cd78 100644 --- a/packages/open-scd/src/open-scd.ts +++ b/packages/open-scd/src/open-scd.ts @@ -17,14 +17,15 @@ import { Hosting } from './Hosting.js'; import { Historing } from './Historing.js'; import { Plugging } from './Plugging.js'; import { Setting } from './Setting.js'; -import { Waiting } from './Waiting.js'; import { Wizarding } from './Wizarding.js'; +import './addons/Waiter.js'; + /** The `` custom element is the main entry point of the * Open Substation Configuration Designer. */ @customElement('open-scd') -export class OpenSCD extends Waiting( - Hosting(Setting(Wizarding(Plugging(Editing(Historing(LitElement)))))) +export class OpenSCD extends Hosting( + Setting(Wizarding(Plugging(Editing(Historing(LitElement))))) ) { private currentSrc = ''; /** The current file's URL. `blob:` URLs are *revoked after parsing*! */ @@ -37,6 +38,9 @@ export class OpenSCD extends Waiting( this.dispatchEvent(newPendingStateEvent(this.loadDoc(value))); } + @property({ type: Boolean }) + waiting = false; + /** Loads and parses an `XMLDocument` after [[`src`]] has changed. */ private async loadDoc(src: string): Promise { const response = await fetch(src); @@ -88,7 +92,10 @@ export class OpenSCD extends Waiting( } render(): TemplateResult { - return html` ${super.render()} ${getTheme(this.settings.theme)} `; + return html`${super.render()} + ${getTheme(this.settings.theme)} `; } static styles = css` diff --git a/packages/open-scd/src/wizards/foundation/references.ts b/packages/open-scd/src/wizards/foundation/references.ts index d166d43871..71a6b7efb8 100644 --- a/packages/open-scd/src/wizards/foundation/references.ts +++ b/packages/open-scd/src/wizards/foundation/references.ts @@ -290,7 +290,7 @@ function updateVals(element: Element, oldName: string | null, newName: string) { '.' + ref?.getAttribute('srcCBName'); - for (let value of valValues) { + for (const value of valValues) { if (oldName + suffixCBReference === value.textContent!.trim()) { const newElement = cloneElementAndTextContent( value, diff --git a/packages/open-scd/test/integration/__snapshots__/open-scd.test.snap.js b/packages/open-scd/test/integration/__snapshots__/open-scd.test.snap.js index 912262d91a..1e7346dc89 100644 --- a/packages/open-scd/test/integration/__snapshots__/open-scd.test.snap.js +++ b/packages/open-scd/test/integration/__snapshots__/open-scd.test.snap.js @@ -1,1412 +1,1411 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots['open-scd looks like its snapshot'] = ` - - Menu - - -
  • -
  • - + + + Menu + + +
  • +
  • + + + folder_open + + + Open project + + + + + + + create_new_folder + + + New project + + + + + + + save + + + Save project + + + + +
  • +
  • + + + rule_folder + + + Validate Schema + + + + + + + rule_folder + + + Validate Templates + + + + +
  • +
  • + + + snippet_folder + + + Import IEDs + + + + + + + play_circle + + + Subscriber Update + + + + + + + merge_type + + + Merge Project + + + + + + + merge_type + + + Update Substation + + + + + + + compare_arrows + + + Compare IED + + + + +
  • +
  • + + + settings + + + Settings + + + + + help + + + Help + + + + + + + history_toggle_off + + + Show SCL History + + + + +
  • +
  • + + + extension + + + Extensions + + +
    + + + +
    +
    + + + + + + + + + + +
    +
    +
    + - - folder_open - - +
    Open project - - - - - + + - - create_new_folder - - +
    New project - - - - - - - save - - - Save project - - - - -
  • -
  • - - - rule_folder - - - Validate Schema - - - - - + +
    + + - - rule_folder - - - Validate Templates - -
    - - -
  • + -
  • - + - - snippet_folder - - - Import IEDs - - - - - + - - play_circle - - - Subscriber Update - - - - - + + Errors, warnings and other notifications will show up here. + + + info + + + + - - merge_type - - - Merge Project - - - - - + + + - - merge_type - - - Update Substation - - - - - + + Edits will show up here + + + info + + + + - - compare_arrows - - - Compare IED - - - - -
  • -
  • - - - settings - - - Settings - - - - - help - - - Help - - - - - + - - history_toggle_off - - - Show SCL History - - - - -
  • -
  • - + - - extension - - - Extensions - - - - - + + + - -
    + + Issues found during validation will show up here + + + info + + + + -
    + Close +
    + + - + + - + Show +
    - + + - + Show + - - -
    - -
    - Open project -
    -
    - -
    - New project -
    -
    -
    - - - - - - - - - - - Errors, warnings and other notifications will show up here. - - - info - - - - - Close - - - - + - - - Edits will show up here - - - info - - - - - - - - - Close - - - - - + - - Issues found during validation will show up here - - - info - - - - - Close - - - - - - - - - Show - - - - - - - Show - - - - - - - Show - - - - - - + + - - - Editor tab - - - tab - - -
  • -
  • - - - developer_board - - IED - - - - margin - - Substation - - - - edit - - Single Line Diagram - - - - link - - Subscriber Message Binding (GOOSE) - - - - link - - Subscriber Data Binding (GOOSE) - - - - link - - Subscriber Later Binding (GOOSE) - - - - link - - Subscriber Message Binding (SMV) - - - - link - - Subscriber Data Binding (SMV) - - - - link - - Subscriber Later Binding (SMV) - - - - settings_ethernet - - Communication - - - - settings_ethernet - - 104 - - - - copy_all - - Templates - - - - publish - - Publisher - - - - cleaning_services - - Cleanup - - - - Menu entry - - + + Editor tab + + + tab + + +
  • +
  • + + + developer_board + + IED + + + + margin + + Substation + + + + edit + + Single Line Diagram + + + + link + + Subscriber Message Binding (GOOSE) + + + + link + + Subscriber Data Binding (GOOSE) + + + + link + + Subscriber Later Binding (GOOSE) + + + + link + + Subscriber Message Binding (SMV) + + + + link + + Subscriber Data Binding (SMV) + + + + link + + Subscriber Later Binding (SMV) + + + + settings_ethernet + + Communication + + + + settings_ethernet + + 104 + + + + copy_all + + Templates + + + + publish + + Publisher + + + + cleaning_services + + Cleanup + + + + Menu entry + + + + play_circle + + + +
  • +
  • + - - play_circle - - - -
  • -
  • - - - folder_open - - Open project - - - - create_new_folder - - New project - - - - save - - Save project - -
  • -
  • - - - rule_folder - - Validate Schema - - - - rule_folder - - Validate Templates - -
  • -
  • - - - snippet_folder - - Import IEDs - - - - developer_board - - Create Virtual IED - - - - play_circle - - Subscriber Update - - - - play_circle - - Update desc (ABB) - - - - play_circle - - Update desc (SEL) - - - - merge_type - - Merge Project - - - - merge_type - - Update Substation - - - - compare_arrows - - Compare IED - - - - sim_card_download - - Export Communication Section - -
  • -
  • - - - help - - Help - - - - history_toggle_off - - Show SCL History - -
    - - - - - - -
    - -
    -

    - Here you may add remote extensions directly from a custom URL. - You do this at your own risk. -

    - - - - + folder_open + + Open project + + - Editor tab - tab + create_new_folder - - + - Menu entry - play_circle + save - - - +
  • +
  • + + + rule_folder + + Validate Schema + + - Validator rule_folder -
    + Validate Templates + +
  • +
  • + + + snippet_folder + + Import IEDs + + + + developer_board + + Create Virtual IED + + + + play_circle + + Subscriber Update + + + + play_circle + + Update desc (ABB) + + + + play_circle + + Update desc (SEL) + + + + merge_type + + Merge Project + + + + merge_type + + Update Substation + + + + compare_arrows + + Compare IED + + + + sim_card_download + + Export Communication Section + +
  • +
  • + + + help + + Help + + + + history_toggle_off + + Show SCL History +
    - - -
    - - - + + + + +
    + - - - - - -
    - +

    + Here you may add remote extensions directly from a custom URL. + You do this at your own risk. +

    + + + + + Editor tab + + tab + + + + Menu entry + + play_circle + + + + + Validator + + rule_folder + + + + + +
    + + + + + + + + + + + + English + + + German (Deutsch) + + + + + + + + + + + + + + + + + +
    +

    + Uploaded NSDoc files +

    + + + +
    + - English + + IEC 61850-7-2 + + + close + + + IEC 61850-7-3 + + + close + + + - German (Deutsch) + + IEC 61850-7-4 + + + close + - - - - - - - - - - - - - - - - -
    -

    - Uploaded NSDoc files -

    - + + + IEC 61850-8-1 + + + close + + + + Cancel -
    - - - - IEC 61850-7-2 - - - close - - - - - IEC 61850-7-3 - - - close - - - - - IEC 61850-7-4 - - - close - - - + - - IEC 61850-8-1 - - - close - - - - - Cancel - - - Reset - - - Save - -
    - - + Save + + + `; /* end snapshot open-scd looks like its snapshot */ + diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index 4354785629..48730da214 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -67,10 +67,15 @@ describe('open-scd', () => { expect(element.diagnosticUI).to.have.property('open', true); }); + /** + * @deprecated + * Remove this integration test. It's no longer an integration test but an E2E test. + */ it('renders a progress indicator on `waiting`', async () => { - const progressBar = element.shadowRoot!.querySelector( - 'mwc-linear-progress[indeterminate]' - ); + const progressBar = element + .shadowRoot!.querySelector('oscd-waiter')! + .shadowRoot!.querySelector('mwc-linear-progress[indeterminate]')!; + console.log('progress bar : ', progressBar); expect(progressBar).property('closed').to.be.true; element.waiting = true; await element.updateComplete; diff --git a/packages/open-scd/test/unit/Plugging.test.ts b/packages/open-scd/test/unit/Plugging.test.ts index 9433921668..96647cb5b5 100644 --- a/packages/open-scd/test/unit/Plugging.test.ts +++ b/packages/open-scd/test/unit/Plugging.test.ts @@ -8,7 +8,7 @@ import { TextField } from '@material/mwc-textfield'; describe('PluggingElement', () => { let element: MockPlugger; let doc: XMLDocument; - const docName: string = 'testDoc'; + const docName = 'testDoc'; afterEach(async () => { await new Promise(resolve => setTimeout(resolve, 50)); // await animation diff --git a/packages/open-scd/test/unit/Waiting.test.ts b/packages/open-scd/test/unit/Waiting.test.ts index ddc0240587..08e7f87976 100644 --- a/packages/open-scd/test/unit/Waiting.test.ts +++ b/packages/open-scd/test/unit/Waiting.test.ts @@ -1,14 +1,15 @@ import { html, fixture, expect } from '@open-wc/testing'; -import './mock-waiter.js'; -import { MockWaiter } from './mock-waiter.js'; +import '../../src/addons/Waiter.js'; + +import { OscdWaiter } from '../../src/addons/Waiter.js'; import { newPendingStateEvent } from '../../src/foundation.js'; -describe('WaitingElement', () => { - let element: MockWaiter; +describe('OSCD-Waiter', () => { + let element: OscdWaiter; beforeEach(async () => { - element = await fixture(html``); + element = await fixture(html``); }); it('does not wait for pending state initially', () => diff --git a/packages/open-scd/test/unit/mock-waiter.ts b/packages/open-scd/test/unit/mock-waiter.ts deleted file mode 100644 index 4f46d25d1f..0000000000 --- a/packages/open-scd/test/unit/mock-waiter.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Waiting } from '../../src/Waiting.js'; -import { LitElement, customElement } from 'lit-element'; - -@customElement('mock-waiter') -export class MockWaiter extends Waiting(LitElement) {} From 26a362f7837378d482417f783d953d6b4332d52e Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Mon, 12 Feb 2024 16:24:28 +0100 Subject: [PATCH 02/11] Fix: removed console.log --- packages/open-scd/test/integration/open-scd.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index 48730da214..803a4ba9cc 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -75,7 +75,7 @@ describe('open-scd', () => { const progressBar = element .shadowRoot!.querySelector('oscd-waiter')! .shadowRoot!.querySelector('mwc-linear-progress[indeterminate]')!; - console.log('progress bar : ', progressBar); + expect(progressBar).property('closed').to.be.true; element.waiting = true; await element.updateComplete; From 2a284a25a155e316a76cb2a6c5d02b8719c7b50e Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Mon, 12 Feb 2024 17:21:21 +0100 Subject: [PATCH 03/11] Fix: Removed .workdone --- packages/open-scd/test/integration/open-scd.test.ts | 3 +-- .../test/integration/validators/ValidateTemplates.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index 803a4ba9cc..6a20264c3b 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -75,7 +75,6 @@ describe('open-scd', () => { const progressBar = element .shadowRoot!.querySelector('oscd-waiter')! .shadowRoot!.querySelector('mwc-linear-progress[indeterminate]')!; - expect(progressBar).property('closed').to.be.true; element.waiting = true; await element.updateComplete; @@ -96,7 +95,7 @@ describe('open-scd', () => { ); expect(await fetch(emptyBlobURL)).to.be.ok; element.setAttribute('src', emptyBlobURL); - await element.workDone; + await element.updateComplete; expect(element.src).to.be.a('string').and.equal(emptyBlobURL); expect(async () => await fetch(emptyBlobURL)).to.throw; }); diff --git a/packages/open-scd/test/integration/validators/ValidateTemplates.test.ts b/packages/open-scd/test/integration/validators/ValidateTemplates.test.ts index 5691663644..b24bbf679a 100644 --- a/packages/open-scd/test/integration/validators/ValidateTemplates.test.ts +++ b/packages/open-scd/test/integration/validators/ValidateTemplates.test.ts @@ -35,7 +35,7 @@ describe('ValidateTemplates OpenSCD integration test ', () => { element.pluginId = '/src/validators/ValidateTemplates.js'; await element.validate(); - await parent.workDone; + await parent.updateComplete; }); it('shows a "No errors" message in the diagnostics pane', async () => { @@ -67,7 +67,7 @@ describe('ValidateTemplates OpenSCD integration test ', () => { element.pluginId = '/src/validators/ValidateTemplates.js'; await element.validate(); - await parent.workDone; + await parent.updateComplete; }); it('generates issues in the diagnistics pane', async () => { const issues = parent.diagnoses.get( @@ -102,7 +102,7 @@ describe('ValidateTemplates OpenSCD integration test ', () => { element.pluginId = '/src/validators/ValidateTemplates.js'; await element.validate(); - await parent.workDone; + await parent.updateComplete; }); it('shows only one message in the diagnostics pane', async () => { const issues = parent.diagnoses.get( From 55413868ce3d8c1164ec7adf5d40a392029e4e1f Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 13 Feb 2024 10:18:53 +0100 Subject: [PATCH 04/11] Fix: Removed waiting from OpenSCD --- packages/open-scd/src/open-scd.ts | 7 +------ packages/open-scd/src/wizards/foundation/references.ts | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/open-scd/src/open-scd.ts b/packages/open-scd/src/open-scd.ts index 70f6f2cd78..ffb24927ca 100644 --- a/packages/open-scd/src/open-scd.ts +++ b/packages/open-scd/src/open-scd.ts @@ -38,9 +38,6 @@ export class OpenSCD extends Hosting( this.dispatchEvent(newPendingStateEvent(this.loadDoc(value))); } - @property({ type: Boolean }) - waiting = false; - /** Loads and parses an `XMLDocument` after [[`src`]] has changed. */ private async loadDoc(src: string): Promise { const response = await fetch(src); @@ -92,9 +89,7 @@ export class OpenSCD extends Hosting( } render(): TemplateResult { - return html`${super.render()} + return html`${super.render()} ${getTheme(this.settings.theme)} `; } diff --git a/packages/open-scd/src/wizards/foundation/references.ts b/packages/open-scd/src/wizards/foundation/references.ts index 71a6b7efb8..99fcd75a4e 100644 --- a/packages/open-scd/src/wizards/foundation/references.ts +++ b/packages/open-scd/src/wizards/foundation/references.ts @@ -6,7 +6,7 @@ import { } from '../../foundation.js'; const referenceInfoTags = ['IED', 'Substation', 'VoltageLevel', 'Bay'] as const; -type ReferencesInfoTag = typeof referenceInfoTags[number]; +type ReferencesInfoTag = (typeof referenceInfoTags)[number]; type FilterFunction = ( element: Element, @@ -290,7 +290,7 @@ function updateVals(element: Element, oldName: string | null, newName: string) { '.' + ref?.getAttribute('srcCBName'); - for (const value of valValues) { + for (let value of valValues) { if (oldName + suffixCBReference === value.textContent!.trim()) { const newElement = cloneElementAndTextContent( value, From 137612e7dde9c99e29af9a172c6dfd4be6032458 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 13 Feb 2024 10:42:03 +0100 Subject: [PATCH 05/11] Fix: Added deferred promise to open-scd.test --- .../test/integration/open-scd.test.ts | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index 6a20264c3b..8cc5fdff3c 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -3,6 +3,22 @@ import { expect, fixture, html } from '@open-wc/testing'; import '../../src/open-scd.js'; import { newEmptySCD } from '../../src/schemas.js'; import { OpenSCD } from '../../src/open-scd.js'; +import { newPendingStateEvent } from '../../src/foundation.js'; + +class DeferredPromise { + private _promise: Promise; + private resolve!: (value: T) => void; + private reject!: (reason?: any) => void; + + constructor() { + this._promise = new Promise((resolve, reject) => { + // assign the resolve and reject functions to `this` + // making them usable on the class instance + this.resolve = resolve; + this.reject = reject; + }); + } +} describe('open-scd', () => { let element: OpenSCD; @@ -76,10 +92,32 @@ describe('open-scd', () => { .shadowRoot!.querySelector('oscd-waiter')! .shadowRoot!.querySelector('mwc-linear-progress[indeterminate]')!; expect(progressBar).property('closed').to.be.true; - element.waiting = true; + + function defer() { + const deferred: { + promise: Promise | null; + resolve: ((value: T) => void) | null; + reject: ((reason?: any) => void) | null; + } = { + promise: null, + resolve: null, + reject: null, + }; + + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + + return deferred; + } + + const deferredPromise = defer(); + + progressBar.dispatchEvent(newPendingStateEvent(deferredPromise.promise!)); await element.updateComplete; expect(progressBar).property('closed').to.be.false; - element.waiting = false; + deferredPromise.resolve!; await element.updateComplete; expect(progressBar).property('closed').to.be.true; }); From 5f11fd8e0988b3e050da1c2df6b433b8b64e1468 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 13 Feb 2024 10:42:12 +0100 Subject: [PATCH 06/11] Fix: Added deferred promise to open-scd.test --- .../open-scd/test/integration/open-scd.test.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index 8cc5fdff3c..b4e24d5334 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -5,21 +5,6 @@ import { newEmptySCD } from '../../src/schemas.js'; import { OpenSCD } from '../../src/open-scd.js'; import { newPendingStateEvent } from '../../src/foundation.js'; -class DeferredPromise { - private _promise: Promise; - private resolve!: (value: T) => void; - private reject!: (reason?: any) => void; - - constructor() { - this._promise = new Promise((resolve, reject) => { - // assign the resolve and reject functions to `this` - // making them usable on the class instance - this.resolve = resolve; - this.reject = reject; - }); - } -} - describe('open-scd', () => { let element: OpenSCD; From 12fc4d71d8c04356884a1163de9efb58db19db75 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 13 Feb 2024 10:56:26 +0100 Subject: [PATCH 07/11] Fix: fixed E2E test --- .../test/integration/open-scd.test.ts | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/packages/open-scd/test/integration/open-scd.test.ts b/packages/open-scd/test/integration/open-scd.test.ts index b4e24d5334..18c92cd66c 100644 --- a/packages/open-scd/test/integration/open-scd.test.ts +++ b/packages/open-scd/test/integration/open-scd.test.ts @@ -4,6 +4,7 @@ import '../../src/open-scd.js'; import { newEmptySCD } from '../../src/schemas.js'; import { OpenSCD } from '../../src/open-scd.js'; import { newPendingStateEvent } from '../../src/foundation.js'; +import { OscdWaiter } from '../../src/addons/Waiter.js'; describe('open-scd', () => { let element: OpenSCD; @@ -73,37 +74,18 @@ describe('open-scd', () => { * Remove this integration test. It's no longer an integration test but an E2E test. */ it('renders a progress indicator on `waiting`', async () => { - const progressBar = element - .shadowRoot!.querySelector('oscd-waiter')! - .shadowRoot!.querySelector('mwc-linear-progress[indeterminate]')!; + const waiter: OscdWaiter = + element.shadowRoot!.querySelector('oscd-waiter')!; + const progressBar = waiter.shadowRoot!.querySelector( + 'mwc-linear-progress[indeterminate]' + )!; expect(progressBar).property('closed').to.be.true; - function defer() { - const deferred: { - promise: Promise | null; - resolve: ((value: T) => void) | null; - reject: ((reason?: any) => void) | null; - } = { - promise: null, - resolve: null, - reject: null, - }; - - deferred.promise = new Promise((resolve, reject) => { - deferred.resolve = resolve; - deferred.reject = reject; - }); - - return deferred; - } - - const deferredPromise = defer(); - - progressBar.dispatchEvent(newPendingStateEvent(deferredPromise.promise!)); - await element.updateComplete; + waiter.waiting = true; + await waiter.updateComplete; expect(progressBar).property('closed').to.be.false; - deferredPromise.resolve!; - await element.updateComplete; + waiter.waiting = false; + await waiter.updateComplete; expect(progressBar).property('closed').to.be.true; }); From 563d4c8347cd24e41397d35ca345b1705d48b6a5 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 13 Feb 2024 11:00:56 +0100 Subject: [PATCH 08/11] Removed waiting mixin --- packages/open-scd/src/Waiting.ts | 52 -------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 packages/open-scd/src/Waiting.ts diff --git a/packages/open-scd/src/Waiting.ts b/packages/open-scd/src/Waiting.ts deleted file mode 100644 index 912dd0c397..0000000000 --- a/packages/open-scd/src/Waiting.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { html, property, TemplateResult } from 'lit-element'; - -import '@material/mwc-linear-progress'; - -import { - LitElementConstructor, - Mixin, - PendingStateDetail, - ifImplemented, -} from './foundation.js'; - -/** Mixin implementing - * [Pending State](https://github.com/justinfagnani/pending-state-protocol) */ -export type WaitingElement = Mixin; - -export function Waiting(Base: TBase) { - class WaitingElement extends Base { - /** Whether the element is currently waiting for some async work. */ - @property({ type: Boolean }) - waiting = false; - - private work: Set> = new Set(); - /** A promise which resolves once all currently pending work is done. */ - workDone = Promise.allSettled(this.work); - - private async onPendingState(e: CustomEvent) { - this.waiting = true; - this.work.add(e.detail.promise); - this.workDone = Promise.allSettled(this.work); - await e.detail.promise.catch(reason => console.warn(reason)); - this.work.delete(e.detail.promise); - this.waiting = this.work.size > 0; - } - - constructor(...args: any[]) { - super(...args); - - this.onPendingState = this.onPendingState.bind(this); - this.addEventListener('pending-state', this.onPendingState); - } - - render(): TemplateResult { - return html`${ifImplemented(super.render())} - `; - } - } - - return WaitingElement; -} From f44e0deb7e768eb5fe35a5717aba99e25cd1f29a Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Wed, 21 Feb 2024 11:38:27 +0100 Subject: [PATCH 09/11] Moved events to connectedCallback / disconnectedCallback --- packages/open-scd/src/addons/Waiter.ts | 12 +- packages/open-scd/src/addons/dist/Waiter.js | 115 ++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 packages/open-scd/src/addons/dist/Waiter.js diff --git a/packages/open-scd/src/addons/Waiter.ts b/packages/open-scd/src/addons/Waiter.ts index 7297cc4dab..ddfda9db94 100644 --- a/packages/open-scd/src/addons/Waiter.ts +++ b/packages/open-scd/src/addons/Waiter.ts @@ -17,6 +17,7 @@ export class OscdWaiter extends LitElement { waiting = false; private work: Set> = new Set(); + /** A promise which resolves once all currently pending work is done. */ workDone = Promise.allSettled(this.work); @@ -32,10 +33,19 @@ export class OscdWaiter extends LitElement { constructor() { super(); this.onPendingState = this.onPendingState.bind(this); + } + + connectedCallback(): void { + super.connectedCallback(); this.addEventListener('pending-state', this.onPendingState); } - render(): TemplateResult { + disconnectedCallback(): void { + super.disconnectedCallback(); + this.removeEventListener('pending-state', this.onPendingState); + } + + override render(): TemplateResult { return html` = 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.OscdWaiter = void 0; +var lit_element_1 = require("lit-element"); +require("@material/mwc-linear-progress"); +var OscdWaiter = /** @class */ (function (_super) { + __extends(OscdWaiter, _super); + function OscdWaiter() { + var _this = _super.call(this) || this; + /** Whether the element is currently waiting for some async work. */ + _this.waiting = false; + _this.work = new Set(); + /** A promise which resolves once all currently pending work is done. */ + _this.workDone = Promise.allSettled(_this.work); + _this.onPendingState = _this.onPendingState.bind(_this); + return _this; + } + OscdWaiter.prototype.onPendingState = function (e) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this.waiting = true; + this.work.add(e.detail.promise); + this.workDone = Promise.allSettled(this.work); + return [4 /*yield*/, e.detail.promise["catch"](function (reason) { return console.warn(reason); })]; + case 1: + _a.sent(); + this.work["delete"](e.detail.promise); + this.waiting = this.work.size > 0; + return [2 /*return*/]; + } + }); + }); + }; + OscdWaiter.prototype.connectedCallback = function () { + _super.prototype.connectedCallback.call(this); + this.addEventListener('pending-state', this.onPendingState); + }; + OscdWaiter.prototype.disconnectedCallback = function () { + _super.prototype.disconnectedCallback.call(this); + this.removeEventListener('pending-state', this.onPendingState); + }; + OscdWaiter.prototype.render = function () { + return lit_element_1.html(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n "], ["\n "])), !this.waiting); + }; + __decorate([ + lit_element_1.property({ type: Boolean }) + ], OscdWaiter.prototype, "waiting"); + OscdWaiter = __decorate([ + lit_element_1.customElement('oscd-waiter') + ], OscdWaiter); + return OscdWaiter; +}(lit_element_1.LitElement)); +exports.OscdWaiter = OscdWaiter; +var templateObject_1; From d5662d4b65eba5ecde270f6694e741037d8721cf Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Wed, 21 Feb 2024 11:49:55 +0100 Subject: [PATCH 10/11] Delete packages/open-scd/src/addons/dist/Waiter.js --- packages/open-scd/src/addons/dist/Waiter.js | 115 -------------------- 1 file changed, 115 deletions(-) delete mode 100644 packages/open-scd/src/addons/dist/Waiter.js diff --git a/packages/open-scd/src/addons/dist/Waiter.js b/packages/open-scd/src/addons/dist/Waiter.js deleted file mode 100644 index f54438bc0b..0000000000 --- a/packages/open-scd/src/addons/dist/Waiter.js +++ /dev/null @@ -1,115 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -exports.__esModule = true; -exports.OscdWaiter = void 0; -var lit_element_1 = require("lit-element"); -require("@material/mwc-linear-progress"); -var OscdWaiter = /** @class */ (function (_super) { - __extends(OscdWaiter, _super); - function OscdWaiter() { - var _this = _super.call(this) || this; - /** Whether the element is currently waiting for some async work. */ - _this.waiting = false; - _this.work = new Set(); - /** A promise which resolves once all currently pending work is done. */ - _this.workDone = Promise.allSettled(_this.work); - _this.onPendingState = _this.onPendingState.bind(_this); - return _this; - } - OscdWaiter.prototype.onPendingState = function (e) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - this.waiting = true; - this.work.add(e.detail.promise); - this.workDone = Promise.allSettled(this.work); - return [4 /*yield*/, e.detail.promise["catch"](function (reason) { return console.warn(reason); })]; - case 1: - _a.sent(); - this.work["delete"](e.detail.promise); - this.waiting = this.work.size > 0; - return [2 /*return*/]; - } - }); - }); - }; - OscdWaiter.prototype.connectedCallback = function () { - _super.prototype.connectedCallback.call(this); - this.addEventListener('pending-state', this.onPendingState); - }; - OscdWaiter.prototype.disconnectedCallback = function () { - _super.prototype.disconnectedCallback.call(this); - this.removeEventListener('pending-state', this.onPendingState); - }; - OscdWaiter.prototype.render = function () { - return lit_element_1.html(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n "], ["\n "])), !this.waiting); - }; - __decorate([ - lit_element_1.property({ type: Boolean }) - ], OscdWaiter.prototype, "waiting"); - OscdWaiter = __decorate([ - lit_element_1.customElement('oscd-waiter') - ], OscdWaiter); - return OscdWaiter; -}(lit_element_1.LitElement)); -exports.OscdWaiter = OscdWaiter; -var templateObject_1; From 141dafaccc31def3d7fb1b955a82725976fa8b9d Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Wed, 21 Feb 2024 15:10:46 +0100 Subject: [PATCH 11/11] Update Waiter.ts --- packages/open-scd/src/addons/Waiter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/open-scd/src/addons/Waiter.ts b/packages/open-scd/src/addons/Waiter.ts index ddfda9db94..83bce55e0d 100644 --- a/packages/open-scd/src/addons/Waiter.ts +++ b/packages/open-scd/src/addons/Waiter.ts @@ -45,7 +45,7 @@ export class OscdWaiter extends LitElement { this.removeEventListener('pending-state', this.onPendingState); } - override render(): TemplateResult { + render(): TemplateResult { return html`