mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-21 17:45:25 -04:00
abe44723ba
- Also fix generated code for events (and Else) not working with 'use strict' (only show in developer changelog) - Also improve the UI to not show horizontal bars for disabled events and comments
669 lines
21 KiB
JavaScript
669 lines
21 KiB
JavaScript
const initializeGDevelopJs = require('../../Binaries/embuild/GDevelop.js/libGD.js');
|
|
const { makeMinimalGDJSMock } = require('../TestUtils/GDJSMocks.js');
|
|
const {
|
|
generateCompiledEventsFromSerializedEvents,
|
|
} = require('../TestUtils/CodeGenerationHelpers.js');
|
|
|
|
describe('libGD.js - GDJS Transparent Events Code Generation integration tests', function () {
|
|
let gd = null;
|
|
beforeAll(async () => {
|
|
gd = await initializeGDevelopJs();
|
|
});
|
|
|
|
// ── Helpers to build events concisely ──────────────────────────────────
|
|
// All conditions compare scene variable "X" which is never modified
|
|
// (defaults to 0), so "X == 0" is always true, "X == 999" is always false.
|
|
|
|
const trueCondition = [
|
|
{ type: { value: 'VarScene' }, parameters: ['X', '=', '0'] },
|
|
];
|
|
const falseCondition = [
|
|
{ type: { value: 'VarScene' }, parameters: ['X', '=', '999'] },
|
|
];
|
|
|
|
const setVar = (name, value) => ({
|
|
type: { value: 'ModVarScene' },
|
|
parameters: [name, '=', String(value)],
|
|
});
|
|
|
|
const addVar = (name, value) => ({
|
|
type: { value: 'ModVarScene' },
|
|
parameters: [name, '+', String(value)],
|
|
});
|
|
|
|
const std = (varName, value, { conditions, subEvents } = {}) => ({
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: conditions || [],
|
|
actions: [setVar(varName, value)],
|
|
events: subEvents || [],
|
|
});
|
|
|
|
const stdFalse = (varName, value, opts = {}) =>
|
|
std(varName, value, { ...opts, conditions: falseCondition });
|
|
|
|
const stdTrue = (varName, value, opts = {}) =>
|
|
std(varName, value, { ...opts, conditions: trueCondition });
|
|
|
|
const elseEv = (varName, value, { conditions, subEvents } = {}) => ({
|
|
type: 'BuiltinCommonInstructions::Else',
|
|
conditions: conditions || [],
|
|
actions: [setVar(varName, value)],
|
|
events: subEvents || [],
|
|
});
|
|
|
|
const elseIfTrue = (varName, value, opts = {}) =>
|
|
elseEv(varName, value, { ...opts, conditions: trueCondition });
|
|
|
|
const elseIfFalse = (varName, value, opts = {}) =>
|
|
elseEv(varName, value, { ...opts, conditions: falseCondition });
|
|
|
|
const repeat = (times, subEvents) => ({
|
|
type: 'BuiltinCommonInstructions::Repeat',
|
|
repeatExpression: String(times),
|
|
conditions: [],
|
|
actions: [],
|
|
events: subEvents || [],
|
|
});
|
|
|
|
const whileLoop = (limit, subEvents) => ({
|
|
type: 'BuiltinCommonInstructions::While',
|
|
whileConditions: [
|
|
{
|
|
type: { value: 'VarScene' },
|
|
parameters: ['Counter', '<', String(limit)],
|
|
},
|
|
],
|
|
conditions: [],
|
|
actions: [addVar('Counter', 1)],
|
|
events: subEvents || [],
|
|
});
|
|
|
|
const chainBreaker = () => repeat(1, []);
|
|
|
|
const disabled = (event) => ({ ...event, disabled: true });
|
|
|
|
const comment = () => ({ type: 'BuiltinCommonInstructions::Comment' });
|
|
|
|
function runEvents(events, options = {}) {
|
|
const serializerElement = gd.Serializer.fromJSObject(events);
|
|
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
|
gd,
|
|
serializerElement,
|
|
options
|
|
);
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
|
|
return (name) =>
|
|
runtimeScene.getVariables().has(name)
|
|
? runtimeScene.getVariables().get(name).getAsNumber()
|
|
: 0;
|
|
}
|
|
|
|
// ── Modes: both disabled events and comment events must be transparent ─
|
|
|
|
const transparentEventModes = [
|
|
{
|
|
label: 'disabled events',
|
|
// Wraps any event to make it transparent (disabled).
|
|
wrap: (event) => disabled(event),
|
|
// Various transparent events to insert in stress tests.
|
|
variants: () => [
|
|
disabled(std('_noop_', 99)),
|
|
disabled(stdFalse('_noop_', 99)),
|
|
disabled(stdTrue('_noop_', 99)),
|
|
disabled(elseEv('_noop_', 99)),
|
|
disabled(elseIfTrue('_noop_', 99)),
|
|
disabled(elseIfFalse('_noop_', 99)),
|
|
],
|
|
// A cluster of transparent events for multi-insert stress test.
|
|
cluster: () => [
|
|
disabled(std('_n1_', 99)),
|
|
disabled(elseEv('_n2_', 99)),
|
|
disabled(stdTrue('_n3_', 99)),
|
|
],
|
|
},
|
|
{
|
|
label: 'comment events',
|
|
wrap: () => comment(),
|
|
variants: () => [comment()],
|
|
cluster: () => [comment(), comment(), comment()],
|
|
},
|
|
];
|
|
|
|
transparentEventModes.forEach(({ label, wrap, variants, cluster }) => {
|
|
describe(`with ${label}`, function () {
|
|
// ── Individual tests ─────────────────────────────────────────
|
|
|
|
it('transparent event between Standard(true) and Else does not affect chain', function () {
|
|
const v = runEvents([
|
|
stdTrue('If', 1),
|
|
wrap(std('T', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(1);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Else')).toBe(0);
|
|
});
|
|
|
|
it('transparent event between Standard(false) and Else does not affect chain', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
wrap(std('T', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('multiple transparent events between Standard(true) and Else do not affect chain', function () {
|
|
const v = runEvents([
|
|
stdTrue('If', 1),
|
|
wrap(std('T1', 99)),
|
|
wrap(stdFalse('T2', 99)),
|
|
wrap(elseEv('T3', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(1);
|
|
expect(v('T1')).toBe(0);
|
|
expect(v('T2')).toBe(0);
|
|
expect(v('T3')).toBe(0);
|
|
expect(v('Else')).toBe(0);
|
|
});
|
|
|
|
it('multiple transparent events between Standard(false) and Else do not affect chain', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
wrap(std('T1', 99)),
|
|
wrap(stdFalse('T2', 99)),
|
|
wrap(elseEv('T3', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('T1')).toBe(0);
|
|
expect(v('T2')).toBe(0);
|
|
expect(v('T3')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('transparent Else between Standard(false) and Else does not affect chain', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
wrap(elseIfTrue('T', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('transparent Else between Standard(true) and Else does not affect chain', function () {
|
|
const v = runEvents([
|
|
stdTrue('If', 1),
|
|
wrap(elseEv('T', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(1);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Else')).toBe(0);
|
|
});
|
|
|
|
it('all transparent events before Else makes it standalone (runs)', function () {
|
|
const v = runEvents([
|
|
chainBreaker(),
|
|
wrap(std('T1', 99)),
|
|
wrap(stdFalse('T2', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('T1')).toBe(0);
|
|
expect(v('T2')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('transparent event in an else-if chain preserves chain semantics', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
wrap(elseIfTrue('T', 99)),
|
|
elseIfFalse('Ei', 1),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Ei')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('transparent event between two Else events in a chain is transparent', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
elseIfFalse('Ei1', 1),
|
|
wrap(elseIfTrue('T', 99)),
|
|
elseIfTrue('Ei2', 1),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('Ei1')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Ei2')).toBe(1);
|
|
expect(v('Else')).toBe(0);
|
|
});
|
|
|
|
it('transparent Standard does not break an else chain', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
elseIfFalse('Ei', 1),
|
|
wrap(std('T', 99)),
|
|
elseEv('Else', 1),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('Ei')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
});
|
|
|
|
it('transparent event inside Repeat does not affect if/else inside', function () {
|
|
const events = [
|
|
{
|
|
type: 'BuiltinCommonInstructions::Repeat',
|
|
repeatExpression: '3',
|
|
conditions: [],
|
|
actions: [],
|
|
events: [
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: falseCondition,
|
|
actions: [addVar('RepIf', 1)],
|
|
events: [],
|
|
},
|
|
wrap(std('T', 99)),
|
|
{
|
|
type: 'BuiltinCommonInstructions::Else',
|
|
conditions: [],
|
|
actions: [addVar('RepElse', 1)],
|
|
events: [],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
const v = runEvents(events);
|
|
expect(v('RepIf')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('RepElse')).toBe(3);
|
|
});
|
|
|
|
it('transparent event inside While sub-events does not affect if/else inside', function () {
|
|
const events = [
|
|
{
|
|
type: 'BuiltinCommonInstructions::While',
|
|
whileConditions: [
|
|
{
|
|
type: { value: 'VarScene' },
|
|
parameters: ['Counter', '<', '3'],
|
|
},
|
|
],
|
|
conditions: [],
|
|
actions: [addVar('Counter', 1)],
|
|
events: [
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: falseCondition,
|
|
actions: [addVar('WIf', 1)],
|
|
events: [],
|
|
},
|
|
wrap(std('T', 99)),
|
|
{
|
|
type: 'BuiltinCommonInstructions::Else',
|
|
conditions: [],
|
|
actions: [addVar('WElse', 1)],
|
|
events: [],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
const v = runEvents(events);
|
|
expect(v('Counter')).toBe(3);
|
|
expect(v('WIf')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('WElse')).toBe(3);
|
|
});
|
|
|
|
it('transparent events in sub-events of an Else do not affect chains', function () {
|
|
const v = runEvents([
|
|
stdFalse('If', 1),
|
|
elseEv('Else', 1, {
|
|
subEvents: [
|
|
stdFalse('SubIf', 1),
|
|
wrap(std('T', 99)),
|
|
elseEv('SubElse', 1),
|
|
],
|
|
}),
|
|
]);
|
|
expect(v('If')).toBe(0);
|
|
expect(v('Else')).toBe(1);
|
|
expect(v('SubIf')).toBe(0);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('SubElse')).toBe(1);
|
|
});
|
|
|
|
it('transparent events in sub-events of a Standard do not affect chains', function () {
|
|
const v = runEvents([
|
|
std('Parent', 1, {
|
|
subEvents: [
|
|
stdTrue('SubIf', 1),
|
|
wrap(elseIfTrue('T', 99)),
|
|
elseEv('SubElse', 1),
|
|
],
|
|
}),
|
|
]);
|
|
expect(v('Parent')).toBe(1);
|
|
expect(v('SubIf')).toBe(1);
|
|
expect(v('T')).toBe(0);
|
|
expect(v('SubElse')).toBe(0);
|
|
});
|
|
|
|
// ── Stress tests ───────────────────────────────────────────
|
|
|
|
it('stress test: inserting at every position in top-level events does not change outcomes', function () {
|
|
const baseEvents = [
|
|
stdFalse('C1_if', 1),
|
|
elseIfFalse('C1_ei1', 1),
|
|
elseIfTrue('C1_ei2', 1),
|
|
elseEv('C1_else', 1),
|
|
|
|
stdTrue('C2_if', 1),
|
|
elseEv('C2_else', 1),
|
|
|
|
repeat(2, [
|
|
stdFalse('C3_repIf', 1),
|
|
elseEv('C3_repElse', 0),
|
|
]),
|
|
|
|
std('C4_std1', 1),
|
|
stdFalse('C4_std2', 1),
|
|
elseIfFalse('C4_ei', 1),
|
|
elseEv('C4_else', 1),
|
|
|
|
stdFalse('C5_if', 1),
|
|
elseEv('C5_else', 1, {
|
|
subEvents: [
|
|
stdFalse('C5_subIf', 1),
|
|
elseIfTrue('C5_subEi', 1),
|
|
elseEv('C5_subElse', 1),
|
|
],
|
|
}),
|
|
|
|
whileLoop(2, [
|
|
stdFalse('C6_wIf', 1),
|
|
elseEv('C6_wElse', 0),
|
|
]),
|
|
|
|
std('C7_after', 1),
|
|
];
|
|
|
|
const variableNames = [
|
|
'C1_if', 'C1_ei1', 'C1_ei2', 'C1_else',
|
|
'C2_if', 'C2_else',
|
|
'C3_repIf', 'C3_repElse',
|
|
'C4_std1', 'C4_std2', 'C4_ei', 'C4_else',
|
|
'C5_if', 'C5_else', 'C5_subIf', 'C5_subEi', 'C5_subElse',
|
|
'C6_wIf', 'C6_wElse',
|
|
'C7_after',
|
|
];
|
|
|
|
const vBase = runEvents(baseEvents);
|
|
const expected = {};
|
|
for (const name of variableNames) {
|
|
expected[name] = vBase(name);
|
|
}
|
|
|
|
expect(expected['C1_ei2']).toBe(1);
|
|
expect(expected['C1_else']).toBe(0);
|
|
expect(expected['C2_if']).toBe(1);
|
|
expect(expected['C2_else']).toBe(0);
|
|
expect(expected['C4_else']).toBe(1);
|
|
expect(expected['C5_else']).toBe(1);
|
|
expect(expected['C5_subEi']).toBe(1);
|
|
expect(expected['C7_after']).toBe(1);
|
|
|
|
const allVariants = variants();
|
|
|
|
for (let pos = 0; pos <= baseEvents.length; pos++) {
|
|
for (const transparentEv of allVariants) {
|
|
const modified = [...baseEvents];
|
|
modified.splice(pos, 0, transparentEv);
|
|
const v = runEvents(modified);
|
|
for (const name of variableNames) {
|
|
if (v(name) !== expected[name]) {
|
|
expect({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: v(name),
|
|
expected: expected[name],
|
|
}).toEqual({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: expected[name],
|
|
expected: expected[name],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('stress test: inserting at every position in sub-events does not change outcomes', function () {
|
|
const baseSubEvents = [
|
|
stdFalse('S_if', 1),
|
|
elseIfFalse('S_ei1', 1),
|
|
elseIfTrue('S_ei2', 1, {
|
|
subEvents: [
|
|
stdFalse('S_nested_if', 1),
|
|
elseEv('S_nested_else', 1),
|
|
],
|
|
}),
|
|
elseEv('S_else', 1),
|
|
std('S_after', 1),
|
|
];
|
|
|
|
const baseEvents = [
|
|
std('Parent', 1, { subEvents: baseSubEvents }),
|
|
];
|
|
|
|
const variableNames = [
|
|
'Parent', 'S_if', 'S_ei1', 'S_ei2', 'S_nested_if',
|
|
'S_nested_else', 'S_else', 'S_after',
|
|
];
|
|
|
|
const vBase = runEvents(baseEvents);
|
|
const expected = {};
|
|
for (const name of variableNames) {
|
|
expected[name] = vBase(name);
|
|
}
|
|
|
|
expect(expected['Parent']).toBe(1);
|
|
expect(expected['S_ei2']).toBe(1);
|
|
expect(expected['S_nested_else']).toBe(1);
|
|
expect(expected['S_else']).toBe(0);
|
|
expect(expected['S_after']).toBe(1);
|
|
|
|
const allVariants = variants();
|
|
|
|
for (let pos = 0; pos <= baseSubEvents.length; pos++) {
|
|
for (const transparentEv of allVariants) {
|
|
const modifiedSubEvents = [...baseSubEvents];
|
|
modifiedSubEvents.splice(pos, 0, transparentEv);
|
|
const modifiedEvents = [
|
|
std('Parent', 1, { subEvents: modifiedSubEvents }),
|
|
];
|
|
const v = runEvents(modifiedEvents);
|
|
for (const name of variableNames) {
|
|
if (v(name) !== expected[name]) {
|
|
expect({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: v(name),
|
|
expected: expected[name],
|
|
}).toEqual({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: expected[name],
|
|
expected: expected[name],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('stress test: inserting at every position inside Repeat does not change outcomes', function () {
|
|
const baseRepeatBody = [
|
|
stdFalse('R_if', 1),
|
|
elseIfFalse('R_ei', 1),
|
|
elseEv('R_else', 0),
|
|
std('R_after', 0),
|
|
];
|
|
|
|
const baseEvents = [repeat(3, baseRepeatBody)];
|
|
|
|
const variableNames = ['R_if', 'R_ei', 'R_else', 'R_after'];
|
|
|
|
const vBase = runEvents(baseEvents);
|
|
const expected = {};
|
|
for (const name of variableNames) {
|
|
expected[name] = vBase(name);
|
|
}
|
|
|
|
const allVariants = variants();
|
|
|
|
for (let pos = 0; pos <= baseRepeatBody.length; pos++) {
|
|
for (const transparentEv of allVariants) {
|
|
const modifiedBody = [...baseRepeatBody];
|
|
modifiedBody.splice(pos, 0, transparentEv);
|
|
const modifiedEvents = [repeat(3, modifiedBody)];
|
|
const v = runEvents(modifiedEvents);
|
|
for (const name of variableNames) {
|
|
if (v(name) !== expected[name]) {
|
|
expect({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: v(name),
|
|
expected: expected[name],
|
|
}).toEqual({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: expected[name],
|
|
expected: expected[name],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('stress test: inserting at every position inside While does not change outcomes', function () {
|
|
const baseWhileBody = [
|
|
stdFalse('W_if', 1),
|
|
elseIfTrue('W_ei', 0),
|
|
elseEv('W_else', 1),
|
|
];
|
|
|
|
const baseEvents = [whileLoop(3, baseWhileBody)];
|
|
|
|
const variableNames = ['Counter', 'W_if', 'W_ei', 'W_else'];
|
|
|
|
const vBase = runEvents(baseEvents);
|
|
const expected = {};
|
|
for (const name of variableNames) {
|
|
expected[name] = vBase(name);
|
|
}
|
|
|
|
expect(expected['Counter']).toBe(3);
|
|
expect(expected['W_ei']).toBe(0);
|
|
expect(expected['W_else']).toBe(0);
|
|
|
|
const allVariants = variants();
|
|
|
|
for (let pos = 0; pos <= baseWhileBody.length; pos++) {
|
|
for (const transparentEv of allVariants) {
|
|
const modifiedBody = [...baseWhileBody];
|
|
modifiedBody.splice(pos, 0, transparentEv);
|
|
const modifiedEvents = [whileLoop(3, modifiedBody)];
|
|
const v = runEvents(modifiedEvents);
|
|
for (const name of variableNames) {
|
|
if (v(name) !== expected[name]) {
|
|
expect({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: v(name),
|
|
expected: expected[name],
|
|
}).toEqual({
|
|
variable: name,
|
|
position: pos,
|
|
insertedType: transparentEv.type,
|
|
actual: expected[name],
|
|
expected: expected[name],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('stress test: multiple transparent events inserted at every position simultaneously', function () {
|
|
const baseEvents = [
|
|
stdFalse('M_if', 1),
|
|
elseIfFalse('M_ei1', 1),
|
|
elseIfTrue('M_ei2', 1),
|
|
elseEv('M_else', 1),
|
|
std('M_after', 1),
|
|
];
|
|
|
|
const variableNames = [
|
|
'M_if', 'M_ei1', 'M_ei2', 'M_else', 'M_after',
|
|
];
|
|
|
|
const vBase = runEvents(baseEvents);
|
|
const expected = {};
|
|
for (const name of variableNames) {
|
|
expected[name] = vBase(name);
|
|
}
|
|
|
|
expect(expected['M_ei2']).toBe(1);
|
|
expect(expected['M_else']).toBe(0);
|
|
expect(expected['M_after']).toBe(1);
|
|
|
|
const clusterEvents = cluster();
|
|
|
|
for (let pos = 0; pos <= baseEvents.length; pos++) {
|
|
const modified = [...baseEvents];
|
|
modified.splice(pos, 0, ...clusterEvents);
|
|
const v = runEvents(modified);
|
|
for (const name of variableNames) {
|
|
if (v(name) !== expected[name]) {
|
|
expect({
|
|
variable: name,
|
|
position: pos,
|
|
actual: v(name),
|
|
expected: expected[name],
|
|
}).toEqual({
|
|
variable: name,
|
|
position: pos,
|
|
actual: expected[name],
|
|
expected: expected[name],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|