mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-21 09:35:27 -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
537 lines
17 KiB
JavaScript
537 lines
17 KiB
JavaScript
const initializeGDevelopJs = require('../../Binaries/embuild/GDevelop.js/libGD.js');
|
|
const { makeMinimalGDJSMock } = require('../TestUtils/GDJSMocks');
|
|
const {
|
|
generateCompiledEventsForEventsFunction,
|
|
generateCompiledEventsFromSerializedEvents,
|
|
} = require('../TestUtils/CodeGenerationHelpers.js');
|
|
|
|
/**
|
|
* Helper generating an event, ready to be unserialized, adding 1 to
|
|
* "TestVariable" of the specified object (or object group).
|
|
*/
|
|
const makeAddOneToObjectTestVariableEvent = (objectName) => ({
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarObjet' },
|
|
parameters: [objectName, 'TestVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
});
|
|
|
|
describe('libGD.js - GDJS Code Generation integration tests', function () {
|
|
let gd = null;
|
|
beforeAll(async () => {
|
|
gd = await initializeGDevelopJs();
|
|
});
|
|
|
|
it('does not crash when generating code with diagnostics', function () {
|
|
const serializerElement = gd.Serializer.fromJSObject([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: [
|
|
'Counter',
|
|
'+',
|
|
'1 + VariableThatDoesNotExist.Test.Test2',
|
|
],
|
|
},
|
|
{
|
|
type: { value: 'SetNumberObjectVariable' },
|
|
parameters: [
|
|
'NonExistingObject',
|
|
'NonExistingVariable',
|
|
'=',
|
|
'123',
|
|
],
|
|
},
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['Counter', '+', '3'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
]);
|
|
|
|
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
|
gd,
|
|
serializerElement
|
|
);
|
|
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
|
|
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
|
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(3);
|
|
});
|
|
|
|
it('does not generate anything for a disabled event and its sub events', function () {
|
|
const serializerElement = gd.Serializer.fromJSObject([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['Counter', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
disabled: true,
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['Counter', '+', '1'],
|
|
},
|
|
],
|
|
events: [
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['Counter', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
|
|
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
|
gd,
|
|
serializerElement
|
|
);
|
|
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
|
|
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
|
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(1);
|
|
});
|
|
|
|
it('generates a working function creating objects', function () {
|
|
const eventsSerializerElement = gd.Serializer.fromJSObject([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'Create' },
|
|
parameters: ['', 'MyObjectA', '0', '0', ''],
|
|
},
|
|
],
|
|
events: [makeAddOneToObjectTestVariableEvent('MyObjectA')],
|
|
},
|
|
makeAddOneToObjectTestVariableEvent('MyObjectA'),
|
|
makeAddOneToObjectTestVariableEvent('MyObjectB'),
|
|
]);
|
|
|
|
const project = new gd.ProjectHelper.createNewGDJSProject();
|
|
const eventsFunction = new gd.EventsFunction();
|
|
eventsFunction
|
|
.getEvents()
|
|
.unserializeFrom(project, eventsSerializerElement);
|
|
|
|
// Add an object parameter to the function:
|
|
eventsFunction
|
|
.getParameters()
|
|
.insertNewParameter('MyObjectA', 0)
|
|
.setType('object');
|
|
eventsFunction
|
|
.getParameters()
|
|
.insertNewParameter('MyObjectB', 1)
|
|
.setType('object');
|
|
|
|
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction
|
|
);
|
|
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
const emptyMyObjectALists = gdjs.Hashtable.newFrom({ MyObjectA: [] });
|
|
const myObjectB = runtimeScene.createObject('MyObjectB');
|
|
const myObjectBLists = gdjs.Hashtable.newFrom({ MyObjectB: [myObjectB] });
|
|
|
|
runCompiledEvents(gdjs, runtimeScene, [
|
|
emptyMyObjectALists,
|
|
myObjectBLists,
|
|
]);
|
|
|
|
// Check that the object was created and its variable was incremented twice.
|
|
expect(runtimeScene.getObjects('MyObjectA')).toHaveLength(1);
|
|
const firstMyObjectA = runtimeScene.getObjects('MyObjectA')[0];
|
|
expect(firstMyObjectA.getVariables().has('TestVariable')).toBe(true);
|
|
expect(
|
|
firstMyObjectA.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
// Object B has nothing to do with the "Create", its variable is just incremented once.
|
|
expect(myObjectB.getVariables().get('TestVariable').getAsNumber()).toBe(1);
|
|
|
|
// Check that if the function is called with the first MyObjectA as parameter, it won't change
|
|
// the way a new object is created...
|
|
const myObjectALists = gdjs.Hashtable.newFrom({
|
|
MyObjectA: [firstMyObjectA],
|
|
});
|
|
runCompiledEvents(gdjs, runtimeScene, [myObjectALists, myObjectBLists]);
|
|
expect(runtimeScene.getObjects('MyObjectA')).toHaveLength(2);
|
|
const secondMyObjectA = runtimeScene.getObjects('MyObjectA')[1];
|
|
expect(secondMyObjectA.getVariables().has('TestVariable')).toBe(true);
|
|
expect(
|
|
secondMyObjectA.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
// ... and the first, "already created" MyObjectA is only picked by the second event.
|
|
// The first event with the "Create" will only pick the "just created" object.
|
|
expect(
|
|
firstMyObjectA.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(3);
|
|
|
|
// Object B has nothing to do with the "Create", its variable is just incremented again.
|
|
expect(myObjectB.getVariables().get('TestVariable').getAsNumber()).toBe(2);
|
|
|
|
eventsFunction.delete();
|
|
project.delete();
|
|
});
|
|
|
|
it('generates working functions with groups', function () {
|
|
// Create events that increment twice the variable of a group, and
|
|
// only once for the 3rd parameter.
|
|
const eventsSerializerElement = gd.Serializer.fromJSObject([
|
|
makeAddOneToObjectTestVariableEvent('MyObjectGroup'),
|
|
makeAddOneToObjectTestVariableEvent('MyObjectGroup'),
|
|
makeAddOneToObjectTestVariableEvent('ObjectParam3'),
|
|
]);
|
|
|
|
const project = new gd.ProjectHelper.createNewGDJSProject();
|
|
const eventsFunction = new gd.EventsFunction();
|
|
eventsFunction
|
|
.getEvents()
|
|
.unserializeFrom(project, eventsSerializerElement);
|
|
const group = eventsFunction
|
|
.getObjectGroups()
|
|
.insertNew('MyObjectGroup', 0);
|
|
group.setName('MyObjectGroup');
|
|
group.addObject('ObjectParam1');
|
|
group.addObject('ObjectParam2');
|
|
|
|
eventsFunction
|
|
.getParameters()
|
|
.insertNewParameter('ObjectParam1', 0)
|
|
.setType('object');
|
|
eventsFunction
|
|
.getParameters()
|
|
.insertNewParameter('ObjectParam2', 1)
|
|
.setType('object');
|
|
eventsFunction
|
|
.getParameters()
|
|
.insertNewParameter('ObjectParam3', 2)
|
|
.setType('object');
|
|
|
|
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction
|
|
);
|
|
|
|
// Check that when running the function, the group works in the function.
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
const myObjectA = runtimeScene.createObject('MyObjectA');
|
|
const myObjectB = runtimeScene.createObject('MyObjectB');
|
|
const myObjectC = runtimeScene.createObject('MyObjectC');
|
|
const myObjectD = runtimeScene.createObject('MyObjectD');
|
|
const objectParam1Lists = gdjs.Hashtable.newFrom({
|
|
// The first parameter is itself a collection of multiple objects
|
|
// with different names.
|
|
MyObjectA: [myObjectA],
|
|
MyObjectB: [myObjectB],
|
|
});
|
|
const objectParam2Lists = gdjs.Hashtable.newFrom({
|
|
MyObjectC: [myObjectC],
|
|
});
|
|
const objectParam3Lists = gdjs.Hashtable.newFrom({
|
|
MyObjectD: [myObjectD],
|
|
});
|
|
|
|
runCompiledEvents(gdjs, runtimeScene, [
|
|
objectParam1Lists,
|
|
objectParam2Lists,
|
|
objectParam3Lists,
|
|
]);
|
|
|
|
expect(runtimeScene.getObjects('MyObjectA')).toHaveLength(1);
|
|
const firstMyObjectA = runtimeScene.getObjects('MyObjectA')[0];
|
|
expect(
|
|
firstMyObjectA.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
expect(runtimeScene.getObjects('MyObjectB')).toHaveLength(1);
|
|
const firstMyObjectB = runtimeScene.getObjects('MyObjectB')[0];
|
|
expect(
|
|
firstMyObjectB.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
expect(runtimeScene.getObjects('MyObjectC')).toHaveLength(1);
|
|
const firstMyObjectC = runtimeScene.getObjects('MyObjectC')[0];
|
|
expect(
|
|
firstMyObjectC.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
expect(runtimeScene.getObjects('MyObjectD')).toHaveLength(1);
|
|
const firstMyObjectD = runtimeScene.getObjects('MyObjectD')[0];
|
|
expect(
|
|
firstMyObjectD.getVariables().get('TestVariable').getAsNumber()
|
|
).toBe(1);
|
|
|
|
eventsFunction.delete();
|
|
project.delete();
|
|
});
|
|
|
|
it('generates a working function with BuiltinCommonInstructions::Once', function () {
|
|
// Event to create an object, then add
|
|
const eventsSerializerElement = gd.Serializer.fromJSObject([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [
|
|
{
|
|
type: {
|
|
value: 'BuiltinCommonInstructions::Once',
|
|
},
|
|
parameters: [],
|
|
},
|
|
],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['SuccessVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [
|
|
{
|
|
type: {
|
|
value: 'BuiltinCommonInstructions::Once',
|
|
},
|
|
parameters: [],
|
|
},
|
|
],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['SuccessVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
]);
|
|
|
|
const project = new gd.ProjectHelper.createNewGDJSProject();
|
|
const eventsFunction = new gd.EventsFunction();
|
|
eventsFunction
|
|
.getEvents()
|
|
.unserializeFrom(project, eventsSerializerElement);
|
|
|
|
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction
|
|
);
|
|
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
|
|
// Check that after running the compiled events twice, actions were
|
|
// executed only twice.
|
|
expect(runtimeScene.getVariables().has('SuccessVariable')).toBe(true);
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
eventsFunction.delete();
|
|
project.delete();
|
|
});
|
|
|
|
it('generates a working function with BuiltinCommonInstructions::Once with stable IDs across compilation', function () {
|
|
// Create events using the Trigger Once condition.
|
|
const eventsSerializerElement = gd.Serializer.fromJSON(
|
|
JSON.stringify([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [
|
|
{
|
|
type: {
|
|
value: 'BuiltinCommonInstructions::Once',
|
|
},
|
|
parameters: [],
|
|
},
|
|
],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['SuccessVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [
|
|
{
|
|
type: {
|
|
value: 'BuiltinCommonInstructions::Once',
|
|
},
|
|
parameters: [],
|
|
},
|
|
],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['SuccessVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
])
|
|
);
|
|
|
|
const project = new gd.ProjectHelper.createNewGDJSProject();
|
|
const eventsFunction = new gd.EventsFunction();
|
|
eventsFunction
|
|
.getEvents()
|
|
.unserializeFrom(project, eventsSerializerElement);
|
|
|
|
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction
|
|
);
|
|
|
|
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents(gdjs, runtimeScene, []);
|
|
|
|
// Check that after running the compiled events twice, actions were
|
|
// executed only twice.
|
|
expect(runtimeScene.getVariables().has('SuccessVariable')).toBe(true);
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
// Check that compiling again the events will result in stable ids for Trigger Once conditions
|
|
// (trigger once should not be launched again), even after a copy of the events function.
|
|
const eventsFunctionCopy = eventsFunction.clone();
|
|
const runCompiledEvents2 = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunctionCopy
|
|
);
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents2(gdjs, runtimeScene, []);
|
|
runtimeScene.getOnceTriggers().startNewFrame();
|
|
runCompiledEvents2(gdjs, runtimeScene, []);
|
|
|
|
expect(runtimeScene.getVariables().has('SuccessVariable')).toBe(true);
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
eventsFunction.delete();
|
|
eventsFunctionCopy.delete();
|
|
project.delete();
|
|
});
|
|
|
|
it('generates a lifecycle function that registers itself, and unregister itself if hot-reloaded', function () {
|
|
// Event to create an object, then add
|
|
const eventsSerializerElement = gd.Serializer.fromJSObject([
|
|
{
|
|
type: 'BuiltinCommonInstructions::Standard',
|
|
conditions: [],
|
|
actions: [
|
|
{
|
|
type: { value: 'ModVarScene' },
|
|
parameters: ['SuccessVariable', '+', '1'],
|
|
},
|
|
],
|
|
events: [],
|
|
},
|
|
]);
|
|
|
|
const project = new gd.ProjectHelper.createNewGDJSProject();
|
|
const eventsFunction = new gd.EventsFunction();
|
|
eventsFunction.setName('onScenePreEvents');
|
|
eventsFunction
|
|
.getEvents()
|
|
.unserializeFrom(project, eventsSerializerElement);
|
|
|
|
const runCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction,
|
|
{ exceptionallyPersistNamespaceOnGdjs: true }
|
|
);
|
|
|
|
const { gdjs, runtimeScene, mocks } = makeMinimalGDJSMock();
|
|
runCompiledEvents(
|
|
gdjs,
|
|
runtimeScene /*, Don't pass arguments to not run the function. */
|
|
);
|
|
mocks.runRuntimeScenePreEventsCallbacks();
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(1);
|
|
mocks.runRuntimeScenePreEventsCallbacks();
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(2);
|
|
|
|
// Simulate a hot reloading by recompiling the function and running it again.
|
|
const runHotReloadedCompiledEvents = generateCompiledEventsForEventsFunction(
|
|
gd,
|
|
project,
|
|
eventsFunction,
|
|
{ exceptionallyPersistNamespaceOnGdjs: true }
|
|
);
|
|
runHotReloadedCompiledEvents(
|
|
gdjs,
|
|
runtimeScene /*, Don't pass arguments to not run the function. */
|
|
);
|
|
|
|
// Ensure that when we call the callbacks, it's called only once (not registered twice).
|
|
mocks.runRuntimeScenePreEventsCallbacks();
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(3);
|
|
mocks.runRuntimeScenePreEventsCallbacks();
|
|
expect(
|
|
runtimeScene.getVariables().get('SuccessVariable').getAsNumber()
|
|
).toBe(4);
|
|
|
|
eventsFunction.delete();
|
|
project.delete();
|
|
});
|
|
});
|