diff --git a/src/editors/ied/da-container.ts b/src/editors/ied/da-container.ts index 88e4c3af16..7196717bba 100644 --- a/src/editors/ied/da-container.ts +++ b/src/editors/ied/da-container.ts @@ -16,6 +16,8 @@ import { IconButtonToggle } from '@material/mwc-icon-button-toggle'; import '../../action-pane.js'; import { getNameAttribute, newWizardEvent } from '../../foundation.js'; import { Nsdoc } from '../../foundation/nsdoc.js'; +import { wizards } from '../../wizards/wizard-library.js'; +import { DaiValidationTypes, getCustomField } from './foundation/foundation.js'; import { createDaInfoWizard } from "./da-wizard.js"; import { getValueElement } from './foundation.js'; @@ -60,12 +62,12 @@ export class DAContainer extends LitElement { * If there is a DAI, it get's priority on top of (B)DA values. * @returns TemplateResult containing the value of the instance, element or nothing. */ - private renderValue(): TemplateResult { + private getValue(): string | null | undefined { if (this.instanceElement) { - return html`${getValueElement(this.instanceElement)?.textContent}` + return getValueElement(this.instanceElement)?.textContent?.trim() } - return html`${getValueElement(this.element)?.textContent}`; + return getValueElement(this.element)?.textContent?.trim(); } /** @@ -80,9 +82,15 @@ export class DAContainer extends LitElement { } return []; } + + private openEditWizard(): void { + const wizard = wizards['DAI'].edit(this.element, this.instanceElement); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } render(): TemplateResult { const bType = this.element!.getAttribute('bType'); + const value = this.getValue() ?? ''; return html` @@ -101,7 +109,19 @@ export class DAContainer extends LitElement { @click=${() => this.requestUpdate()} > ` : nothing} -
${this.renderValue()}
+ ${this.instanceElement && getCustomField()[bType] ? + html`
+
+

${value}

+
+
+ this.openEditWizard()} + > +
+
` : + html`

${value}

`} ${this.toggleButton?.on && bType == 'Struct' ? this.getBDAElements().map(element => html` { + return { + BOOLEAN: booleanField(), + INT8: integerField('INT8', -(2**8), 2**8-1), + INT16: integerField('INT16', -(2**16), 2**16-1), + INT24: integerField('INT24', -(2**24), 2**24-1), + INT32: integerField('INT32', -(2**32), 2**32-1), + INT64: integerField('INT64', -(2**64), 2**64-1), + INT128: integerField('INT128', -(2**128), 2**128-1), + INT8U: integerField('INT8U', 0, 2**8-1), + INT16U: integerField('INT16U', 0, 2**16-1), + INT24U: integerField('INT24U', 0, 2**24-1), + INT32U: integerField('INT32U', 0, 2**32-1), + FLOAT32: floatField('FLOAT32', -(2**32), 2**32-1), + FLOAT64: floatField('FLOAT64', -(2**64), 2**64-1), + VisString32: stringField('VisString32', 32), + VisString64: stringField('VisString64', 64), + VisString65: stringField('VisString65', 65), + VisString129: stringField('VisString129', 129), + VisString255: stringField('VisString255', 255) + } + + function booleanField(): CustomField { + return { + render: (value: string) => { + return html` + true + false + `; + } + } + } + + function integerField(type: string, min: number, max: number): CustomField { + return { + render: (value: string) => { + return html``; + } + } + } + + function floatField(type: string, min: number, max: number): CustomField { + return { + render: (value: string) => { + return html``; + } + } + } + + function stringField(type: string, maxNrOfCharacters: number): CustomField { + return { + render: (value: string) => { + return html``; + } + } + } +} \ No newline at end of file diff --git a/src/translations/de.ts b/src/translations/de.ts index 612b6a43fa..9167788be2 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -381,6 +381,17 @@ export const de: Translations = { }, }, }, + dai: { + wizard: { + valueHelper: 'Der Wert sollte vom Typ sein {{type}}', + title: { + edit: 'Edit {{daiName}}', + }, + }, + action: { + updatedai: 'DAI "{{daiName}} bearbeitet"', + } + }, sdo: { wizard: { title: { diff --git a/src/translations/en.ts b/src/translations/en.ts index a540911eeb..481f815a30 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -378,6 +378,17 @@ export const en = { }, }, }, + dai: { + wizard: { + valueHelper: 'Value should be of type {{type}}', + title: { + edit: 'Edit {{daiName}}', + }, + }, + action: { + updatedai: 'Edited DAI "{{daiName}}"', + } + }, sdo: { wizard: { title: { diff --git a/src/wizards/dai.ts b/src/wizards/dai.ts new file mode 100644 index 0000000000..b8fe9671ab --- /dev/null +++ b/src/wizards/dai.ts @@ -0,0 +1,64 @@ +import { html, TemplateResult } from 'lit-element'; +import { get } from 'lit-translate'; +import { DaiValidationTypes, getCustomField } from '../editors/ied/foundation/foundation.js'; + +import { + ComplexAction, + EditorAction, + getValue, + Wizard, + WizardActor, + WizardInputElement, +} from '../foundation.js'; + +export function updateValue(element: Element): WizardActor { + return (inputs: WizardInputElement[]): EditorAction[] => { + const newValue = getValue(inputs.find(i => i.value)!)!; + const name = element.getAttribute('name'); + + const oldVal = element.querySelector('Val')!; + + const complexAction: ComplexAction = { + actions: [], + title: get('dai.action.updatedai', {daiName: name!}), + }; + + const newVal = oldVal.cloneNode(false); + newVal.textContent = newValue; + + complexAction.actions.push({ old: { element: oldVal }, new: { element: newVal } }); + return [complexAction]; + }; +} + +export function renderDAIWizard( + element: Element, + instanceElement?: Element +): TemplateResult[] { + const bType = element.getAttribute('bType')!; + const value = instanceElement!.querySelector('Val')?.textContent?.trim() ?? ''; + + return [ + html`${getCustomField()[bType].render(value)}`, + ]; +} + +export function editDAIWizard(element: Element, instanceElement?: Element): Wizard { + return [ + { + title: get('dai.wizard.title.edit', { + daiName: instanceElement?.getAttribute('name') ?? '' + }), + element: instanceElement, + primary: { + icon: 'edit', + label: get('save'), + action: updateValue(instanceElement!), + }, + content: renderDAIWizard( + element, + instanceElement + ), + }, + ]; +} diff --git a/src/wizards/wizard-library.ts b/src/wizards/wizard-library.ts index f8acf8ec34..d993403bcd 100644 --- a/src/wizards/wizard-library.ts +++ b/src/wizards/wizard-library.ts @@ -19,8 +19,10 @@ import { createPowerTransformerWizard, editPowerTransformerWizard } from './powe import { editSubNetworkWizard } from './subnetwork.js'; import { editIEDWizard } from './ied.js'; import { editTrgOpsWizard } from './trgops.js'; +import { createDaWizard } from './da.js'; +import { editDAIWizard } from './dai.js'; -type SclElementWizard = (element: Element) => Wizard | undefined; +type SclElementWizard = (element: Element, instanceElement?: Element) => Wizard | undefined; export function emptyWizard(): Wizard | undefined { return; @@ -122,11 +124,11 @@ export const wizards: Record< create: emptyWizard, }, DA: { - edit: emptyWizard, + edit: createDaWizard, create: emptyWizard, }, DAI: { - edit: emptyWizard, + edit: editDAIWizard, create: emptyWizard, }, DAType: { diff --git a/test/testfiles/valid2007B4ForDAIValidation.scd b/test/testfiles/valid2007B4ForDAIValidation.scd new file mode 100644 index 0000000000..9b80208d4f --- /dev/null +++ b/test/testfiles/valid2007B4ForDAIValidation.scd @@ -0,0 +1,745 @@ + + +
+ TrainingIEC61850 + + + +
+ + + 110.0 + + + + + + + + + + + + + + + + + + + + + + + + + 20 + + + + + + + 100.0 + +
+

192.168.210.111

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+ +
+

01-0C-CD-01-00-10

+

005

+

4

+

0010

+
+
+ +

RJ45

+
+
+
+ + +
+

192.168.0.112

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+
+ +
+

192.168.0.113

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+ +
+

01-0C-CD-04-00-20

+

007

+

4

+

4002

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IED2 + + + + + + + true + + + 5 + + + 500 + + + 8321 + + + 83218 + + + -543923 + + + -8 + + + 99 + + + 20000 + + + 654321 + + + 2 + + + 659.3 + + + 1111659.8 + + + pull-ups + + + lat pulldown + + + bench press + + + front squat + + + deadlift + + + + + status-only + + + + + + + sbo-with-enhanced-security + + + + + + + status-only + + + + + + + + 1 + + + + sbo-with-enhanced-security + + + + + + + + + + status-only + + + + + + + + + + + + + + + status-only + + + + + + + + + direct-with-normal-security + + + + + + + sbo-with-normal-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + + + + + + + + + + + + status-only + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + status-only + + + + + + + + + + + status-only + + + + + + + direct-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + + + + + + + + + + + + + + + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + Ok + Warning + Alarm + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + +
\ No newline at end of file diff --git a/test/testfiles/wizards/ied.scd b/test/testfiles/wizards/ied.scd index 0db66d418d..aa23c73f53 100644 --- a/test/testfiles/wizards/ied.scd +++ b/test/testfiles/wizards/ied.scd @@ -158,6 +158,11 @@ + + + 5 + + @@ -512,6 +517,7 @@ + diff --git a/test/unit/editors/ied/foundation/__snapshots__/foundation.test.snap.js b/test/unit/editors/ied/foundation/__snapshots__/foundation.test.snap.js new file mode 100644 index 0000000000..f388f550db --- /dev/null +++ b/test/unit/editors/ied/foundation/__snapshots__/foundation.test.snap.js @@ -0,0 +1,734 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["foundation getCustomField renders a BOOLEAN field correctly"] = +`
+
+
+ + + + + + +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a BOOLEAN field correctly */ + +snapshots["foundation getCustomField renders a INT8 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT8 field correctly */ + +snapshots["foundation getCustomField renders a INT16 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT16 field correctly */ + +snapshots["foundation getCustomField renders a INT24 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT24 field correctly */ + +snapshots["foundation getCustomField renders a INT32 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT32 field correctly */ + +snapshots["foundation getCustomField renders a INT64 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT64 field correctly */ + +snapshots["foundation getCustomField renders a INT128 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT128 field correctly */ + +snapshots["foundation getCustomField renders a INT8U field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT8U field correctly */ + +snapshots["foundation getCustomField renders a INT16U field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT16U field correctly */ + +snapshots["foundation getCustomField renders a INT24U field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT24U field correctly */ + +snapshots["foundation getCustomField renders a INT32U field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a INT32U field correctly */ + +snapshots["foundation getCustomField renders a FLOAT32 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a FLOAT32 field correctly */ + +snapshots["foundation getCustomField renders a FLOAT64 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a FLOAT64 field correctly */ + +snapshots["foundation getCustomField renders a VisString32 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a VisString32 field correctly */ + +snapshots["foundation getCustomField renders a VisString64 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a VisString64 field correctly */ + +snapshots["foundation getCustomField renders a VisString65 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a VisString65 field correctly */ + +snapshots["foundation getCustomField renders a VisString129 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a VisString129 field correctly */ + +snapshots["foundation getCustomField renders a VisString255 field correctly"] = +`
+
+ +
+ +
+
+
+
+
+`; +/* end snapshot foundation getCustomField renders a VisString255 field correctly */ + diff --git a/test/unit/editors/ied/foundation/foundation.test.ts b/test/unit/editors/ied/foundation/foundation.test.ts new file mode 100644 index 0000000000..0611c3388d --- /dev/null +++ b/test/unit/editors/ied/foundation/foundation.test.ts @@ -0,0 +1,163 @@ +import { html, fixture, expect } from '@open-wc/testing'; + +import { DaiValidationTypes, getCustomField } from '../../../../../src/editors/ied/foundation/foundation.js'; + +describe('foundation', async () => { + let validSCL: XMLDocument; + + beforeEach(async () => { + validSCL = await fetch('/test/testfiles/valid2007B4ForDAIValidation.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + describe('getCustomField', () => { + function getValue(daiName: string) { + return validSCL.querySelector(`DAI[name="${daiName}"] > Val`)?.textContent?.trim() + } + + it('renders a BOOLEAN field correctly', async () => { + const value = getValue("booleantest"); + const element = await fixture(html`${getCustomField()['BOOLEAN']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('true'); + }); + + it('renders a INT8 field correctly', async () => { + const value = getValue("int8test"); + const element = await fixture(html`${getCustomField()['INT8']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('5'); + }); + + it('renders a INT16 field correctly', async () => { + const value = getValue("int16test"); + const element = await fixture(html`${getCustomField()['INT16']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('500'); + }); + + it('renders a INT24 field correctly', async () => { + const value = getValue("int24test"); + const element = await fixture(html`${getCustomField()['INT24']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('8321'); + }); + + it('renders a INT32 field correctly', async () => { + const value = getValue("int32test"); + const element = await fixture(html`${getCustomField()['INT32']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('83218'); + }); + + it('renders a INT64 field correctly', async () => { + const value = getValue("int64test"); + const element = await fixture(html`${getCustomField()['INT64']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('-543923'); + }); + + it('renders a INT128 field correctly', async () => { + const value = getValue("int128test"); + const element = await fixture(html`${getCustomField()['INT128']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('-8'); + }); + + it('renders a INT8U field correctly', async () => { + const value = getValue("int8utest"); + const element = await fixture(html`${getCustomField()['INT8U']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('99'); + }); + + it('renders a INT16U field correctly', async () => { + const value = getValue("int16utest"); + const element = await fixture(html`${getCustomField()['INT16U']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('20000'); + }); + + it('renders a INT24U field correctly', async () => { + const value = getValue("int24utest"); + const element = await fixture(html`${getCustomField()['INT24U']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('654321'); + }); + + it('renders a INT32U field correctly', async () => { + const value = getValue("int32utest"); + const element = await fixture(html`${getCustomField()['INT32U']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('2'); + }); + + it('renders a FLOAT32 field correctly', async () => { + const value = getValue("float32test"); + const element = await fixture(html`${getCustomField()['INT32U']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('659.3'); + }); + + it('renders a FLOAT64 field correctly', async () => { + const value = getValue("float64test"); + const element = await fixture(html`${getCustomField()['FLOAT64']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('1111659.8'); + }); + + it('renders a VisString32 field correctly', async () => { + const value = getValue("visstring32test"); + const element = await fixture(html`${getCustomField()['VisString32']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('pull-ups'); + }); + + it('renders a VisString64 field correctly', async () => { + const value = getValue("visstring64test"); + const element = await fixture(html`${getCustomField()['VisString64']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('lat pulldown'); + }); + + it('renders a VisString65 field correctly', async () => { + const value = getValue("visstring65test"); + const element = await fixture(html`${getCustomField()['VisString65']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('bench press'); + }); + + it('renders a VisString129 field correctly', async () => { + const value = getValue("visstring129test"); + const element = await fixture(html`${getCustomField()['VisString129']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('front squat'); + }); + + it('renders a VisString255 field correctly', async () => { + const value = getValue("visstring255test"); + const element = await fixture(html`${getCustomField()['VisString255']?.render(value!)}`); + + await expect(element).shadowDom.to.equalSnapshot(); + expect(element.shadowRoot?.querySelector('input')?.value).to.eql('deadlift'); + }); + }); +}); diff --git a/test/unit/wizards/__snapshots__/dai.test.snap.js b/test/unit/wizards/__snapshots__/dai.test.snap.js new file mode 100644 index 0000000000..1c5a3ef5a1 --- /dev/null +++ b/test/unit/wizards/__snapshots__/dai.test.snap.js @@ -0,0 +1,38 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL element DAI edit existing DAI looks like the latest snapshot"] = +` +
+ + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element DAI edit existing DAI looks like the latest snapshot */ + diff --git a/test/unit/wizards/dai.test.ts b/test/unit/wizards/dai.test.ts new file mode 100644 index 0000000000..6924bbdbe2 --- /dev/null +++ b/test/unit/wizards/dai.test.ts @@ -0,0 +1,62 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../mock-wizard.js'; +import { MockWizard } from '../../mock-wizard.js'; + +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { + ComplexAction, + isSimple, + Replace, + WizardInputElement, +} from '../../../src/foundation.js'; + +import { + fetchDoc, + newWizard, + setWizardTextFieldValue, +} from './foundation.js'; +import { editDAIWizard, updateValue } from '../../../src/wizards/dai.js'; + +describe('Wizards for SCL element DAI', () => { + let doc: XMLDocument; + let dai: Element; + let da: Element; + let element: MockWizard; + let inputs: WizardInputElement[]; + + describe('edit existing DAI', () => { + beforeEach(async () => { + doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); + dai = doc.querySelector('IED[name="IED1"] > AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[prefix="CB"][lnClass="CSWI"] > DOI[name="Beh"] > DAI[name="integer"]')!; + da = doc.querySelector('DOType[cdc="ENS"][id="Dummy.LLN0.Beh"] > DA[name="integer"]')!; + + element = await fixture(html``); + const wizard = editDAIWizard(da, dai); + element.workflow.push(() => wizard); + await element.requestUpdate(); + inputs = Array.from(element.wizardUI.inputs); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); + + it('update value should be updated in document', async function () { + await setWizardTextFieldValue(inputs[0], '8'); + + const complexAction = updateValue(dai)(inputs, newWizard()); + expect(complexAction.length).to.equal(1); + expect(complexAction[0]).to.not.satisfy(isSimple); + + expect((complexAction[0]).title).to.eql('[dai.action.updatedai]'); + + const actions = (complexAction[0]).actions; + expect(actions.length).to.eql(1); + + const replace = (actions[0]); + expect(replace.old.element.textContent).to.eql('5'); + expect(replace.new.element.textContent).to.eql('8'); + }); + }); +});