Files
GDevelop/GDevelop.js/__tests__/Core.js

4773 lines
172 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const initializeGDevelopJs = require('../../Binaries/embuild/GDevelop.js/libGD.js');
const path = require('path');
const {
makeFakeAbstractFileSystem,
} = require('../TestUtils/FakeAbstractFileSystem');
const extend = require('extend');
const mapFor = /*:: <T> */ (
start /*: number */,
end /*: number */,
func /*: (number) => T */
) /*: Array<T> */ => {
const result = [];
for (let i = start; i < end; i++) {
result.push(func(i));
}
return result;
};
describe('libGD.js', function () {
let gd = null;
beforeAll(async () => {
gd = await initializeGDevelopJs();
});
describe('gd.VersionWrapper', function () {
it('can return the version number of the library', function () {
expect(typeof gd.VersionWrapper.major()).toBe('number');
expect(typeof gd.VersionWrapper.minor()).toBe('number');
expect(typeof gd.VersionWrapper.build()).toBe('number');
expect(typeof gd.VersionWrapper.revision()).toBe('number');
expect(typeof gd.VersionWrapper.fullString()).toBe('string');
});
});
describe('gd.Project', function () {
let project = null;
beforeAll(() => (project = gd.ProjectHelper.createNewGDJSProject()));
it('has properties that can be read and changed', function () {
project.setName('My super project');
expect(project.getName()).toBe('My super project');
project.setDescription("This is a great game I'm really proud of");
expect(project.getDescription()).toBe(
"This is a great game I'm really proud of"
);
project.setVersion('1.2.34');
expect(project.getVersion()).toBe('1.2.34');
project.setPackageName('com.test.package');
expect(project.getPackageName()).toBe('com.test.package');
project.setOrientation('portrait');
expect(project.getOrientation()).toBe('portrait');
project.setOrientation('landscape');
expect(project.getOrientation()).toBe('landscape');
project.setAuthor('Me');
expect(project.getAuthor()).toBe('Me');
project.setMaximumFPS(15);
expect(project.getMaximumFPS()).toBe(15);
project.setMinimumFPS(15);
expect(project.getMinimumFPS()).toBe(15);
project.setFolderProject(true);
expect(project.isFolderProject()).toBe(true);
project.setFolderProject(false);
expect(project.isFolderProject()).toBe(false);
});
it('can store loading screen setup', function () {
project.getLoadingScreen().showGDevelopLogoDuringLoadingScreen(true);
expect(
project.getLoadingScreen().isGDevelopLogoShownDuringLoadingScreen()
).toBe(true);
project.getLoadingScreen().showGDevelopLogoDuringLoadingScreen(false);
expect(
project.getLoadingScreen().isGDevelopLogoShownDuringLoadingScreen()
).toBe(false);
});
it('handles layouts', function () {
expect(project.hasLayoutNamed('Scene')).toBe(false);
project.insertNewLayout('Scene', 0);
expect(project.hasLayoutNamed('Scene')).toBe(true);
expect(project.getLayout('Scene').getName()).toBe('Scene');
project.removeLayout('Scene');
expect(project.hasLayoutNamed('Scene')).toBe(false);
});
it('handles external events', function () {
expect(project.hasExternalEventsNamed('My events')).toBe(false);
project.insertNewExternalEvents('My events', 0);
expect(project.hasExternalEventsNamed('My events')).toBe(true);
expect(project.getExternalEvents('My events').getName()).toBe(
'My events'
);
project.removeExternalEvents('My events');
expect(project.hasExternalEventsNamed('My events')).toBe(false);
});
it('handles external layouts', function () {
expect(project.hasExternalLayoutNamed('My layout')).toBe(false);
project.insertNewExternalLayout('My layout', 0);
expect(project.hasExternalLayoutNamed('My layout')).toBe(true);
expect(project.getExternalLayout('My layout').getName()).toBe(
'My layout'
);
project.removeExternalLayout('My layout');
expect(project.hasExternalLayoutNamed('My layout')).toBe(false);
});
it('should validate object names', function () {
expect(gd.Project.isNameSafe('ThisNameIs_Ok_123')).toBe(true);
expect(gd.Project.isNameSafe('ThisNameIs_👍_123')).toBe(true);
expect(gd.Project.isNameSafe('ThisName IsNot_Ok_123')).toBe(false);
expect(gd.Project.isNameSafe('ThisName()IsNot_Ok_123')).toBe(false);
expect(gd.Project.isNameSafe('ThisNameIsNot_Ok!')).toBe(false);
expect(gd.Project.isNameSafe('1ThisNameIsNot_Ok_123')).toBe(false);
expect(gd.Project.getSafeName('ThisNameIs_Ok_123')).toBe(
'ThisNameIs_Ok_123'
);
expect(gd.Project.getSafeName('ThisNameIs_👍_123')).toBe(
'ThisNameIs_👍_123'
);
expect(gd.Project.getSafeName('ThisName IsNot_Ok_123')).toBe(
'ThisName_IsNot_Ok_123'
);
expect(gd.Project.getSafeName('ThisName()IsNot_Ok_123')).toBe(
'ThisName__IsNot_Ok_123'
);
expect(gd.Project.getSafeName('ThisNameIsNot_Ok!')).toBe(
'ThisNameIsNot_Ok_'
);
expect(gd.Project.getSafeName('1ThisNameIsNot_Ok_123')).toBe(
'_1ThisNameIsNot_Ok_123'
);
expect(gd.Project.getSafeName('官话 name')).toBe('官话_name');
expect(gd.Project.getSafeName('')).toBe('Unnamed');
expect(gd.Project.getSafeName('9')).toBe('_9');
});
it('should have a list of extensions', function () {
expect(
gd.UsedExtensionsFinder.scanProject(project)
.getUsedExtensions()
.toNewVectorString()
.toJSArray()
).toEqual([]);
project.insertNewObject(project, 'Sprite', 'MyObject', 0);
expect(
gd.UsedExtensionsFinder.scanProject(project)
.getUsedExtensions()
.toNewVectorString()
.toJSArray()
).toEqual([
'AnimatableCapability',
'EffectCapability',
'FlippableCapability',
'OpacityCapability',
'ResizableCapability',
'ScalableCapability',
'Sprite',
]);
});
it('handles events functions extensions', function () {
expect(project.hasEventsFunctionsExtensionNamed('Ext')).toBe(false);
project.insertNewEventsFunctionsExtension('Ext', 0);
expect(project.hasEventsFunctionsExtensionNamed('Ext')).toBe(true);
expect(project.getEventsFunctionsExtension('Ext').getName()).toBe('Ext');
project.removeEventsFunctionsExtension('Ext');
expect(project.hasEventsFunctionsExtensionNamed('Ext')).toBe(false);
});
afterAll(function () {
project.delete();
});
});
describe('gd.Layout', function () {
let project = null;
let layout = null;
beforeAll(() => {
project = gd.ProjectHelper.createNewGDJSProject();
layout = project.insertNewLayout('Scene', 0);
});
it('can have a new name', function () {
expect(layout.getName()).toBe('Scene');
layout.setName('My super layout');
expect(layout.getName()).toBe('My super layout');
});
it('can have a name with UTF8 characters', function () {
layout.setName('Scene with a 官话 name');
expect(layout.getName()).toBe('Scene with a 官话 name');
});
it('can store events', function () {
let evts = layout.getEvents();
expect(evts.getEventsCount()).toBe(0);
let evt = evts.insertNewEvent(
project,
'BuiltinCommonInstructions::Standard',
0
);
expect(evts.getEventsCount()).toBe(1);
evt
.getSubEvents()
.insertNewEvent(project, 'BuiltinCommonInstructions::Standard', 0);
expect(evts.getEventAt(0).getSubEvents().getEventsCount()).toBe(1);
});
it('can have objects', function () {
let object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
let object2 = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject2',
1
);
expect(layout.getObjectAt(0).ptr).toBe(object.ptr);
expect(layout.getObjectAt(1).ptr).toBe(object2.ptr);
expect(layout.getObjectAt(0).getType()).toBe('Sprite');
expect(layout.getObjectAt(1).getType()).toBe('TextObject::Text');
});
afterAll(function () {
project.delete();
});
});
describe('gd.Layer', function () {
it('can have a name and visibility', function () {
const layer = new gd.Layer();
layer.setName('GUI');
layer.setVisibility(false);
expect(layer.getName()).toBe('GUI');
expect(layer.getVisibility()).toBe(false);
layer.delete();
});
it('can have effects', function () {
const layer = new gd.Layer();
const effects = layer.getEffects();
expect(effects.hasEffectNamed('EffectThatDoesNotExist')).toBe(false);
expect(effects.getEffectsCount()).toBe(0);
expect(effects.hasEffectNamed('EffectThatDoesNotExist')).toBe(false);
expect(effects.getEffectsCount()).toBe(0);
effects.insertNewEffect('MyEffect', 0);
expect(effects.hasEffectNamed('EffectThatDoesNotExist')).toBe(false);
expect(effects.hasEffectNamed('MyEffect')).toBe(true);
expect(effects.getEffectsCount()).toBe(1);
expect(effects.getEffectPosition('MyEffect')).toBe(0);
const effect2 = new gd.Effect();
effect2.setName('MyEffect2');
effects.insertEffect(effect2, 1);
expect(effects.hasEffectNamed('MyEffect2')).toBe(true);
expect(effects.getEffectsCount()).toBe(2);
expect(effects.getEffectPosition('MyEffect')).toBe(0);
expect(effects.getEffectPosition('MyEffect2')).toBe(1);
effects.swapEffects(0, 1);
expect(effects.getEffectPosition('MyEffect2')).toBe(0);
expect(effects.getEffectPosition('MyEffect')).toBe(1);
const effect3 = new gd.Effect();
effect3.setName('MyEffect3');
effects.insertEffect(effect3, 2);
expect(effects.hasEffectNamed('MyEffect3')).toBe(true);
expect(effects.getEffectsCount()).toBe(3);
effects.moveEffect(2, 0);
expect(effects.getEffectPosition('MyEffect3')).toBe(0);
expect(effects.getEffectPosition('MyEffect2')).toBe(1);
expect(effects.getEffectPosition('MyEffect')).toBe(2);
layer.delete();
});
it('can be serialized', function () {
const layer = new gd.Layer();
const layer2 = new gd.Layer();
layer.setName('GUI');
layer.setVisibility(false);
layer.getEffects().insertNewEffect('MyEffect', 0);
layer.setCameraCount(1);
const element = new gd.SerializerElement();
layer.serializeTo(element);
for (let i = 0; i < 5; ++i) {
// Repeat multiple time to check idempotency.
layer2.unserializeFrom(element);
expect(layer2.getName()).toBe('GUI');
expect(layer2.getVisibility()).toBe(false);
expect(layer2.getEffects().getEffectsCount()).toBe(1);
expect(layer2.getEffects().getEffectAt(0).getName()).toBe('MyEffect');
expect(layer2.getCameraCount()).toBe(1);
}
layer.delete();
layer2.delete();
});
});
describe('gd.Effect', function () {
it('can have a name, effect name and parameters', function () {
const effect = new gd.Effect();
effect.setName('MyEffect');
effect.setEffectType('Sepia');
expect(effect.getName()).toBe('MyEffect');
expect(effect.getEffectType()).toBe('Sepia');
effect.setDoubleParameter('Brightness', 1);
effect.setDoubleParameter('Darkness', 0.3);
effect.setDoubleParameter('Param3', 6);
expect(effect.getAllDoubleParameters().keys().size()).toBe(3);
expect(effect.getDoubleParameter('Brightness')).toBe(1);
expect(effect.getDoubleParameter('Darkness')).toBe(0.3);
expect(effect.getDoubleParameter('Param3')).toBe(6);
effect.setStringParameter('SomeImage', 'myImageResource');
expect(effect.getStringParameter('SomeImage')).toBe('myImageResource');
expect(effect.getAllStringParameters().keys().size()).toBe(1);
effect.setBooleanParameter('SomeBoolean', true);
expect(effect.getBooleanParameter('SomeBoolean')).toBe(true);
effect.setBooleanParameter('SomeBoolean', false);
expect(effect.getBooleanParameter('SomeBoolean')).toBe(false);
expect(effect.getAllBooleanParameters().keys().size()).toBe(1);
effect.delete();
});
});
describe('gd.ObjectsContainer (using gd.Layout)', function () {
let project = null;
beforeAll(() => (project = gd.ProjectHelper.createNewGDJSProject()));
it('can move objects', function () {
let layout = project.insertNewLayout('Scene', 0);
let object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
let object2 = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject2',
1
);
let object3 = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject3',
2
);
expect(layout.getObjectAt(0).getName()).toBe('MyObject');
expect(layout.getObjectAt(1).getName()).toBe('MyObject2');
expect(layout.getObjectAt(2).getName()).toBe('MyObject3');
layout.moveObject(0, 2);
expect(layout.getObjectAt(0).getName()).toBe('MyObject2');
expect(layout.getObjectAt(1).getName()).toBe('MyObject3');
expect(layout.getObjectAt(2).getName()).toBe('MyObject');
layout.moveObject(0, 0);
expect(layout.getObjectAt(0).getName()).toBe('MyObject2');
expect(layout.getObjectAt(1).getName()).toBe('MyObject3');
expect(layout.getObjectAt(2).getName()).toBe('MyObject');
layout.moveObject(1, 0);
expect(layout.getObjectAt(0).getName()).toBe('MyObject3');
expect(layout.getObjectAt(1).getName()).toBe('MyObject2');
expect(layout.getObjectAt(2).getName()).toBe('MyObject');
layout.moveObject(0, 999);
expect(layout.getObjectAt(0).getName()).toBe('MyObject3');
expect(layout.getObjectAt(1).getName()).toBe('MyObject2');
expect(layout.getObjectAt(2).getName()).toBe('MyObject');
});
it('can find position of objects', function () {
let layout = project.insertNewLayout('Scene2', 0);
let object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
let object2 = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject2',
1
);
let object3 = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject3',
2
);
expect(layout.getObjectPosition('MyObject')).toBe(0);
expect(layout.getObjectPosition('MyObject2')).toBe(1);
expect(layout.getObjectPosition('MyObject3')).toBe(2);
expect(layout.getObjectPosition('MyObject4')).toBe(-1);
});
afterAll(function () {
project.delete();
});
});
describe('gd.ObjectsContainer', function () {
it('can move objects between containers, without moving them in memory', function () {
const project = new gd.ProjectHelper.createNewGDJSProject();
// Prepare two containers, one with 3 objects and one empty
const objectsContainer1 = new gd.ObjectsContainer();
const rootFolder1 = objectsContainer1.getRootFolder();
const objectsContainer2 = new gd.ObjectsContainer();
const rootFolder2 = objectsContainer2.getRootFolder();
const subFolder2 = rootFolder2.insertNewFolder('Folder', 1);
const mySpriteObject = objectsContainer1.insertNewObject(
project,
'Sprite',
'MySprite',
0
);
const mySprite2Object = objectsContainer1.insertNewObject(
project,
'Sprite',
'MySprite2',
1
);
const mySprite3Object = objectsContainer1.insertNewObject(
project,
'Sprite',
'MySprite3',
2
);
expect(objectsContainer1.getObjectsCount()).toBe(3);
expect(objectsContainer2.getObjectsCount()).toBe(0);
expect(rootFolder1.getChildrenCount()).toBe(3);
expect(rootFolder2.getChildrenCount()).toBe(1);
// Find the pointer to the objects in memory
const mySpriteObjectPtr = gd.getPointer(objectsContainer1.getObjectAt(0));
const mySprite2ObjectPtr = gd.getPointer(
objectsContainer1.getObjectAt(1)
);
const mySprite3ObjectPtr = gd.getPointer(
objectsContainer1.getObjectAt(2)
);
const mySpriteObjectFolderOrObject = rootFolder1.getChildAt(0);
const mySprite2ObjectFolderOrObject = rootFolder1.getChildAt(1);
const mySprite3ObjectFolderOrObject = rootFolder1.getChildAt(2);
const mySpriteObjectFolderOrObjectPtr = gd.getPointer(
mySpriteObjectFolderOrObject
);
const mySprite2ObjectFolderOrObjectPtr = gd.getPointer(
mySprite2ObjectFolderOrObject
);
const mySprite3ObjectFolderOrObjectPtr = gd.getPointer(
mySprite3ObjectFolderOrObject
);
// Move objects between containers
objectsContainer1.moveObjectFolderOrObjectToAnotherContainerInFolder(
mySprite2ObjectFolderOrObject,
objectsContainer2,
rootFolder2,
0
);
expect(objectsContainer1.getObjectsCount()).toBe(2);
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite');
expect(objectsContainer1.getObjectAt(1).getName()).toBe('MySprite3');
expect(objectsContainer2.getObjectsCount()).toBe(1);
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite2');
expect(rootFolder2.hasObjectNamed('MySprite2')).toBe(true);
expect(rootFolder2.getChildrenCount()).toBe(2);
expect(gd.getPointer(rootFolder2.getObjectChild('MySprite2'))).toBe(
mySprite2ObjectFolderOrObjectPtr
);
expect(rootFolder2.getObjectChild('MySprite2')).toBe(
mySprite2ObjectFolderOrObject
);
expect(mySprite2ObjectFolderOrObject.getParent()).toBe(rootFolder2);
// Move object in sub folder.
objectsContainer1.moveObjectFolderOrObjectToAnotherContainerInFolder(
mySprite3ObjectFolderOrObject,
objectsContainer2,
subFolder2,
0
);
expect(objectsContainer1.getObjectsCount()).toBe(1);
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite');
expect(objectsContainer2.getObjectsCount()).toBe(2);
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite2');
expect(objectsContainer2.getObjectAt(1).getName()).toBe('MySprite3');
expect(subFolder2.hasObjectNamed('MySprite3')).toBe(true);
expect(subFolder2.getChildrenCount()).toBe(1);
expect(gd.getPointer(subFolder2.getObjectChild('MySprite3'))).toBe(
mySprite3ObjectFolderOrObjectPtr
);
expect(mySprite3ObjectFolderOrObject.getParent()).toBe(subFolder2);
// Check that the object in memory are the same, even if moved to another container
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(
mySpriteObjectPtr
);
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(
mySprite2ObjectPtr
);
expect(gd.getPointer(objectsContainer2.getObjectAt(1))).toBe(
mySprite3ObjectPtr
);
expect(gd.getPointer(rootFolder2.getObjectChild('MySprite2'))).toBe(
mySprite2ObjectFolderOrObjectPtr
);
expect(rootFolder2.getObjectChild('MySprite2')).toBe(
mySprite2ObjectFolderOrObject
);
// Move back first object to first container
objectsContainer2.moveObjectFolderOrObjectToAnotherContainerInFolder(
mySprite2ObjectFolderOrObject,
objectsContainer1,
rootFolder1,
0
);
expect(objectsContainer1.getObjectsCount()).toBe(2);
expect(objectsContainer1.getObjectAt(0).getName()).toBe('MySprite');
expect(objectsContainer1.getObjectAt(1).getName()).toBe('MySprite2');
expect(objectsContainer2.getObjectsCount()).toBe(1);
expect(objectsContainer2.getObjectAt(0).getName()).toBe('MySprite3');
expect(rootFolder2.hasObjectNamed('MySprite2')).toBe(false);
expect(rootFolder2.getChildrenCount()).toBe(1);
expect(rootFolder1.getChildrenCount()).toBe(2);
expect(rootFolder1.getChildAt(0).getObject().getName()).toBe('MySprite2');
expect(rootFolder1.getChildAt(1).getObject().getName()).toBe('MySprite');
expect(rootFolder1.hasObjectNamed('MySprite2')).toBe(true);
expect(mySprite2ObjectFolderOrObject.getParent()).toBe(rootFolder1);
// Check again that the object in memory are the same, even if moved to another container
expect(gd.getPointer(objectsContainer1.getObjectAt(0))).toBe(
mySpriteObjectPtr
);
expect(gd.getPointer(objectsContainer1.getObjectAt(1))).toBe(
mySprite2ObjectPtr
);
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(
mySprite3ObjectPtr
);
expect(gd.getPointer(rootFolder1.getObjectChild('MySprite2'))).toBe(
mySprite2ObjectFolderOrObjectPtr
);
expect(gd.getPointer(rootFolder1.getObjectChild('MySprite'))).toBe(
mySpriteObjectFolderOrObjectPtr
);
expect(gd.getPointer(subFolder2.getObjectChild('MySprite3'))).toBe(
mySprite3ObjectFolderOrObjectPtr
);
project.delete();
});
it('enumerates folders and objects', function () {
const project = new gd.ProjectHelper.createNewGDJSProject();
// Prepare two containers, one with 3 objects and one empty
const objectsContainer = new gd.ObjectsContainer();
const rootFolder = objectsContainer.getRootFolder();
const folder = rootFolder.insertNewFolder('Folder 1', 0);
const mySpriteObject = objectsContainer.insertNewObjectInFolder(
project,
'Sprite',
'MySprite',
folder,
0
);
const subFolder = folder.insertNewFolder('Sub Folder 1', 1);
const mySprite2Object = objectsContainer.insertNewObjectInFolder(
project,
'Sprite',
'MySprite2',
subFolder,
0
);
const mySprite3Object = objectsContainer.insertNewObjectInFolder(
project,
'Sprite',
'MySprite3',
subFolder,
1
);
const subSubFolder = subFolder.insertNewFolder('Sub Sub Folder 1', 2);
expect(objectsContainer.getObjectsCount()).toBe(3);
expect(rootFolder.getChildrenCount()).toBe(1);
expect(folder.getChildrenCount()).toBe(2);
expect(subFolder.getChildrenCount()).toBe(3);
const vectorObjectFolderOrObjects = objectsContainer.getAllObjectFolderOrObjects();
expect(vectorObjectFolderOrObjects.size()).toBe(6);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(0))).toBe(
gd.getPointer(folder)
);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(1))).toBe(
gd.getPointer(folder.getChildAt(0))
);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(2))).toBe(
gd.getPointer(subFolder)
);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(3))).toBe(
gd.getPointer(subFolder.getChildAt(0))
);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(4))).toBe(
gd.getPointer(subFolder.getChildAt(1))
);
expect(gd.getPointer(vectorObjectFolderOrObjects.at(5))).toBe(
gd.getPointer(subSubFolder)
);
objectsContainer.delete();
project.delete();
});
});
describe('gd.InitialInstancesContainer', function () {
let container = null;
let containerCopy = null;
beforeAll(() => {
container = new gd.InitialInstancesContainer();
});
it('initial state', function () {
expect(container.getInstancesCount()).toBe(0);
});
it('adding instances', function () {
let instance = container.insertNewInitialInstance();
instance.setObjectName('MyObject1');
instance.setZOrder(10);
let instance2 = new gd.InitialInstance();
instance2.setObjectName('MyObject2');
instance2 = container.insertInitialInstance(instance2);
let instance3 = container.insertNewInitialInstance();
instance3.setObjectName('MyObject3');
instance3.setZOrder(-1);
instance3.setLayer('OtherLayer');
expect(container.getInstancesCount()).toBe(3);
});
it('iterating', function () {
let i = 0;
let functor = new gd.InitialInstanceJSFunctor();
functor.invoke = function (instance) {
instance = gd.wrapPointer(instance, gd.InitialInstance);
expect(
(i === 0 && instance.getObjectName() === 'MyObject1') ||
(i === 1 && instance.getObjectName() === 'MyObject2') ||
(i === 2 && instance.getObjectName() === 'MyObject3')
).toBe(true);
i++;
};
container.iterateOverInstances(functor);
});
it('can rename instances', function () {
container.renameInstancesOfObject('MyObject1', 'MyObject');
let i = 0;
let functor = new gd.InitialInstanceJSFunctor();
functor.invoke = function (instance) {
instance = gd.wrapPointer(instance, gd.InitialInstance);
expect(
(i === 0 && instance.getObjectName() === 'MyObject') ||
(i === 1 && instance.getObjectName() === 'MyObject2') ||
(i === 2 && instance.getObjectName() === 'MyObject3')
).toBe(true);
i++;
};
container.iterateOverInstances(functor);
});
it('iterating with z ordering', function () {
let i = 0;
let functor = new gd.InitialInstanceJSFunctor();
functor.invoke = function (instance) {
instance = gd.wrapPointer(instance, gd.InitialInstance);
expect(
(i === 0 && instance.getObjectName() === 'MyObject2') ||
(i === 1 && instance.getObjectName() === 'MyObject')
).toBe(true);
i++;
};
container.iterateOverInstancesWithZOrdering(functor, '');
});
it('moving from layers to another', function () {
container.moveInstancesToLayer('OtherLayer', 'YetAnotherLayer');
let functor = new gd.InitialInstanceJSFunctor();
functor.invoke = function (instance) {
instance = gd.wrapPointer(instance, gd.InitialInstance);
expect(instance.getObjectName()).toBe('MyObject3');
};
container.iterateOverInstancesWithZOrdering(functor, 'YetAnotherLayer');
});
it('can be cloned', function () {
containerCopy = container.clone();
expect(containerCopy.getInstancesCount()).toBe(3);
let instance = containerCopy.insertNewInitialInstance();
instance.setObjectName('MyObject4');
expect(containerCopy.getInstancesCount()).toBe(4);
expect(container.getInstancesCount()).toBe(3);
containerCopy.delete();
containerCopy = null;
});
it('removing instances', function () {
container.removeInitialInstancesOfObject('MyObject');
expect(container.getInstancesCount()).toBe(2);
});
it('removing instances on a layer', function () {
container.removeAllInstancesOnLayer('YetAnotherLayer');
expect(container.getInstancesCount()).toBe(1);
});
it('can be serialized', function () {
expect(container.serializeTo).not.toBe(undefined);
expect(container.unserializeFrom).not.toBe(undefined);
});
afterAll(function () {
container.delete();
});
});
describe('gd.InitialInstance', function () {
let project = null;
let layout = null;
let initialInstance = null;
beforeAll(() => {
project = gd.ProjectHelper.createNewGDJSProject();
layout = project.insertNewLayout('Scene', 0);
layout.insertNewObject(project, 'Sprite', 'MySpriteObject', 0);
initialInstance = layout.getInitialInstances().insertNewInitialInstance();
});
it('properties', function () {
initialInstance.setObjectName('MySpriteObject');
expect(initialInstance.getObjectName()).toBe('MySpriteObject');
initialInstance.setX(150);
expect(initialInstance.getX()).toBe(150);
initialInstance.setY(140);
expect(initialInstance.getY()).toBe(140);
initialInstance.setAngle(45);
expect(initialInstance.getAngle()).toBe(45);
initialInstance.setZOrder(12);
expect(initialInstance.getZOrder()).toBe(12);
initialInstance.setLayer('MyLayer');
expect(initialInstance.getLayer()).toBe('MyLayer');
initialInstance.setLocked(true);
expect(initialInstance.isLocked()).toBe(true);
initialInstance.setHasCustomSize(true);
expect(initialInstance.hasCustomSize()).toBe(true);
initialInstance.setCustomWidth(34);
expect(initialInstance.getCustomWidth()).toBe(34);
initialInstance.setCustomHeight(30);
expect(initialInstance.getCustomHeight()).toBe(30);
expect(initialInstance.hasCustomDepth()).toBe(false);
});
it('Sprite object custom properties', function () {
initialInstance.updateCustomProperty('animation', '2', project, layout);
expect(
initialInstance
.getCustomProperties(project, layout)
.get('animation')
.getValue()
).toBe('2');
expect(initialInstance.getRawDoubleProperty('animation')).toBe(2);
});
it('can be serialized', function () {
expect(initialInstance.serializeTo).not.toBe(undefined);
expect(initialInstance.unserializeFrom).not.toBe(undefined);
let element = new gd.SerializerElement();
initialInstance.serializeTo(element);
let initialInstance2 = layout
.getInitialInstances()
.insertNewInitialInstance();
initialInstance2.unserializeFrom(element);
expect(initialInstance2.getObjectName()).toBe('MySpriteObject');
expect(initialInstance2.getX()).toBe(150);
expect(initialInstance2.getY()).toBe(140);
expect(initialInstance2.getZ()).toBe(0);
expect(initialInstance2.getAngle()).toBe(45);
expect(initialInstance2.getRotationX()).toBe(0);
expect(initialInstance2.getRotationY()).toBe(0);
expect(initialInstance2.getZOrder()).toBe(12);
expect(initialInstance2.getLayer()).toBe('MyLayer');
expect(initialInstance2.isLocked()).toBe(true);
expect(initialInstance2.hasCustomSize()).toBe(true);
expect(initialInstance2.hasCustomDepth()).toBe(false);
expect(initialInstance2.getCustomWidth()).toBe(34);
expect(initialInstance2.getCustomHeight()).toBe(30);
expect(initialInstance2.getCustomDepth()).toBe(0);
});
it('can have 3D properties migrated from number properties', function () {
const element = gd.Serializer.fromJSObject({
angle: 2,
customSize: true,
height: 100,
layer: '',
name: 'Walls',
persistentUuid: '1075df65-af84-472d-a431-47d6f7a5cb63',
width: 950,
x: -100,
y: -75,
zOrder: 1,
numberProperties: [
{
name: 'depth', // This indicates there is a custom depth.
value: 300,
},
{
name: 'z',
value: 12,
},
{
name: 'rotationX',
value: 1,
},
{
name: 'rotationY',
value: 3,
},
],
stringProperties: [],
initialVariables: [],
});
const migratedInstance = layout
.getInitialInstances()
.insertNewInitialInstance();
migratedInstance.unserializeFrom(element);
element.delete();
expect(migratedInstance.getX()).toBe(-100);
expect(migratedInstance.getY()).toBe(-75);
expect(migratedInstance.getZ()).toBe(12);
expect(migratedInstance.getAngle()).toBe(2);
expect(migratedInstance.getRotationX()).toBe(1);
expect(migratedInstance.getRotationY()).toBe(3);
expect(migratedInstance.hasCustomSize()).toBe(true);
expect(migratedInstance.hasCustomDepth()).toBe(true);
expect(migratedInstance.getCustomWidth()).toBe(950);
expect(migratedInstance.getCustomHeight()).toBe(100);
expect(migratedInstance.getCustomDepth()).toBe(300);
});
it('can have depth without a custom size', function () {
const element = gd.Serializer.fromJSObject({
angle: 2,
customSize: false,
height: 100,
layer: '',
name: 'Walls',
persistentUuid: '1075df65-af84-472d-a431-47d6f7a5cb63',
width: 950,
x: -100,
y: -75,
zOrder: 1,
z: 12,
depth: 300, // This indicates there is a custom depth.
rotationX: 1,
rotationY: 3,
numberProperties: [],
stringProperties: [],
initialVariables: [],
});
const instanceWithJustDepth = layout
.getInitialInstances()
.insertNewInitialInstance();
instanceWithJustDepth.unserializeFrom(element);
element.delete();
expect(instanceWithJustDepth.getX()).toBe(-100);
expect(instanceWithJustDepth.getY()).toBe(-75);
expect(instanceWithJustDepth.getZ()).toBe(12);
expect(instanceWithJustDepth.getAngle()).toBe(2);
expect(instanceWithJustDepth.getRotationX()).toBe(1);
expect(instanceWithJustDepth.getRotationY()).toBe(3);
expect(instanceWithJustDepth.hasCustomSize()).toBe(false);
expect(instanceWithJustDepth.hasCustomDepth()).toBe(true);
expect(instanceWithJustDepth.getCustomDepth()).toBe(300);
expect(instanceWithJustDepth.getCustomWidth()).toBe(950);
expect(instanceWithJustDepth.getCustomHeight()).toBe(100);
});
it('can have 3D properties', function () {
const initialInstanceIn3D = layout
.getInitialInstances()
.insertNewInitialInstance();
initialInstanceIn3D.setX(40);
initialInstanceIn3D.setY(41);
initialInstanceIn3D.setZ(42);
initialInstanceIn3D.setAngle(43);
initialInstanceIn3D.setRotationX(44);
initialInstanceIn3D.setRotationY(45);
initialInstanceIn3D.setHasCustomSize(true);
initialInstanceIn3D.setHasCustomDepth(true);
initialInstanceIn3D.setCustomWidth(46);
initialInstanceIn3D.setCustomHeight(47);
initialInstanceIn3D.setCustomDepth(48);
expect(initialInstanceIn3D.getX()).toBe(40);
expect(initialInstanceIn3D.getY()).toBe(41);
expect(initialInstanceIn3D.getZ()).toBe(42);
expect(initialInstanceIn3D.getAngle()).toBe(43);
expect(initialInstanceIn3D.getRotationX()).toBe(44);
expect(initialInstanceIn3D.getRotationY()).toBe(45);
expect(initialInstanceIn3D.hasCustomSize()).toBe(true);
expect(initialInstanceIn3D.hasCustomDepth()).toBe(true);
expect(initialInstanceIn3D.getCustomWidth()).toBe(46);
expect(initialInstanceIn3D.getCustomHeight()).toBe(47);
expect(initialInstanceIn3D.getCustomDepth()).toBe(48);
const element = new gd.SerializerElement();
initialInstanceIn3D.serializeTo(element);
const initialInstance2 = layout
.getInitialInstances()
.insertNewInitialInstance();
initialInstance2.unserializeFrom(element);
expect(initialInstance2.getX()).toBe(40);
expect(initialInstance2.getY()).toBe(41);
expect(initialInstance2.getZ()).toBe(42);
expect(initialInstance2.getAngle()).toBe(43);
expect(initialInstance2.getRotationX()).toBe(44);
expect(initialInstance2.getRotationY()).toBe(45);
expect(initialInstance2.hasCustomSize()).toBe(true);
expect(initialInstance2.hasCustomDepth()).toBe(true);
expect(initialInstance2.getCustomWidth()).toBe(46);
expect(initialInstance2.getCustomHeight()).toBe(47);
expect(initialInstance2.getCustomDepth()).toBe(48);
});
it('can be serialized with a persistent UUID called persistentUuid', function () {
const initialInstance = new gd.InitialInstance();
initialInstance.setObjectName('MyObject');
// Serialized object must have a non empty "persistentUuid".
let element = new gd.SerializerElement();
initialInstance.serializeTo(element);
const persistentUuid = element.getStringAttribute('persistentUuid');
expect(persistentUuid).toBeTruthy();
// The UUID must persist across serializations.
const initialInstance2 = new gd.InitialInstance();
initialInstance2.unserializeFrom(element);
let element2 = new gd.SerializerElement();
initialInstance.serializeTo(element2);
const persistentUuid2 = element2.getStringAttribute('persistentUuid');
expect(persistentUuid2).toBe(persistentUuid);
// The UUID can be reset
initialInstance2.resetPersistentUuid();
let element3 = new gd.SerializerElement();
initialInstance2.serializeTo(element3);
const persistentUuid3 = element3.getStringAttribute('persistentUuid');
expect(persistentUuid3).not.toBe(persistentUuid2);
element.delete();
element2.delete();
element3.delete();
initialInstance.delete();
initialInstance2.delete();
});
afterAll(function () {
project.delete();
});
});
describe('gd.VariablesContainer', function () {
it('container is empty after being created', function () {
let container = new gd.VariablesContainer();
expect(container.has('Variable')).toBe(false);
expect(container.count()).toBe(0);
container.delete();
});
it('can insert variables', function () {
let container = new gd.VariablesContainer();
container.insertNew('Variable', 0);
expect(container.has('Variable')).toBe(true);
expect(container.count()).toBe(1);
container.insertNew('SecondVariable', 0);
expect(container.has('SecondVariable')).toBe(true);
expect(container.count()).toBe(2);
container.delete();
});
it('can rename variables', function () {
let container = new gd.VariablesContainer();
container.insertNew('Variable', 0);
container
.insertNew('SecondVariable', 0)
.setString('String of SecondVariable');
container.insertNew('ThirdVariable', 0);
expect(container.has('SecondVariable')).toBe(true);
expect(container.has('NewName')).toBe(false);
container.rename('SecondVariable', 'NewName');
expect(container.has('SecondVariable')).toBe(false);
expect(container.has('NewName')).toBe(true);
expect(container.get('NewName').getString()).toBe(
'String of SecondVariable'
);
container.delete();
});
it('can swap variables', function () {
let container = new gd.VariablesContainer();
container.insertNew('Variable', 0).setValue(4);
container
.insertNew('SecondVariable', 1)
.setString('String of SecondVariable');
container.insertNew('ThirdVariable', 2).getChild('Child1').setValue(7);
expect(container.getNameAt(0)).toBe('Variable');
expect(container.getNameAt(2)).toBe('ThirdVariable');
container.swap(0, 2);
expect(container.getNameAt(0)).toBe('ThirdVariable');
expect(container.getNameAt(2)).toBe('Variable');
expect(container.getAt(2).getValue()).toBe(4);
container.delete();
});
it('can move variables', function () {
let container = new gd.VariablesContainer();
container.insertNew('Variable', 0).setValue(4);
container
.insertNew('SecondVariable', 1)
.setString('String of SecondVariable');
container.insertNew('ThirdVariable', 2).getChild('Child1').setValue(7);
container.move(1, 2);
expect(container.getNameAt(0)).toBe('Variable');
expect(container.getNameAt(1)).toBe('ThirdVariable');
expect(container.getNameAt(2)).toBe('SecondVariable');
container.move(1, 9999);
expect(container.getNameAt(0)).toBe('Variable');
expect(container.getNameAt(1)).toBe('ThirdVariable');
expect(container.getNameAt(2)).toBe('SecondVariable');
container.move(2, 0);
expect(container.getNameAt(0)).toBe('SecondVariable');
expect(container.getNameAt(1)).toBe('Variable');
expect(container.getNameAt(2)).toBe('ThirdVariable');
container.delete();
});
});
describe('gd.Variable', function () {
let variable = null;
beforeEach(() => (variable = new gd.Variable()));
it('should have initial value', function () {
expect(variable.getValue()).toBe(0);
expect(variable.getType()).toBe(gd.Variable.Number);
});
it('can have a value', function () {
variable.setValue(5);
expect(variable.getValue()).toBe(5);
expect(variable.getType()).toBe(gd.Variable.Number);
});
it('can have a string value', function () {
variable.setString('Hello');
expect(variable.getString()).toBe('Hello');
expect(variable.getType()).toBe(gd.Variable.String);
});
it('can have a boolean value', function () {
variable.setBool(true);
expect(variable.getBool()).toBe(true);
expect(variable.getType()).toBe(gd.Variable.Boolean);
});
it('can be a structure', function () {
variable.getChild('FirstChild').setValue(1);
variable.getChild('SecondChild').setString('two');
expect(variable.hasChild('FirstChild')).toBe(true);
expect(variable.hasChild('SecondChild')).toBe(true);
expect(variable.hasChild('NotExisting')).toBe(false);
expect(variable.getChild('SecondChild').getString()).toBe('two');
expect(variable.getType()).toBe(gd.Variable.Structure);
variable.removeChild('FirstChild');
expect(variable.hasChild('FirstChild')).toBe(false);
expect(variable.getType()).toBe(gd.Variable.Structure);
variable.removeChild('SecondChild');
expect(variable.getType()).toBe(gd.Variable.Structure);
});
it('can insert a child in structure', function () {
variable.getChild('FirstChild').setValue(1);
variable.getChild('SecondChild').setString('two');
expect(variable.getType()).toBe(gd.Variable.Structure);
const variableToInsert = new gd.Variable();
variableToInsert.setString('strVariable');
{
const result = variable.insertChild('FirstChild', variableToInsert);
expect(result).toBe(false);
}
{
const result = variable.insertChild('ThirdChild', variableToInsert);
expect(result).toBe(true);
}
variableToInsert.delete();
expect(variable.hasChild('ThirdChild')).toBe(true);
expect(variable.getChild('ThirdChild').getString()).toBe('strVariable');
});
it('can expose its children', function () {
variable.getChild('FirstChild').setValue(1);
variable.getChild('SecondChild').setValue(1);
let childrenNames = variable.getAllChildrenNames();
expect(childrenNames.size()).toBe(2);
variable.getChild(childrenNames.get(0)).setString('one');
variable.getChild(childrenNames.get(1)).setValue(2);
expect(variable.getChild('FirstChild').getString()).toBe('one');
expect(variable.getChild('SecondChild').getValue()).toBe(2);
expect(childrenNames.size()).toBe(2);
});
it('can be an array', function () {
variable.getAtIndex(0).setValue(1);
expect(variable.getType()).toBe(gd.Variable.Array);
variable.getAtIndex(2).setString('three');
expect(variable.getAllChildrenArray().size()).toBe(3);
expect(variable.getAtIndex(0).getValue()).toBe(1);
expect(variable.getAtIndex(1).getValue()).toBe(0);
expect(variable.getAtIndex(2).getType()).toBe(gd.Variable.String);
variable.removeAtIndex(2);
expect(variable.getType()).toBe(gd.Variable.Array);
variable.removeAtIndex(1);
variable.removeAtIndex(0);
expect(variable.getType()).toBe(gd.Variable.Array);
});
it('can move children inside array', function () {
variable.getAtIndex(0).setValue(1);
expect(variable.getType()).toBe(gd.Variable.Array);
variable.getAtIndex(1).setValue(2);
variable.getAtIndex(2).setValue(3);
variable.getAtIndex(3).setValue(4);
variable.moveChildInArray(2, 0);
expect(variable.getAtIndex(2).getValue()).toBe(2);
expect(variable.getAtIndex(0).getValue()).toBe(3);
});
it('can insert child in array', function () {
variable.getAtIndex(0).setValue(1);
expect(variable.getType()).toBe(gd.Variable.Array);
variable.getAtIndex(1).setValue(2);
variable.getAtIndex(2).setValue(3);
variable.getAtIndex(3).setValue(4);
const variableToInsert = new gd.Variable();
variableToInsert.setString('strVariable');
const result = variable.insertAtIndex(variableToInsert, 2);
variableToInsert.delete();
expect(result).toBe(true);
expect(variable.getAtIndex(2).getString()).toBe('strVariable');
expect(variable.getAtIndex(3).getValue()).toBe(3);
expect(variable.getAtIndex(4).getValue()).toBe(4);
});
it('can search inside children and remove them recursively', function () {
let parentVariable = new gd.Variable();
let child1 = parentVariable.getChild('Child1');
let child2 = parentVariable.getChild('Child2');
let grandChild1 = parentVariable
.getChild('Child1')
.getChild('GrandChild');
let grandChild2 = parentVariable.getChild('Child2').getAtIndex(0);
expect(parentVariable.contains(grandChild1, true)).toBe(true);
expect(parentVariable.contains(grandChild1, false)).toBe(false);
expect(parentVariable.contains(grandChild2, true)).toBe(true);
expect(parentVariable.contains(grandChild2, false)).toBe(false);
expect(parentVariable.contains(child1, true)).toBe(true);
expect(parentVariable.contains(child2, true)).toBe(true);
expect(parentVariable.contains(child1, false)).toBe(true);
expect(parentVariable.contains(child2, false)).toBe(true);
expect(child1.contains(grandChild1, true)).toBe(true);
expect(child1.contains(grandChild1, false)).toBe(true);
expect(child2.contains(grandChild1, true)).toBe(false);
expect(child2.contains(grandChild1, false)).toBe(false);
expect(child1.contains(grandChild2, true)).toBe(false);
expect(child1.contains(grandChild2, false)).toBe(false);
expect(child2.contains(grandChild2, true)).toBe(true);
expect(child2.contains(grandChild2, false)).toBe(true);
expect(grandChild1.contains(grandChild1, true)).toBe(false);
expect(grandChild1.contains(grandChild1, false)).toBe(false);
expect(grandChild1.contains(child1, true)).toBe(false);
expect(grandChild1.contains(child2, true)).toBe(false);
expect(grandChild1.contains(parentVariable, true)).toBe(false);
expect(grandChild1.contains(child1, false)).toBe(false);
expect(grandChild1.contains(child2, false)).toBe(false);
expect(grandChild1.contains(parentVariable, false)).toBe(false);
expect(grandChild2.contains(grandChild1, true)).toBe(false);
expect(grandChild2.contains(grandChild1, false)).toBe(false);
expect(grandChild2.contains(child1, true)).toBe(false);
expect(grandChild2.contains(child2, true)).toBe(false);
expect(grandChild2.contains(parentVariable, true)).toBe(false);
expect(grandChild2.contains(child1, false)).toBe(false);
expect(grandChild2.contains(child2, false)).toBe(false);
expect(grandChild2.contains(parentVariable, false)).toBe(false);
parentVariable.removeRecursively(grandChild1);
expect(child1.hasChild('GrandChild')).toBe(false);
expect(child1.getChildrenCount()).toBe(0);
expect(parentVariable.getChildrenCount()).toBe(2);
parentVariable.removeRecursively(grandChild2);
expect(child2.getAllChildrenArray().size()).toBe(0);
expect(child2.getChildrenCount()).toBe(0);
expect(parentVariable.getChildrenCount()).toBe(2);
parentVariable.removeRecursively(child2);
expect(parentVariable.getChildrenCount()).toBe(1);
expect(parentVariable.hasChild('Child1')).toBe(true);
expect(parentVariable.hasChild('Child2')).toBe(false);
parentVariable.delete();
});
afterAll(function () {
variable.delete();
});
});
describe('gd.ImageResource', function () {
it('should have name and file', function () {
const resource = new gd.ImageResource();
resource.setName('MyResource');
resource.setFile('MyFile');
expect(resource.getName()).toBe('MyResource');
expect(resource.getFile()).toBe('MyFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.ImageResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
it('has smooth and alreadyLoaded custom properties', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const resource = new gd.ImageResource();
const properties = resource.getProperties();
expect(properties.get('Smooth the image').getValue()).toBe('true');
expect(properties.get('Always loaded in memory').getValue()).toBe(
'false'
);
// Note: updateProperty expect the booleans in an usual "0" or "1" format.
resource.updateProperty('Smooth the image', '0');
resource.updateProperty('Always loaded in memory', '1');
const updatedProperties = resource.getProperties();
expect(updatedProperties.get('Smooth the image').getValue()).toBe(
'false'
);
expect(updatedProperties.get('Always loaded in memory').getValue()).toBe(
'true'
);
resource.delete();
project.delete();
});
});
describe('gd.AudioResource', function () {
it('should have name and file', function () {
const resource = new gd.AudioResource();
resource.setName('MyAudioResource');
resource.setFile('MyAudioFile');
expect(resource.getName()).toBe('MyAudioResource');
expect(resource.getFile()).toBe('MyAudioFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.AudioResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
});
describe('gd.FontResource', function () {
it('should have name and file', function () {
const resource = new gd.FontResource();
resource.setName('MyFontResource');
resource.setFile('MyFontFile');
expect(resource.getName()).toBe('MyFontResource');
expect(resource.getFile()).toBe('MyFontFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.FontResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
});
describe('gd.BitmapFontResource', function () {
it('should have name and file', function () {
const resource = new gd.BitmapFontResource();
resource.setName('MyBitmapFontResource');
resource.setFile('MyBitmapFontFile');
expect(resource.getName()).toBe('MyBitmapFontResource');
expect(resource.getFile()).toBe('MyBitmapFontFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.BitmapFontResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
});
describe('gd.VideoResource', function () {
it('should have name and file', function () {
const resource = new gd.VideoResource();
resource.setName('MyVideoResource');
resource.setFile('MyVideoFile');
expect(resource.getName()).toBe('MyVideoResource');
expect(resource.getFile()).toBe('MyVideoFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.VideoResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
});
describe('gd.JsonResource', function () {
it('should have name and file', function () {
const resource = new gd.JsonResource();
resource.setName('MyJsonResource');
resource.setFile('MyJsonFile');
expect(resource.getName()).toBe('MyJsonResource');
expect(resource.getFile()).toBe('MyJsonFile');
resource.delete();
});
it('can have metadata', function () {
const resource = new gd.JsonResource();
expect(resource.getMetadata()).toBe('');
resource.setMetadata(JSON.stringify({ hello: 'world' }));
expect(resource.getMetadata()).toBe('{"hello":"world"}');
resource.delete();
});
it('has disablePreload custom properties', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const resource = new gd.JsonResource();
const properties = resource.getProperties();
expect(properties.get('disablePreload').getValue()).toBe('false');
// Note: updateProperty expect the booleans in an usual "0" or "1" format.
resource.updateProperty('disablePreload', '1');
const updatedProperties = resource.getProperties();
expect(updatedProperties.get('disablePreload').getValue()).toBe('true');
resource.delete();
project.delete();
});
});
describe('gd.ResourcesManager', function () {
it('should support adding resources', function () {
let project = gd.ProjectHelper.createNewGDJSProject();
let resource = new gd.Resource();
let resource2 = new gd.Resource();
resource.setName('MyResource');
resource2.setName('MyResource2');
project.getResourcesManager().addResource(resource);
project.getResourcesManager().addResource(resource2);
let allResources = project.getResourcesManager().getAllResourceNames();
expect(allResources.size()).toBe(2);
project.delete();
});
it('should support finding resources', function () {
let project = gd.ProjectHelper.createNewGDJSProject();
let resource = new gd.Resource();
let resource2 = new gd.Resource();
resource.setName('MyResource');
resource2.setName('MyResource2');
project.getResourcesManager().addResource(resource);
project.getResourcesManager().addResource(resource2);
expect(
project.getResourcesManager().getResourcePosition('MyResource')
).toBe(0);
expect(
project.getResourcesManager().getResourcePosition('MyResource2')
).toBe(1);
expect(
project.getResourcesManager().getResourcePosition('MyResource3')
).toBe(-1);
project.delete();
});
it('can find files that are not in the resources', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const resource = new gd.ImageResource();
const resource2 = new gd.AudioResource();
const resource3 = new gd.AudioResource();
const resource4 = new gd.JsonResource();
resource.setName('MyResource');
resource.setFile('MyFile.png');
resource2.setName('MyResource2');
resource2.setFile('MySubFolder/MyOtherFile.mp3');
resource3.setName('MyResource3');
resource3.setFile('MySubFolder\\MyOtherFileInWindowsFormat.mp3');
resource4.setName('MyResource4');
resource4.setFile('MySubFolder/MyOtherFileInUnixFormat.json');
project.getResourcesManager().addResource(resource);
project.getResourcesManager().addResource(resource2);
project.getResourcesManager().addResource(resource3);
project.getResourcesManager().addResource(resource4);
const filesToCheck = new gd.VectorString();
[
'MyFile.png',
'MySubFolder/MyFileMissingInResource.png',
'MySubFolder/MyOtherFile.mp3',
// Check that resources can be found even if specified using
// a different path separator:
'MySubFolder/MyOtherFileInWindowsFormat.mp3',
'MySubFolder\\MyOtherFileInUnixFormat.json',
].forEach((filePath) => {
filesToCheck.push_back(filePath);
});
const filesNotInResources = project
.getResourcesManager()
.findFilesNotInResources(filesToCheck);
filesToCheck.delete();
expect(filesNotInResources.size()).toBe(1);
expect(filesNotInResources.at(0)).toBe(
'MySubFolder/MyFileMissingInResource.png'
);
project.delete();
});
it('should support removing resources', function () {
let project = gd.ProjectHelper.createNewGDJSProject();
let resource = new gd.Resource();
resource.setName('MyResource');
project.getResourcesManager().addResource(resource);
project.getResourcesManager().removeResource('MyResource');
let allResources = project.getResourcesManager().getAllResourceNames();
expect(allResources.size()).toBe(0);
project.delete();
});
});
describe('gd.ProjectResourcesAdder', function () {
it('should support removing useless resources', function () {
let project = gd.ProjectHelper.createNewGDJSProject();
let resource1 = new gd.ImageResource();
resource1.setName('Useless');
let resource2 = new gd.ImageResource();
resource2.setName('Used');
project.getResourcesManager().addResource(resource1);
project.getResourcesManager().addResource(resource2);
//Create an object using a resource
let obj = project.insertNewObject(project, 'Sprite', 'MyObject', 0);
let sprite1 = new gd.Sprite();
sprite1.setImageName('Used');
let anim1 = new gd.Animation();
anim1.setDirectionsCount(1);
anim1.getDirection(0).addSprite(sprite1);
gd.castObject(obj.getConfiguration(), gd.SpriteObject).getAnimations().addAnimation(
anim1
);
{
let allResources = project.getResourcesManager().getAllResourceNames();
expect(allResources.size()).toBe(2);
}
gd.ProjectResourcesAdder.removeAllUseless(project, 'image');
{
let allResources = project.getResourcesManager().getAllResourceNames();
expect(allResources.size()).toBe(1);
expect(allResources.get(0)).toBe('Used');
}
project.delete();
});
it('should not remove loading screen image when removing useless resources', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const resource = new gd.ImageResource();
resource.setName('LoadingScreenImage');
project
.getLoadingScreen()
.setBackgroundImageResourceName(resource.getName());
expect(project.getLoadingScreen().getBackgroundImageResourceName()).toBe(
'LoadingScreenImage'
);
const worker = new gd.ResourcesInUseHelper();
gd.ResourceExposer.exposeWholeProjectResources(project, worker);
expect(worker.getAllImages().toNewVectorString().toJSArray().length).toBe(
1
);
expect(worker.getAllImages().toNewVectorString().toJSArray()[0]).toBe(
'LoadingScreenImage'
);
gd.ProjectResourcesAdder.removeAllUseless(project, 'image');
const newWorker = new gd.ResourcesInUseHelper();
gd.ResourceExposer.exposeWholeProjectResources(project, newWorker);
expect(
newWorker.getAllImages().toNewVectorString().toJSArray().length
).toBe(1);
expect(newWorker.getAllImages().toNewVectorString().toJSArray()[0]).toBe(
'LoadingScreenImage'
);
project.delete();
});
});
describe('gd.ArbitraryResourceWorker', function () {
it('should be called with resources of the project', function (done) {
let project = gd.ProjectHelper.createNewGDJSProject();
let obj = project.insertNewObject(project, 'Sprite', 'MyObject', 0);
const spriteConfiguration = gd.asSpriteConfiguration(
obj.getConfiguration()
);
let sprite1 = new gd.Sprite();
sprite1.setImageName('Used');
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
spriteConfiguration.getAnimations().addAnimation(animation);
let worker = extend(new gd.ArbitraryResourceWorkerJS(project.getResourcesManager()), {
exposeImage: function (image) {
expect(image).toBe('Used');
done();
return image;
},
});
gd.ResourceExposer.exposeWholeProjectResources(project, worker);
project.delete();
});
});
describe('gd.ResourcesInUseHelper', function () {
it('should find the images used by objects', function () {
let sprite1 = new gd.Sprite();
sprite1.setImageName('Image1');
let sprite2 = new gd.Sprite();
sprite2.setImageName('Image2');
let sprite3 = new gd.Sprite();
sprite3.setImageName('Image3');
const spriteObject = new gd.SpriteObject();
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
animation.getDirection(0).addSprite(sprite2);
animation.getDirection(0).addSprite(sprite1);
spriteObject.getAnimations().addAnimation(animation);
const spriteObject2 = new gd.SpriteObject();
const animation2 = new gd.Animation();
animation2.setDirectionsCount(1);
animation2.getDirection(0).addSprite(sprite1);
animation2.getDirection(0).addSprite(sprite3);
animation2.getDirection(0).addSprite(sprite1);
spriteObject2.getAnimations().addAnimation(animation2);
const resourcesInUse = new gd.ResourcesInUseHelper();
{
spriteObject.exposeResources(resourcesInUse);
const resourceNames = resourcesInUse
.getAllImages()
.toNewVectorString()
.toJSArray();
expect(resourceNames).toHaveLength(2);
expect(resourceNames).toContain('Image1');
expect(resourceNames).toContain('Image2');
}
{
spriteObject2.exposeResources(resourcesInUse);
const resourceNames = resourcesInUse
.getAllImages()
.toNewVectorString()
.toJSArray();
expect(resourceNames).toHaveLength(3);
expect(resourceNames).toContain('Image1');
expect(resourceNames).toContain('Image2');
expect(resourceNames).toContain('Image3');
}
resourcesInUse.delete();
spriteObject.delete();
spriteObject2.delete();
});
});
describe('gd.ObjectsUsingResourceCollector', function () {
it('lists objects that use the given resources', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
const sprite1 = new gd.Sprite();
sprite1.setImageName('Image1');
const sprite2 = new gd.Sprite();
sprite2.setImageName('Image2');
const sprite3 = new gd.Sprite();
sprite3.setImageName('Image3');
const spriteObject = gd.asSpriteConfiguration(object.getConfiguration());
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
animation.getDirection(0).addSprite(sprite2);
animation.getDirection(0).addSprite(sprite1);
spriteObject.getAnimations().addAnimation(animation);
const object2 = project.insertNewObject(
project,
'Sprite',
'MyObject2',
0
);
const spriteObject2 = gd.asSpriteConfiguration(
object2.getConfiguration()
);
const animation2 = new gd.Animation();
animation2.setDirectionsCount(1);
animation2.getDirection(0).addSprite(sprite1);
animation2.getDirection(0).addSprite(sprite3);
animation2.getDirection(0).addSprite(sprite1);
spriteObject2.getAnimations().addAnimation(animation2);
{
const objectsCollector = new gd.ObjectsUsingResourceCollector(project.getResourcesManager(), 'Image1');
gd.ProjectBrowserHelper.exposeProjectObjects(project, objectsCollector);
const objectNames = objectsCollector.getObjectNames().toJSArray();
objectsCollector.delete();
expect(objectNames.length).toEqual(2);
expect(objectNames).toContain('MyObject');
expect(objectNames).toContain('MyObject2');
}
{
const objectsCollector = new gd.ObjectsUsingResourceCollector(project.getResourcesManager(), 'Image2');
gd.ProjectBrowserHelper.exposeProjectObjects(project, objectsCollector);
const objectNames = objectsCollector.getObjectNames().toJSArray();
objectsCollector.delete();
expect(objectNames.length).toEqual(1);
expect(objectNames).toContain('MyObject');
}
{
const objectsCollector = new gd.ObjectsUsingResourceCollector(project.getResourcesManager(), 'Image3');
gd.ProjectBrowserHelper.exposeProjectObjects(project, objectsCollector);
const objectNames = objectsCollector.getObjectNames().toJSArray();
objectsCollector.delete();
expect(objectNames.length).toEqual(1);
expect(objectNames).toContain('MyObject2');
}
project.delete();
});
});
describe('gd.Behavior', function () {
it('update a not existing property', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const behavior = new gd.Behavior();
const serializerElement = new gd.SerializerElement();
expect(
behavior.updateProperty(
serializerElement,
'PropertyThatDoesNotExist',
'MyValue'
)
).toBe(false);
serializerElement.delete();
behavior.delete();
project.delete();
});
});
describe('gd.BehaviorsSharedData', function () {
it('can be created by gd.Layout.updateBehaviorsSharedData', function () {
let project = gd.ProjectHelper.createNewGDJSProject();
let layout = project.insertNewLayout('Scene', 0);
let object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
layout.updateBehaviorsSharedData(project);
expect(layout.hasBehaviorSharedData('Physics')).toBe(false);
let behaviorContent = object.addNewBehavior(
project,
'PhysicsBehavior::PhysicsBehavior',
'Physics'
);
expect(layout.hasBehaviorSharedData('Physics')).toBe(false);
layout.updateBehaviorsSharedData(project);
expect(layout.hasBehaviorSharedData('Physics')).toBe(true);
layout.removeObject('MyObject');
expect(layout.hasBehaviorSharedData('Physics')).toBe(true);
layout.updateBehaviorsSharedData(project);
expect(layout.hasBehaviorSharedData('Physics')).toBe(false);
project.delete();
});
});
describe('gd.BehaviorSharedDataJsImplementation', function () {
it('can declare a gd.BehaviorSharedDataJsImplementation and pass sanity checks', function () {
let mySharedData = new gd.BehaviorSharedDataJsImplementation();
mySharedData.updateProperty = function (
behaviorContent,
propertyName,
newValue
) {
if (propertyName === 'My first property') {
behaviorContent.setStringAttribute('property1', newValue);
return true;
}
if (propertyName === 'My other property') {
behaviorContent.setBoolAttribute('property2', newValue === '1');
return true;
}
return false;
};
mySharedData.getProperties = function (behaviorContent) {
let properties = new gd.MapStringPropertyDescriptor();
properties
.getOrCreate('My first property')
.setValue(behaviorContent.getStringAttribute('property1'));
properties
.getOrCreate('My other property')
.setValue(behaviorContent.getBoolAttribute('property2') ? '1' : '0')
.setType('Boolean');
return properties;
};
mySharedData.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
behaviorContent.setBoolAttribute('property2', true);
};
try {
expect(
gd.ProjectHelper.sanityCheckBehaviorsSharedDataProperty(
mySharedData,
'My first property',
'Value1'
)
).toBe('');
expect(
gd.ProjectHelper.sanityCheckBehaviorsSharedDataProperty(
mySharedData,
'My other property',
'0'
)
).toBe('');
} catch (ex) {
console.error(ex);
throw Error(
'Exception caught while launching sanityCheckBehavior on a gd.BehaviorSharedDataJsImplementation.'
);
}
});
});
describe('gd.NamedPropertyDescriptor', function () {
const makeNewProperty = () => {
const property = new gd.NamedPropertyDescriptor();
property
.setName('Property1')
.setLabel('The first property')
.setValue('Hello world')
.setType('string')
.addExtraInfo('Info1')
.addExtraInfo('Info2');
return property;
};
it('can be created and manipulated', function () {
const property = makeNewProperty();
expect(property.getName()).toBe('Property1');
expect(property.getLabel()).toBe('The first property');
expect(property.getValue()).toBe('Hello world');
expect(property.getType()).toBe('string');
expect(property.getExtraInfo().toJSArray()).toContain('Info1');
expect(property.getExtraInfo().toJSArray()).toContain('Info2');
property.delete();
});
it('can be serialized', function () {
const property = makeNewProperty();
let serializerElement = new gd.SerializerElement();
property.serializeTo(serializerElement);
property.delete();
const property2 = new gd.NamedPropertyDescriptor();
property2.unserializeFrom(serializerElement);
serializerElement.delete();
expect(property2.getName()).toBe('Property1');
expect(property2.getLabel()).toBe('The first property');
expect(property2.getValue()).toBe('Hello world');
expect(property2.getType()).toBe('string');
expect(property2.getExtraInfo().toJSArray()).toContain('Info1');
expect(property2.getExtraInfo().toJSArray()).toContain('Info2');
});
});
describe('gd.PropertiesContainer', function () {
it('can be used to store named properties', function () {
const list = new gd.PropertiesContainer(0);
const property1 = list.insertNew('Property1', 0);
expect(list.has('Property1')).toBe(true);
expect(list.getCount()).toBe(1);
expect(property1.getName()).toBe('Property1');
expect(list.getAt(0).getName()).toBe('Property1');
property1.setLabel('Property 1');
property1.setValue('123');
expect(list.getAt(0).getLabel()).toBe('Property 1');
expect(list.getAt(0).getValue()).toBe('123');
list.delete();
});
});
describe('gd.MapStringPropertyDescriptor', function () {
it('can be used to manipulate properties', function () {
let properties = new gd.MapStringPropertyDescriptor();
expect(properties.has('Property0')).toBe(false);
// Ensure the "set" method works (though in practice, prefer "getOrCreate")
const property1 = new gd.PropertyDescriptor('Hello Property1');
properties.set('Property1', property1);
property1.delete();
expect(properties.get('Property1').getValue()).toBe('Hello Property1');
expect(properties.get('Property1').getType()).toBe('string');
properties.get('Property1').setValue('Hello modified Property1');
expect(properties.get('Property1').getValue()).toBe(
'Hello modified Property1'
);
expect(properties.keys().toJSArray()).not.toContain('Property0');
expect(properties.keys().toJSArray()).toContain('Property1');
// Ensure the "getOrCreate" method works
expect(properties.has('Property0')).toBe(false);
properties
.getOrCreate('Property0')
.setValue('Hello Property0')
.setType('another type')
.addExtraInfo('Info1')
.addExtraInfo('Info3');
expect(properties.has('Property0')).toBe(true);
expect(properties.get('Property0').getValue()).toBe('Hello Property0');
expect(properties.get('Property0').getType()).toBe('another type');
expect(properties.get('Property0').getExtraInfo().toJSArray()).toContain(
'Info1'
);
expect(
properties.get('Property0').getExtraInfo().toJSArray()
).not.toContain('Info2');
expect(properties.get('Property0').getExtraInfo().toJSArray()).toContain(
'Info3'
);
expect(properties.has('Property0')).toBe(true);
expect(properties.has('Property1')).toBe(true);
expect(properties.keys().toJSArray()).toContain('Property0');
expect(properties.keys().toJSArray()).toContain('Property1');
properties.delete();
});
});
describe('gd.BehaviorJsImplementation', function () {
it('can declare a gd.BehaviorJsImplementation and pass sanity checks', function () {
let myBehavior = new gd.BehaviorJsImplementation();
myBehavior.updateProperty = function (
behaviorContent,
propertyName,
newValue
) {
if (propertyName === 'My first property') {
behaviorContent.setStringAttribute('property1', newValue);
return true;
}
if (propertyName === 'My other property') {
behaviorContent.setBoolAttribute('property2', newValue === '1');
return true;
}
return false;
};
myBehavior.getProperties = function (behaviorContent) {
let properties = new gd.MapStringPropertyDescriptor();
properties
.getOrCreate('My first property')
.setValue(behaviorContent.getStringAttribute('property1'));
properties
.getOrCreate('My other property')
.setValue(behaviorContent.getBoolAttribute('property2') ? '1' : '0')
.setType('Boolean');
return properties;
};
myBehavior.initializeContent = function (behaviorContent) {
behaviorContent.setStringAttribute('property1', 'Initial value 1');
behaviorContent.setBoolAttribute('property2', true);
};
try {
expect(
gd.ProjectHelper.sanityCheckBehaviorProperty(
myBehavior,
'My first property',
'Value1'
)
).toBe('');
expect(
gd.ProjectHelper.sanityCheckBehaviorProperty(
myBehavior,
'My other property',
'0'
)
).toBe('');
} catch (ex) {
console.error(ex);
throw Error(
'Exception caught while launching sanityCheckBehavior on a gd.BehaviorJsImplementation.'
);
}
});
});
describe('gd.Object', function () {
let project = null;
let layout = null;
let object = null;
let object2 = null;
beforeAll(() => {
project = gd.ProjectHelper.createNewGDJSProject();
layout = project.insertNewLayout('Scene', 0);
object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
object2 = layout.insertNewObject(project, 'Sprite', 'MyObject2', 1);
});
it('has properties and initial values', function () {
object.setName('TheObject');
expect(object.getName()).toBe('TheObject');
expect(object.hasBehaviorNamed('DoNotExists')).toBe(false);
});
it('can have its type retrieved with gd.getTypeOfObject', function () {
expect(gd.getTypeOfObject(project, layout, 'TheObject', true)).toBe(
'Sprite'
);
});
it('can have behaviors', function () {
let behavior = object.addNewBehavior(
project,
'DraggableBehavior::Draggable',
'Draggable'
);
expect(object.hasBehaviorNamed('Draggable')).toBe(true);
expect(object.getBehavior('Draggable')).toBe(behavior);
});
const spriteDefaultBehaviorCount = 6;
it('can have its behaviors retrieved with gd.getBehaviorsOfObject', function () {
let behaviors = gd.getBehaviorsOfObject(
project,
layout,
'TheObject',
true
);
expect(behaviors.size()).toBe(1 + spriteDefaultBehaviorCount);
expect(behaviors.get(1)).toBe('Draggable');
});
it('can be un/serialized (basic)', function () {
let serializerElement = new gd.SerializerElement();
object.serializeTo(serializerElement);
object2.unserializeFrom(project, serializerElement);
object2.unserializeFrom(project, serializerElement); // Also check that multiple
object2.unserializeFrom(project, serializerElement); // unserialization is idempotent
serializerElement.delete();
//Check that behaviors were persisted and restored
let behaviors = object2.getAllBehaviorNames();
expect(behaviors.size()).toBe(1 + spriteDefaultBehaviorCount);
expect(behaviors.at(1)).toBe('Draggable');
});
it('can be un/serialized (with behavior content)', function () {
const behaviorContent = object.getBehavior('Draggable');
behaviorContent.updateProperty('checkCollisionMask', 'true');
let serializerElement = new gd.SerializerElement();
object.serializeTo(serializerElement);
object2.unserializeFrom(project, serializerElement);
object2.unserializeFrom(project, serializerElement); // Also check that multiple
object2.unserializeFrom(project, serializerElement); // unserialization is idempotent
serializerElement.delete();
//Check that behaviors were persisted and restored
let behaviors = object2.getAllBehaviorNames();
expect(behaviors.size()).toBe(1 + spriteDefaultBehaviorCount);
expect(behaviors.at(1)).toBe('Draggable');
const behaviorContent2 = object2.getBehavior('Draggable');
expect(
behaviorContent2.getProperties().get('checkCollisionMask').getValue()
).toBe('true');
});
afterAll(function () {
project.delete();
});
});
describe('gd.ObjectJsImplementation', function () {
const createSampleObjectJsImplementation = () => {
let myObject = new gd.ObjectJsImplementation();
myObject.updateProperty = function (content, propertyName, newValue) {
if (propertyName === 'My first property') {
content.property1 = newValue;
return true;
}
if (propertyName === 'My other property') {
content.property2 = newValue === '1';
return true;
}
return false;
};
myObject.getProperties = function (content) {
let properties = new gd.MapStringPropertyDescriptor();
properties.getOrCreate('My first property').setValue(content.property1);
properties
.getOrCreate('My other property')
.setValue(content.property2 ? '1' : '0')
.setType('Boolean');
return properties;
};
myObject.setRawJSONContent(
JSON.stringify({
property1: 'Initial value 1',
property2: true,
})
);
myObject.updateInitialInstanceProperty = function (
content,
instance,
propertyName,
newValue,
project,
layout
) {
if (propertyName === 'My instance property') {
instance.setRawStringProperty('instanceprop1', newValue);
return true;
}
if (propertyName === 'My other instance property') {
instance.setRawDoubleProperty('instanceprop2', parseFloat(newValue));
return true;
}
return false;
};
myObject.getInitialInstanceProperties = function (
content,
instance,
project,
layout
) {
let properties = new gd.MapStringPropertyDescriptor();
properties
.getOrCreate('My instance property')
.setValue(instance.getRawStringProperty('instanceprop1'));
properties
.getOrCreate('My other instance property')
.setValue(instance.getRawDoubleProperty('instanceprop2').toString())
.setType('number');
return properties;
};
// TODO: Workaround a bad design of ObjectJsImplementation. When getProperties
// and associated methods are redefined in JS, they have different arguments (
// see ObjectJsImplementation C++ implementation). If called directly here from JS,
// the arguments will be mismatched. To workaround this, always case the object to
// a base gdObject to ensure C++ methods are called.
return gd.castObject(myObject, gd.ObjectConfiguration);
};
it('can create a gd.ObjectJsImplementation and pass sanity checks', function () {
const myObject = createSampleObjectJsImplementation();
try {
expect(
gd.ProjectHelper.sanityCheckObjectProperty(
myObject,
'My first property',
'Value1'
)
).toBe('');
expect(
gd.ProjectHelper.sanityCheckObjectProperty(
myObject,
'My other property',
'0'
)
).toBe('');
expect(
gd.ProjectHelper.sanityCheckObjectInitialInstanceProperty(
myObject,
'My instance property',
'Value1'
)
).toBe('');
expect(
gd.ProjectHelper.sanityCheckObjectInitialInstanceProperty(
myObject,
'My other instance property',
'0'
)
).toBe('');
} catch (ex) {
console.error(ex);
throw Error(
'Exception caught while launching sanityCheckObject on a gd.ObjectJsImplementation.'
);
}
});
it('can clone a gd.ObjectJsImplementation', function () {
const object1 = createSampleObjectJsImplementation();
expect(
object1.getProperties().get('My first property').getValue() ==
'Initial value 1'
);
object1.updateProperty('My first property', 'test1');
const object2 = object1.clone().release();
const object3 = object1.clone().release();
{
const propertiesObject1 = object1.getProperties();
expect(propertiesObject1.has('My first property'));
expect(
propertiesObject1.get('My first property').getValue() == 'test1'
);
const propertiesObject2 = object2.getProperties();
expect(propertiesObject2.has('My first property'));
expect(
propertiesObject2.get('My first property').getValue() == 'test1'
);
}
{
object1.updateProperty('My first property', 'updated value');
const propertiesObject1 = object1.getProperties();
expect(propertiesObject1.has('My first property'));
expect(
propertiesObject1.get('My first property').getValue() ==
'updated value'
);
const propertiesObject2 = object2.getProperties();
expect(propertiesObject2.has('My first property'));
expect(
propertiesObject2.get('My first property').getValue() == 'test1'
);
}
{
object2.updateProperty('My first property', 'updated value object 2');
const propertiesObject1 = object1.getProperties();
expect(propertiesObject1.has('My first property'));
expect(
propertiesObject1.get('My first property').getValue() ==
'updated value'
);
const propertiesObject2 = object2.getProperties();
expect(propertiesObject2.has('My first property'));
expect(
propertiesObject2.get('My first property').getValue() ==
'updated value object 2'
);
const propertiesObject3 = object3.getProperties();
expect(propertiesObject3.has('My first property'));
expect(
propertiesObject3.get('My first property').getValue() == 'test1'
);
}
});
});
describe('gd.ObjectGroupsContainer', function () {
let container = null;
let group1 = null;
let group2 = null;
let group3 = null;
beforeAll(() => (container = new gd.ObjectGroupsContainer()));
it('can have groups inserted', function () {
group1 = container.insertNew('Group1', 0);
group2 = container.insertNew('Group2', 1);
group3 = container.insertNew('Group3', 2);
expect(container.getAt(0).getName()).toBe('Group1');
expect(container.getAt(1).getName()).toBe('Group2');
expect(container.getAt(2).getName()).toBe('Group3');
expect(container.has('Group1')).toBe(true);
expect(container.has('Group2')).toBe(true);
expect(container.has('Group3')).toBe(true);
expect(container.has('Group4')).toBe(false);
expect(container.get('Group1').getName()).toBe('Group1');
expect(container.get('Group2').getName()).toBe('Group2');
expect(container.get('Group3').getName()).toBe('Group3');
expect(container.count()).toBe(3);
});
it('can move groups', function () {
container.move(0, 1);
expect(container.getAt(0).getName()).toBe('Group2');
expect(container.getAt(1).getName()).toBe('Group1');
expect(container.getAt(2).getName()).toBe('Group3');
});
it('can rename groups', function () {
container.rename('Inexisting', 'Whatever');
container.rename('Group1', 'Group1Renamed');
expect(container.has('Group1')).toBe(false);
expect(container.has('Group1Renamed')).toBe(true);
});
it('can remove groups', function () {
container.remove('Group2');
expect(container.has('Group2')).toBe(false);
expect(container.has('Group3')).toBe(true);
expect(container.count()).toBe(2);
});
});
describe('gd.Instruction', function () {
it('initial values', function () {
let instr = new gd.Instruction();
expect(instr.getParametersCount()).toBe(0);
expect(instr.getSubInstructions().size()).toBe(0);
instr.delete();
});
it('setting parameters', function () {
let instr = new gd.Instruction();
instr.setParametersCount(3);
expect(instr.getParametersCount()).toBe(3);
expect(instr.getParameter(1).getPlainString()).toBe('');
instr.setParameter(2, 'MyValue');
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
instr.delete();
});
it('can be cloned', function () {
let instr = new gd.Instruction();
instr.setParametersCount(3);
instr.setParameter(2, 'MyValue');
let newInstr = instr.clone();
expect(newInstr.getParametersCount()).toBe(3);
expect(newInstr.getParameter(1).getPlainString()).toBe('');
expect(newInstr.getParameter(2).getPlainString()).toBe('MyValue');
newInstr.setParameter(2, 'MyChangedValue');
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
expect(newInstr.getParameter(2).getPlainString()).toBe('MyChangedValue');
newInstr.delete();
expect(instr.getParameter(2).getPlainString()).toBe('MyValue');
instr.delete();
});
});
describe('gd.InstructionsList', function () {
let list = null;
beforeAll(() => (list = new gd.InstructionsList()));
it('can insert instructions', function () {
expect(list.size()).toBe(0);
list.insert(new gd.Instruction(), 0);
expect(list.size()).toBe(1);
});
it('can modify its instructions', function () {
expect(list.get(0).getType()).toBe('');
let newInstr = new gd.Instruction();
newInstr.setType('Type2');
list.set(0, newInstr);
expect(list.get(0).getType()).toBe('Type2');
expect(list.size()).toBe(1);
});
it('can remove its instructions', function () {
let newInstr = new gd.Instruction();
newInstr.setType('Type3');
let instruction = list.insert(newInstr, 1);
expect(list.get(1).getType()).toBe('Type3');
expect(list.size()).toBe(2);
expect(list.contains(instruction)).toBe(true);
list.remove(instruction);
expect(list.size()).toBe(1);
expect(list.get(0).getType()).toBe('Type2');
});
it('can clear its instructions', function () {
list.clear();
expect(list.size()).toBe(0);
});
it('can insert events from another list', function () {
let list1 = new gd.InstructionsList();
let list2 = new gd.InstructionsList();
let newInstr = new gd.Instruction();
newInstr.setType('Type1');
list1.insert(newInstr, 0);
let newInstr2 = new gd.Instruction();
newInstr2.setType('Type2');
list1.insert(newInstr2, 1);
list2.insertInstructions(list1, 0, list1.size(), 0);
expect(list2.size()).toBe(2);
expect(list2.get(0).getType()).toBe('Type1');
expect(list2.get(1).getType()).toBe('Type2');
list2.insertInstructions(list1, 0, list1.size(), 1);
expect(list2.size()).toBe(4);
expect(list2.get(0).getType()).toBe('Type1');
expect(list2.get(1).getType()).toBe('Type1');
expect(list2.get(2).getType()).toBe('Type2');
expect(list2.get(3).getType()).toBe('Type2');
list1.delete();
list2.delete();
});
it('can be un/serialized', function () {
let newInstr = new gd.Instruction();
newInstr.setType('Type1');
newInstr.setParametersCount(2);
newInstr.setParameter(0, 'Param1');
newInstr.setParameter(1, 'Param2');
let instruction = list.insert(newInstr, 1);
let newInstr2 = new gd.Instruction();
newInstr2.setType('Type2');
newInstr2.setParametersCount(1);
newInstr2.setParameter(0, 'Param3');
let instruction2 = list.insert(newInstr2, 1);
let project = gd.ProjectHelper.createNewGDJSProject();
let serializerElement = new gd.SerializerElement();
list.serializeTo(serializerElement);
let list2 = new gd.InstructionsList();
list2.unserializeFrom(project, serializerElement);
expect(list2.size()).toBe(2);
expect(list2.get(0).getType()).toBe('Type1');
expect(list2.get(1).getType()).toBe('Type2');
expect(list2.get(0).getParametersCount()).toBe(2);
expect(list2.get(1).getParametersCount()).toBe(1);
expect(list2.get(0).getParameter(0).getPlainString()).toBe('Param1');
expect(list2.get(0).getParameter(1).getPlainString()).toBe('Param2');
expect(list2.get(1).getParameter(0).getPlainString()).toBe('Param3');
list2.delete();
project.delete();
});
afterAll(function () {
list.delete();
});
});
describe('InstructionSentenceFormatter', function () {
it('should translate instructions (plain text or into a vector of text with formatting)', function () {
let action = new gd.Instruction(); //Create a simple instruction
action.setType('Delete');
action.setParametersCount(2);
action.setParameter(0, 'MyCharacter');
let formattedTexts = gd.InstructionSentenceFormatter.get().getAsFormattedText(
action,
gd.MetadataProvider.getActionMetadata(gd.JsPlatform.get(), 'Delete')
);
expect(formattedTexts.size()).toBe(2);
expect(formattedTexts.getString(0)).toBe('Delete ');
expect(formattedTexts.getString(1)).toBe('MyCharacter');
expect(formattedTexts.getTextFormatting(0).getUserData()).not.toBe(0);
expect(formattedTexts.getTextFormatting(1).getUserData()).toBe(0);
action.delete();
});
});
describe('EventsRefactorer', function () {
describe('SearchInEvents', function () {
let eventList = null;
let event1 = null;
let event2 = null;
beforeAll(() => {
eventList = new gd.EventsList();
/* Event 1 */
{
const event = new gd.StandardEvent();
let eventActions1 = event.getActions();
let action1 = new gd.Instruction();
action1.setType('RotateTowardPosition'); // should generate the sentence `Rotate _PARAM0_ towards _PARAM1_;_PARAM2_ at speed _PARAM3_deg/second`
action1.setParametersCount(4);
action1.setParameter(0, 'Platform');
action1.setParameter(1, '450');
action1.setParameter(2, '200');
action1.setParameter(3, '90');
eventActions1.push_back(action1);
let eventConditions1 = event.getConditions();
let condition1 = new gd.Instruction();
condition1.setType('PosX'); // should generate the sentence `the X position of _PARAM0_ _PARAM1_ _PARAM2_`
condition1.setParametersCount(3);
condition1.setParameter(0, 'MyCharacter');
condition1.setParameter(1, '<');
condition1.setParameter(2, '300');
eventConditions1.push_back(condition1);
event1 = eventList.insertEvent(event, 0);
action1.delete();
condition1.delete();
}
/* Event 2 */
{
const event = new gd.StandardEvent();
let eventActions2 = event.getActions();
let action2 = new gd.Instruction();
action2.setType('Delete'); // should generate the sentence `Delete _PARAM0_`
action2.setParametersCount(1);
action2.setParameter(0, 'OtherCharacter');
eventActions2.push_back(action2);
let eventConditions2 = event.getConditions();
let condition2 = new gd.Instruction();
condition2.setType('Angle'); // should generate the sentence `the angle (in degrees) of _PARAM0_ _PARAM1_ _PARAM2_`
condition2.setParametersCount(3);
condition2.setParameter(0, 'OtherPlatform');
condition2.setParameter(1, '>');
condition2.setParameter(2, '55');
eventConditions2.push_back(condition2);
event2 = eventList.insertEvent(event, 0);
action2.delete();
condition2.delete();
}
});
afterAll(() => {
eventList.delete();
});
it('should search string in parameters only and respect case', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'mycharacter',
true,
true,
true,
false,
false
);
expect(searchResultEvents1.size()).toBe(0);
const searchResultEvents2 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'MyCharacter',
true,
true,
true,
false,
false
);
expect(searchResultEvents2.size()).toBe(1);
expect(searchResultEvents2.at(0).getEvent()).toBe(event1);
});
it('should search string in parameters only', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'mycharacter',
false,
true,
true,
false,
false
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event1);
const searchResultEvents2 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'position',
false,
true,
true,
false,
false
);
expect(searchResultEvents2.size()).toBe(0);
});
it('should search string in sentences', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'In Degrees',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event2);
});
it('should search string in sentences with parameter placeholders replaced', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'position of MyCharacter',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event1);
});
it('should search string in sentences with parameter placeholders replaced and special characters removed', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'towards 450200',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event1);
});
it('should search string in sentences with parameter placeholders replaced and special characters removed in searched string', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'towards 450;200',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event1);
});
it('should search string in sentences with parameter placeholders replaced and consecutive special characters removed in searched string', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'towards 450();200',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event1);
});
it('should search string in sentences with multiple adjacent spaces reduced to one space', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'the angle (in ',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event2);
});
it('should search string in sentences with leading and trailing white spaces', function () {
const searchResultEvents1 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
' the angle (in ',
false,
true,
true,
false,
true
);
expect(searchResultEvents1.size()).toBe(1);
expect(searchResultEvents1.at(0).getEvent()).toBe(event2);
const searchResultEvents2 = gd.EventsRefactorer.searchInEvents(
gd.JsPlatform.get(),
eventList,
'Delete OtherCharacter ',
false,
true,
true,
false,
true
);
expect(searchResultEvents2.size()).toBe(1);
expect(searchResultEvents2.at(0).getEvent()).toBe(event2);
});
});
});
describe('gd.EventsList', function () {
it('can have events', function () {
let list = new gd.EventsList();
list.insertEvent(new gd.StandardEvent(), 0);
let lastEvent = list.insertEvent(new gd.StandardEvent(), 1);
list.insertEvent(new gd.StandardEvent(), 1);
expect(list.getEventsCount()).toBe(3);
expect(list.getEventAt(2).ptr).toBe(lastEvent.ptr);
list.delete();
});
it('can create lots of new events', function () {
let project = new gd.ProjectHelper.createNewGDJSProject();
let list = new gd.EventsList();
for (let i = 0; i < 500; ++i) {
let evt = list.insertNewEvent(
project,
'BuiltinCommonInstructions::Standard',
0
);
expect(evt.getType()).toBe('BuiltinCommonInstructions::Standard');
expect(gd.asStandardEvent(list.getEventAt(0)).getType()).toBe(
'BuiltinCommonInstructions::Standard'
);
expect(list.getEventAt(0).getType()).toBe(
'BuiltinCommonInstructions::Standard'
);
}
project.delete();
list.delete();
});
it('can tell if it contains an event', function () {
let list = new gd.EventsList();
let parentEvent = list.insertEvent(new gd.StandardEvent(), 0);
let subEvent = parentEvent
.getSubEvents()
.insertEvent(new gd.StandardEvent(), 0);
expect(list.contains(parentEvent, false)).toBe(true);
expect(list.contains(subEvent, false)).toBe(false);
expect(list.contains(subEvent, true)).toBe(true);
list.delete();
});
it('can move an event to another list without invalidating it/copying it in memory', function () {
let list1 = new gd.EventsList();
let list2 = new gd.EventsList();
let list1ParentEvent = list1.insertEvent(new gd.StandardEvent(), 0);
let list2ParentEvent = list2.insertEvent(new gd.StandardEvent(), 0);
let originalSubEvent = list1ParentEvent
.getSubEvents()
.insertEvent(new gd.StandardEvent(), 0);
let originalSubEventPtr = originalSubEvent.ptr;
expect(
list1ParentEvent
.getSubEvents()
.moveEventToAnotherEventsList(
originalSubEvent,
list2ParentEvent.getSubEvents(),
0
)
).toBe(true);
expect(list2ParentEvent.getSubEvents().getEventsCount()).toBe(1);
let movedSubEvent = list2ParentEvent.getSubEvents().getEventAt(0);
expect(movedSubEvent.ptr).toBe(originalSubEventPtr);
list1.delete();
list2.delete();
});
});
describe('gd.BaseEvent', function () {
it('can have a type', function () {
let event = new gd.BaseEvent();
event.setType('Type1');
let event2 = new gd.BaseEvent();
event2.setType('Type2');
expect(event.getType()).toBe('Type1');
expect(event2.getType()).toBe('Type2');
event.delete();
event2.delete();
});
it('can be cloned', function () {
let event = new gd.BaseEvent();
event.setType('Type1');
let event2 = event.clone();
expect(event.getType()).toBe('Type1');
expect(event2.getType()).toBe('Type1');
event.delete();
event2.delete();
});
it('can be de/serialized', function () {
let event = new gd.BaseEvent();
expect(typeof event.serializeTo).toBe('function');
expect(typeof event.unserializeFrom).toBe('function');
event.delete();
});
});
describe('gd.ArbitraryEventsWorker', function () {
describe('gd.EventsParametersLister', function () {
it('can list parameters and their types', function () {
let project = new gd.ProjectHelper.createNewGDJSProject();
let list = new gd.EventsList();
let evt = new gd.StandardEvent();
let actions = evt.getActions();
let act = new gd.Instruction();
act.setType('Delete');
act.setParametersCount(1);
act.setParameter(0, 'MyObject');
actions.push_back(act);
evt = list.insertEvent(evt, 0);
let subEvt = new gd.StandardEvent();
let conditions = subEvt.getConditions();
let cnd = new gd.Instruction();
cnd.setType('PosX');
cnd.setParametersCount(3);
cnd.setParameter(0, 'MyObject');
cnd.setParameter(1, '<');
cnd.setParameter(2, '300');
conditions.push_back(cnd);
evt.getSubEvents().insertEvent(subEvt, 0);
let parametersLister = new gd.EventsParametersLister(project);
parametersLister.launch(list);
expect(parametersLister.getParametersAndTypes().keys().size()).toBe(3);
expect(parametersLister.getParametersAndTypes().get('MyObject')).toBe(
'object'
);
// There are a lot of parameter definitions with 'expression' instead
// of 'number'. They both means the same thing but 'expression' is
// deprecated.
expect(parametersLister.getParametersAndTypes().get('300')).toBe(
'number'
);
project.delete();
list.delete();
});
});
describe('gd.EventsPositionFinder', function () {
it('can find positions of a list of given events', function () {
// evt0
// ├── evt00 <------- 0
// │   ├── evt000
// │   ├── evt001
// │   └── evt002 <-- 1
// ├── evt01
// │   └── evt010 <-- 2
// └── evt02
//    └── evt020
// evt1 <------------ 3
// └── evt10 <------- 4
const events = new gd.EventsList();
let evt0 = new gd.StandardEvent();
evt0 = events.insertEvent(evt0, 0);
let evt1 = new gd.StandardEvent();
evt1 = events.insertEvent(evt1, 1);
let evt00 = new gd.StandardEvent();
evt00 = evt0.getSubEvents().insertEvent(evt00, 0);
let evt000 = new gd.StandardEvent();
let evt001 = new gd.StandardEvent();
let evt002 = new gd.StandardEvent();
evt000 = evt00.getSubEvents().insertEvent(evt000, 0);
evt001 = evt00.getSubEvents().insertEvent(evt001, 1);
evt002 = evt00.getSubEvents().insertEvent(evt002, 2);
let evt01 = new gd.StandardEvent();
evt01 = evt0.getSubEvents().insertEvent(evt01, 1);
let evt010 = new gd.StandardEvent();
evt010 = evt01.getSubEvents().insertEvent(evt010, 0);
let evt02 = new gd.StandardEvent();
evt02 = evt0.getSubEvents().insertEvent(evt02, 2);
let evt020 = new gd.StandardEvent();
evt020 = evt02.getSubEvents().insertEvent(evt020, 0);
let evt10 = new gd.StandardEvent();
evt10 = evt1.getSubEvents().insertEvent(evt10, 0);
const missingEvent = new gd.StandardEvent();
const positionFinder = new gd.EventsPositionFinder();
positionFinder.addEventToSearch(evt00);
positionFinder.addEventToSearch(evt10);
positionFinder.addEventToSearch(evt1);
positionFinder.addEventToSearch(evt002);
positionFinder.addEventToSearch(evt010);
positionFinder.addEventToSearch(missingEvent);
positionFinder.launch(events);
expect(positionFinder.getPositions().size()).toBe(6);
expect(positionFinder.getPositions().toJSArray()).toEqual([
1,
10,
9,
4,
6,
-1,
]);
events.delete();
});
});
});
describe('gd.GroupEvent', function () {
it('handle basic properties', function () {
const evt = new gd.GroupEvent();
evt.setName('MyName');
evt.setSource('http://source.url');
evt.setCreationTimestamp(150);
expect(evt.getName()).toBe('MyName');
expect(evt.getSource()).toBe('http://source.url');
expect(evt.getCreationTimestamp()).toBe(150);
});
it('can be folded', function () {
const evt = new gd.GroupEvent();
expect(evt.isFolded()).toBe(false);
evt.setFolded(true);
expect(evt.isFolded()).toBe(true);
});
it('can remember parameters used to create the group from a template event', function () {
const evt = new gd.GroupEvent();
let parameters = evt.getCreationParameters();
parameters.push_back('Param1');
parameters.push_back('Param2');
expect(evt.getCreationParameters().size()).toBe(2);
expect(evt.getCreationParameters().get(0)).toBe('Param1');
expect(evt.getCreationParameters().get(1)).toBe('Param2');
parameters.clear();
expect(evt.getCreationParameters().size()).toBe(0);
parameters.push_back('Param1');
expect(evt.getCreationParameters().get(0)).toBe('Param1');
});
});
describe('gd.StandardEvent', function () {
it('initial values', function () {
const evt = new gd.StandardEvent();
expect(evt.canHaveSubEvents()).toBe(true);
expect(evt.isExecutable()).toBe(true);
evt.delete();
});
it('conditions and actions', function () {
const evt = new gd.StandardEvent();
let conditions = evt.getConditions();
expect(evt.getConditions().size()).toBe(0);
let cnd = new gd.Instruction();
conditions.push_back(cnd);
expect(evt.getConditions().size()).toBe(1);
let actions = evt.getActions();
expect(evt.getActions().size()).toBe(0);
let act = new gd.Instruction();
actions.push_back(act);
expect(evt.getActions().size()).toBe(1);
evt.delete();
});
});
describe('gd.CommentEvent', function () {
it('initial values', function () {
const evt = new gd.CommentEvent();
expect(evt.canHaveSubEvents()).toBe(false);
expect(evt.isExecutable()).toBe(false);
});
it('can have a comment', function () {
const evt = new gd.CommentEvent();
evt.setComment('My nice comment about my events!');
expect(evt.getComment()).toBe('My nice comment about my events!');
});
it('can have a comment with UTF8 characters', function () {
const evt = new gd.CommentEvent();
evt.setComment('Hello 官话 world!');
expect(evt.getComment()).toBe('Hello 官话 world!');
});
it('can have a background color', function () {
const evt = new gd.CommentEvent();
evt.setBackgroundColor(100, 200, 255);
expect(evt.getBackgroundColorRed()).toBe(100);
expect(evt.getBackgroundColorGreen()).toBe(200);
expect(evt.getBackgroundColorBlue()).toBe(255);
});
it('can have a text color', function () {
const evt = new gd.CommentEvent();
evt.setTextColor(101, 201, 254);
expect(evt.getTextColorRed()).toBe(101);
expect(evt.getTextColorGreen()).toBe(201);
expect(evt.getTextColorBlue()).toBe(254);
});
});
describe('gd.SpriteObject', function () {
it('is a gd.Object', function () {
const project = new gd.ProjectHelper.createNewGDJSProject();
let object = project.insertNewObject(project, 'Sprite', 'MySpriteObject');
expect(object instanceof gd.Object).toBe(true);
expect(object.getVariables()).toBeTruthy();
project.delete();
});
it('can have animations', function () {
const obj = new gd.SpriteObject();
const animations = obj.getAnimations();
animations.addAnimation(new gd.Animation());
animations.addAnimation(new gd.Animation());
expect(animations.getAnimationsCount()).toBe(2);
animations.removeAnimation(1);
expect(animations.getAnimationsCount()).toBe(1);
});
it('can swap animations', function () {
const obj = new gd.SpriteObject();
const animations = obj.getAnimations();
animations.removeAllAnimations();
let anim1 = new gd.Animation();
let anim2 = new gd.Animation();
let sprite1 = new gd.Sprite();
let sprite2 = new gd.Sprite();
sprite1.setImageName('image1');
sprite2.setImageName('image2');
anim1.setDirectionsCount(1);
anim2.setDirectionsCount(1);
anim1.getDirection(0).addSprite(sprite1);
anim2.getDirection(0).addSprite(sprite2);
animations.addAnimation(anim1);
animations.addAnimation(anim2);
expect(
animations.getAnimation(0).getDirection(0).getSprite(0).getImageName()
).toBe('image1');
animations.swapAnimations(0, 1);
expect(
animations.getAnimation(0).getDirection(0).getSprite(0).getImageName()
).toBe('image2');
});
describe('gd.Direction', function () {
it('can swap sprites', function () {
const direction = new gd.Direction();
const sprite1 = new gd.Sprite();
const sprite2 = new gd.Sprite();
sprite1.setImageName('image1');
sprite2.setImageName('image2');
direction.addSprite(sprite1);
direction.addSprite(sprite2);
expect(direction.getSprite(0).getImageName()).toBe('image1');
direction.swapSprites(0, 1);
expect(direction.getSprite(0).getImageName()).toBe('image2');
direction.swapSprites(1, 0);
expect(direction.getSprite(0).getImageName()).toBe('image1');
direction.delete();
});
it('can move sprites', function () {
const direction = new gd.Direction();
const sprite1 = new gd.Sprite();
const sprite2 = new gd.Sprite();
const sprite3 = new gd.Sprite();
sprite1.setImageName('image1');
sprite2.setImageName('image2');
sprite3.setImageName('image3');
direction.addSprite(sprite1);
direction.addSprite(sprite2);
direction.addSprite(sprite3);
expect(direction.getSprite(0).getImageName()).toBe('image1');
direction.moveSprite(0, 2);
expect(direction.getSprite(0).getImageName()).toBe('image2');
expect(direction.getSprite(1).getImageName()).toBe('image3');
expect(direction.getSprite(2).getImageName()).toBe('image1');
direction.swapSprites(1, 1);
expect(direction.getSprite(0).getImageName()).toBe('image2');
expect(direction.getSprite(1).getImageName()).toBe('image3');
expect(direction.getSprite(2).getImageName()).toBe('image1');
direction.swapSprites(1, 0);
expect(direction.getSprite(0).getImageName()).toBe('image3');
expect(direction.getSprite(1).getImageName()).toBe('image2');
expect(direction.getSprite(2).getImageName()).toBe('image1');
direction.swapSprites(999, 998);
expect(direction.getSprite(0).getImageName()).toBe('image3');
expect(direction.getSprite(1).getImageName()).toBe('image2');
expect(direction.getSprite(2).getImageName()).toBe('image1');
direction.delete();
});
it('can have metadata', function () {
const direction = new gd.Direction();
expect(direction.getMetadata()).toBe('');
direction.setMetadata('{test: 1}');
expect(direction.getMetadata()).toBe('{test: 1}');
direction.delete();
});
});
describe('gd.Sprite', function () {
it('can have default points', function () {
let sprite1 = new gd.Sprite();
sprite1.getCenter().setX(2);
sprite1.getCenter().setY(3);
sprite1.getOrigin().setX(4);
sprite1.getOrigin().setY(5);
expect(sprite1.getCenter().getX()).toBe(2);
expect(sprite1.getCenter().getY()).toBe(3);
expect(sprite1.getOrigin().getX()).toBe(4);
expect(sprite1.getOrigin().getY()).toBe(5);
});
it('can have custom points', function () {
let sprite1 = new gd.Sprite();
let point = new gd.Point('test');
sprite1.addPoint(point);
point.delete();
expect(sprite1.hasPoint('test')).toBe(true);
sprite1.getPoint('test').setX(1);
sprite1.getPoint('test').setY(2);
expect(sprite1.getPoint('test').getX()).toBe(1);
expect(sprite1.getPoint('test').getY()).toBe(2);
});
});
});
describe('gd.MetadataProvider', function () {
it('can return metadata about expressions (even if they do not exist)', function () {
expect(
gd.MetadataProvider.getExpressionMetadata(
gd.JsPlatform.get(),
'NotExistingExpression'
).getFullName()
).toBe('');
});
describe('gd.ObjectMetadata', function () {
it('can return standard information about Sprite object', function () {
let objMetadata = gd.MetadataProvider.getObjectMetadata(
gd.JsPlatform.get(),
'Sprite'
);
expect(objMetadata.getName()).toBe('Sprite');
expect(objMetadata.getFullName()).toBe('Sprite');
expect(objMetadata.getDescription().length).not.toBe(0);
expect(objMetadata.getIconFilename().length).not.toBe(0);
});
it('can have conditions and actions added at the same time for booleans', function () {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'TestExtensionName',
'Full name of test extension',
'Description of test extension',
'Author of test extension',
'License of test extension'
);
const fakeObject = new gd.ObjectJsImplementation();
const objectMetadata = extension.addObject(
'FakeObject',
'FakeObject',
'This is FakeObject',
'',
fakeObject
);
objectMetadata
.addExpressionAndConditionAndAction(
'boolean',
'Disabled',
'Disabled',
'the object is disabled',
'disabled',
'',
'Disable.png'
)
.addParameter('object', 'My object', 'MyObject', false)
.useStandardParameters(
'boolean',
gd.ParameterOptions.makeNewOptions()
)
.setFunctionName('setDisabled')
.setGetter('isDisabled');
expect(
objectMetadata
.getAllConditions()
.has('TestExtensionName::FakeObject::Disabled')
).toBe(true);
expect(
objectMetadata
.getAllActions()
.has('TestExtensionName::FakeObject::SetDisabled')
).toBe(true);
// Check sentences are properly set up.
expect(
objectMetadata
.getAllConditions()
.get('TestExtensionName::FakeObject::Disabled')
.getSentence()
).toBe('_PARAM0_ is disabled');
expect(
objectMetadata
.getAllActions()
.get('TestExtensionName::FakeObject::SetDisabled')
.getSentence()
).toBe('Set _PARAM0_ as disabled: _PARAM1_');
});
});
describe('gd.BehaviorMetadata', function () {
it('have standard methods to get information', function () {
let autoMetadata = gd.MetadataProvider.getBehaviorMetadata(
gd.JsPlatform.get(),
'NotExistingBehavior'
);
expect(autoMetadata.getFullName).not.toBe(undefined);
expect(autoMetadata.getDefaultName).not.toBe(undefined);
expect(autoMetadata.getDescription).not.toBe(undefined);
expect(autoMetadata.getGroup).not.toBe(undefined);
expect(autoMetadata.getIconFilename).not.toBe(undefined);
expect(autoMetadata.getObjectType).not.toBe(undefined);
});
it('can have conditions and actions added at the same time for booleans', function () {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'TestExtensionName',
'Full name of test extension',
'Description of test extension',
'Author of test extension',
'License of test extension'
);
const dummyBehavior = new gd.BehaviorJsImplementation();
dummyBehavior.initializeContent = function (behaviorContent) {};
const behaviorMetadata = extension.addBehavior(
'DummyBehavior',
'Dummy behavior for testing',
'DummyBehavior',
'Do nothing.',
'',
'',
'DummyBehavior',
dummyBehavior,
new gd.BehaviorsSharedData()
);
behaviorMetadata
.addExpressionAndConditionAndAction(
'boolean',
'Disabled',
'Disabled',
'the object is disabled',
'disabled',
'',
'Disable.png'
)
.addParameter('object', 'My object', '', false)
.useStandardParameters(
'boolean',
gd.ParameterOptions.makeNewOptions()
)
.setFunctionName('setDisabled')
.setGetter('isDisabled');
expect(
behaviorMetadata
.getAllConditions()
.has('TestExtensionName::DummyBehavior::Disabled')
).toBe(true);
expect(
behaviorMetadata
.getAllActions()
.has('TestExtensionName::DummyBehavior::SetDisabled')
).toBe(true);
// Check sentences are properly set up.
expect(
behaviorMetadata
.getAllConditions()
.get('TestExtensionName::DummyBehavior::Disabled')
.getSentence()
).toBe('_PARAM0_ is disabled');
expect(
behaviorMetadata
.getAllActions()
.get('TestExtensionName::DummyBehavior::SetDisabled')
.getSentence()
).toBe('Set _PARAM0_ as disabled: _PARAM1_');
});
});
describe('gd.EffectMetadata', function () {
it('have standard methods to get information', function () {
let autoMetadata = gd.MetadataProvider.getEffectMetadata(
gd.JsPlatform.get(),
'NotExistingEffect'
);
expect(autoMetadata.getType).not.toBe(undefined);
expect(autoMetadata.getFullName).not.toBe(undefined);
expect(autoMetadata.getDescription).not.toBe(undefined);
expect(autoMetadata.getProperties).not.toBe(undefined);
});
});
});
describe('gd.ResourcesMergingHelper (and gd.AbstractFileSystemJS)', function () {
it('should export files of the project', function () {
// Create a project with a mix of resources
const project = new gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const resource = new gd.ImageResource();
const resource2 = new gd.ImageResource();
const resource3 = new gd.ImageResource();
const resource4 = new gd.ImageResource();
const resource5 = new gd.ImageResource();
resource.setName('MyResource');
resource.setFile('MyResource.png');
resource2.setName('MyAudioResource');
resource2.setFile('MyResource.wav');
resource3.setName('test/MyResource.png');
resource3.setFile('test/MyResource.png');
resource4.setName('test/MyResourceWithExtension');
resource4.setFile('test/MyResourceWithExtension');
resource5.setName('test/sub/folder/MyResourceWithExtension');
resource5.setFile('test/sub/folder/MyResourceWithExtension');
project.getResourcesManager().addResource(resource);
project.getResourcesManager().addResource(resource2);
project.getResourcesManager().addResource(resource3);
project.getResourcesManager().addResource(resource4);
project.getResourcesManager().addResource(resource5);
const fs = makeFakeAbstractFileSystem(gd, {});
// Check that ResourcesMergingHelper can update the filenames
const resourcesMergingHelper = new gd.ResourcesMergingHelper(project.getResourcesManager(), fs);
resourcesMergingHelper.setBaseDirectory('/my/project/');
gd.ResourceExposer.exposeWholeProjectResources(
project,
resourcesMergingHelper
);
const oldAndNewFilenames = resourcesMergingHelper.getAllResourcesOldAndNewFilename();
expect(oldAndNewFilenames.get('/my/project/MyResource.png')).toBe(
'MyResource.png'
);
expect(oldAndNewFilenames.get('/my/project/MyResource.wav')).toBe(
'MyResource.wav'
);
expect(oldAndNewFilenames.get('/my/project/test/MyResource.png')).toBe(
'MyResource2.png'
);
expect(
oldAndNewFilenames.get('/my/project/test/MyResourceWithExtension')
).toBe('MyResourceWithExtension');
expect(
oldAndNewFilenames.get(
'/my/project/test/sub/folder/MyResourceWithExtension'
)
).toBe('MyResourceWithExtension2');
resourcesMergingHelper.delete();
project.delete();
});
});
describe('gd.ProjectResourcesCopier (and gd.AbstractFileSystemJS)', function () {
it('should export files of the project', function () {
// Create a project with a mix of resources, stored in /my/project folder.
const project = new gd.ProjectHelper.createNewGDJSProject();
project.setProjectFile('/my/project/project.json');
const layout = project.insertNewLayout('Scene', 0);
const resource = new gd.ImageResource();
const resource2 = new gd.ImageResource();
const resource3 = new gd.ImageResource();
const resource4 = new gd.ImageResource();
const resource5 = new gd.ImageResource();
resource.setName('MyResource');
resource.setFile('MyResource.png');
resource2.setName('MyAudioResource');
resource2.setFile('MyResource.wav');
resource3.setName('MyAbsoluteResource');
resource3.setFile('/my/absolute/path/MyResource2.png');
resource4.setName('test/MyResourceWithoutExtension');
resource4.setFile('test/MyResourceWithoutExtension');
resource5.setName('test/sub/folder/MyResourceWithoutExtension');
resource5.setFile('test/sub/folder/MyResourceWithoutExtension'); // Same filename as resource4
project.getResourcesManager().addResource(resource);
project.getResourcesManager().addResource(resource2);
project.getResourcesManager().addResource(resource3);
project.getResourcesManager().addResource(resource4);
project.getResourcesManager().addResource(resource5);
const fs = makeFakeAbstractFileSystem(gd, {});
// Check that resources can be copied to another folder:
// * including absolute files.
// * preserving relative file structures
fs.copyFile.mockClear();
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fs,
'/my/new/folder',
false,
false,
true
);
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.png',
'/my/new/folder/MyResource.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.wav',
'/my/new/folder/MyResource.wav'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/absolute/path/MyResource2.png',
'/my/new/folder/MyResource2.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/MyResourceWithoutExtension',
'/my/new/folder/test/MyResourceWithoutExtension'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/sub/folder/MyResourceWithoutExtension',
'/my/new/folder/test/sub/folder/MyResourceWithoutExtension'
);
// Check that resources can be copied to another folder:
// * including absolute files.
// * NOT preserving relative file structures
// Check that filename collisions are avoided.
fs.copyFile.mockClear();
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fs,
'/my/new/folder',
false,
false,
false
);
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.png',
'/my/new/folder/MyResource.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.wav',
'/my/new/folder/MyResource.wav'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/absolute/path/MyResource2.png',
'/my/new/folder/MyResource2.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/MyResourceWithoutExtension',
'/my/new/folder/MyResourceWithoutExtension'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/sub/folder/MyResourceWithoutExtension',
'/my/new/folder/MyResourceWithoutExtension2'
);
// Check that resources can be copied to another folder:
// * without touching absolute files.
// * preserving relative file structures
fs.copyFile.mockClear();
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fs,
'/my/new/folder',
false,
true,
true
);
expect(fs.copyFile).toHaveBeenCalledTimes(4); // Only the 4 relative resources are copied
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.png',
'/my/new/folder/MyResource.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.wav',
'/my/new/folder/MyResource.wav'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/MyResourceWithoutExtension',
'/my/new/folder/test/MyResourceWithoutExtension'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/sub/folder/MyResourceWithoutExtension',
'/my/new/folder/test/sub/folder/MyResourceWithoutExtension'
);
// Check that resources can be copied to another folder:
// * without touching absolute files.
// * NOT preserving relative file structures
// Check that filename collisions are avoided.
fs.copyFile.mockClear();
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fs,
'/my/new/folder',
false,
true,
false
);
expect(fs.copyFile).toHaveBeenCalledTimes(5); // All 5 resources are copied
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.png',
'/my/new/folder/MyResource.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/MyResource.wav',
'/my/new/folder/MyResource.wav'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/absolute/path/MyResource2.png',
'/my/new/folder/MyResource2.png'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/MyResourceWithoutExtension',
'/my/new/folder/MyResourceWithoutExtension'
);
expect(fs.copyFile).toHaveBeenCalledWith(
'/my/project/test/sub/folder/MyResourceWithoutExtension',
'/my/new/folder/MyResourceWithoutExtension2'
);
project.delete();
});
});
describe('gd.Exporter (and gd.AbstractFileSystemJS)', function () {
it('should export a layout for preview', function (done) {
let fs = new gd.AbstractFileSystemJS();
let project = new gd.ProjectHelper.createNewGDJSProject();
let layout = project.insertNewLayout('Scene', 0);
fs.mkDir = fs.clearDir = function () {};
fs.getTempDir = function (path) {
return '/tmp/';
};
fs.fileNameFrom = function (fullpath) {
return path.basename(fullpath);
};
fs.dirNameFrom = function (fullpath) {
return path.dirname(fullpath);
};
fs.readDir = function () {
return new gd.VectorString();
};
fs.writeToFile = function (path, content) {
//Validate that some code have been generated:
expect(content).toMatch('runtimeScene.getOnceTriggers().startNewFrame');
done();
};
const exporter = new gd.Exporter(fs);
const previewExportOptions = new gd.PreviewExportOptions(
project,
'/path/for/export/'
);
previewExportOptions.setLayoutName('Scene');
exporter.exportProjectForPixiPreview(previewExportOptions);
previewExportOptions.delete();
exporter.delete();
});
});
describe('gd.EventsRemover', function () {
it('should remove events', function () {
let list = new gd.EventsList();
let event1 = list.insertEvent(new gd.StandardEvent(), 0);
let event2 = list.insertEvent(new gd.StandardEvent(), 1);
let event3 = list.insertEvent(new gd.StandardEvent(), 2);
let remover = new gd.EventsRemover();
remover.addEventToRemove(event1);
remover.addEventToRemove(event3);
remover.launch(list);
expect(list.getEventsCount()).toBe(1);
expect(list.getEventAt(0)).toBe(event2);
});
});
describe('gd.WholeProjectRefactorer', function () {
it('should rename and delete an object', function () {
let project = new gd.ProjectHelper.createNewGDJSProject();
let layout = project.insertNewLayout('Scene', 0);
let instance1 = layout.getInitialInstances().insertNewInitialInstance();
let instance2 = layout.getInitialInstances().insertNewInitialInstance();
instance1.setObjectName('Object1');
instance2.setObjectName('Object2');
gd.WholeProjectRefactorer.objectOrGroupRenamedInLayout(
project,
layout,
'Object1',
'Object3',
/* isObjectGroup=*/ false
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object1')).toBe(
false
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object2')).toBe(
true
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object3')).toBe(
true
);
gd.WholeProjectRefactorer.objectOrGroupRemovedInLayout(
project,
layout,
'Object3',
/* isObjectGroup=*/ false,
true
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object1')).toBe(
false
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object2')).toBe(
true
);
expect(layout.getInitialInstances().hasInstancesOfObject('Object3')).toBe(
false
);
});
// See other tests in WholeProjectRefactorer.cpp
});
describe('gd.ExpressionParser2 and gd.ExpressionValidator', function () {
let project = null;
let layout = null;
beforeAll(() => {
project = new gd.ProjectHelper.createNewGDJSProject();
layout = project.insertNewLayout('Scene', 0);
layout.insertNewObject(project, 'Sprite', 'MySpriteObject', 0);
});
function testExpression(
type,
expression,
expectedError,
expectedErrorPosition,
expectedError2,
expectedErrorPosition2
) {
const parser = new gd.ExpressionParser2();
const expressionNode = parser.parseExpression(expression).get();
const expressionValidator = new gd.ExpressionValidator(
gd.JsPlatform.get(),
gd.ProjectScopedContainers.makeNewProjectScopedContainersForProjectAndLayout(
project,
layout
),
type
);
expressionNode.visit(expressionValidator);
if (expectedError2) {
expect(expressionValidator.getAllErrors().size()).toBe(2);
expect(expressionValidator.getAllErrors().at(0).getMessage()).toBe(
expectedError
);
if (expectedErrorPosition)
expect(
expressionValidator.getAllErrors().at(0).getStartPosition()
).toBe(expectedErrorPosition);
expect(expressionValidator.getAllErrors().at(1).getMessage()).toBe(
expectedError2
);
if (expectedErrorPosition2)
expect(
expressionValidator.getAllErrors().at(1).getStartPosition()
).toBe(expectedErrorPosition2);
} else if (expectedError) {
expect(expressionValidator.getAllErrors().size()).toBe(1);
expect(expressionValidator.getAllErrors().at(0).getMessage()).toBe(
expectedError
);
if (expectedErrorPosition)
expect(
expressionValidator.getAllErrors().at(0).getStartPosition()
).toBe(expectedErrorPosition);
} else {
expect(expressionValidator.getAllErrors().size()).toBe(0);
}
expressionValidator.delete();
parser.delete();
}
it('can parse valid expressions (number)', function () {
testExpression('number', '1+1');
testExpression('number', '2-3');
testExpression('number', '4/5');
testExpression('number', '6*7');
testExpression('number', '8 + 9');
testExpression('number', '10 + 11');
testExpression('number', '12 + 13 - 14');
testExpression('number', ' 15 + 16 - 17 ');
testExpression('number', '.14');
testExpression('number', '3.');
});
it('can parse valid expressions (string)', function () {
testExpression('string', '"Hello"');
testExpression('string', '"Hello" + " " + "World"');
});
it('can parse valid expressions ("number|string" type)', function () {
testExpression('number|string', '1+1');
testExpression('number|string', '2-3');
testExpression('number|string', '4/5');
testExpression('number|string', '6*7');
testExpression('number|string', '"Hello"');
testExpression('number|string', '"Hello" + " " + "World"');
});
it('report errors in invalid expressions', function () {
testExpression(
'number',
'1//2',
'You must enter a number or a valid expression call.',
2
);
testExpression('number', 'bad expression', 'You must enter a number.', 0);
testExpression(
'number',
'1 + test()',
"Cannot find an expression with this name: test\nDouble check that you've not made any typo in the name.",
4
);
testExpression(
'number',
'3..14',
'More than one term was found. Verify that your expression is properly written.',
2,
'No operator found. Did you forget to enter an operator (like +, -, * or /) between numbers or expressions?',
2
);
testExpression(
'string',
'="Mynewscene"',
'You must enter a text (between quotes) or a valid expression call.',
0
);
});
it('report errors in invalid expressions ("number|string" type)', function () {
testExpression(
'number|string',
'123 + "World"',
'You entered a text, but a number was expected.',
6
);
testExpression(
'number|string',
'"World" + 123',
'You entered a number, but a text was expected (in quotes).',
10
);
testExpression(
'number|string',
'"World" + ToNumber("123")',
'You tried to use an expression that returns a number, but a string is expected. Use `ToString` if you need to convert a number to a string.',
10
);
testExpression(
'number|string',
'123 + ToString(456)',
'You tried to use an expression that returns a string, but a number is expected. Use `ToNumber` if you need to convert a string to a number.',
6
);
});
it('can parse valid expressions with free functions', function () {
testExpression('number', '1+sin(3.14)');
testExpression('number', 'abs(-5)');
testExpression('number', 'abs(-5) + cos(sin(3))');
testExpression('number', 'atan2(-5, 3)');
testExpression('number', 'MouseX("", 0) + 1');
});
it('can report errors when using too much arguments', function () {
testExpression(
'number',
'abs(-5, 3)',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
);
testExpression(
'number',
'MouseX("", 0, 0) + 1',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be: 0-2"
);
});
it('can parse valid expressions with free functions having optional parameters', function () {
testExpression('number', 'MouseX() + 1');
testExpression('number', 'MouseX("") + 1');
});
it('can parse expressions with objects functions', function () {
testExpression('number', 'MySpriteObject.X()');
testExpression('number', 'MySpriteObject.X() + 1');
testExpression('number', 'MySpriteObject.PointX("Point")');
});
it('can report errors when using too much arguments in object functions', function () {
testExpression(
'number',
'MySpriteObject.PointX("Point", 2)',
"This parameter was not expected by this expression. Remove it or verify that you've entered the proper expression name. The number of parameters must be exactly 1"
);
});
it('can parse arguments being expressions', function () {
testExpression('number', 'MouseX(VariableString(myVariable), 0) + 1');
});
});
describe('gd.Vector2f', function () {
describe('gd.VectorVector2f', function () {
it('can be used to manipulate a vector of gd.Vector2f', function () {
const vectorVector2f = new gd.VectorVector2f();
const vector2f = new gd.Vector2f();
vectorVector2f.push_back(vector2f);
vectorVector2f.push_back(vector2f);
vectorVector2f.push_back(vector2f);
expect(vectorVector2f.size()).toBe(3);
vectorVector2f.at(0).set_x(1);
vectorVector2f.at(1).set_x(2);
vectorVector2f.at(2).set_x(3);
expect(vectorVector2f.at(0).get_x()).toBe(1);
expect(vectorVector2f.at(1).get_x()).toBe(2);
expect(vectorVector2f.at(2).get_x()).toBe(3);
gd.moveVector2fInVector(vectorVector2f, 2, 0);
expect(vectorVector2f.at(0).get_x()).toBe(3);
expect(vectorVector2f.at(1).get_x()).toBe(1);
expect(vectorVector2f.at(2).get_x()).toBe(2);
gd.removeFromVectorVector2f(vectorVector2f, 1);
expect(vectorVector2f.size()).toBe(2);
expect(vectorVector2f.at(0).get_x()).toBe(3);
expect(vectorVector2f.at(1).get_x()).toBe(2);
vectorVector2f.clear();
expect(vectorVector2f.size()).toBe(0);
});
});
});
describe('gd.PlatformExtension', function () {
const makeTestExtension = () => {
const extension = new gd.PlatformExtension();
extension
.setExtensionInformation(
'TestExtensionName',
'Full name of test extension',
'Description of test extension',
'Author of test extension',
'License of test extension'
)
.setExtensionHelpPath('/path/to/extension/help');
return extension;
};
it('can be created and have basic information filled', function () {
const extension = makeTestExtension();
expect(extension.getName()).toBe('TestExtensionName');
expect(extension.getFullName()).toBe('Full name of test extension');
expect(extension.getDescription()).toBe('Description of test extension');
expect(extension.getAuthor()).toBe('Author of test extension');
expect(extension.getLicense()).toBe('License of test extension');
expect(extension.getHelpPath()).toBe('/path/to/extension/help');
extension.delete();
});
it('can have actions and conditions added', function () {
const extension = makeTestExtension();
extension
.addCondition(
'BannerShowing',
'Banner showing',
'Check if there is a banner being displayed.',
'Banner is showing',
'AdMob',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isBannerShowing');
expect(
extension.getAllConditions().has('TestExtensionName::BannerShowing')
).toBe(true);
const condition = extension
.getAllConditions()
.get('TestExtensionName::BannerShowing');
expect(condition.getFullName()).toBe('Banner showing');
expect(condition.isHidden()).toBe(false);
// Check also the API to duplicate a condition.
extension
.addDuplicatedCondition('AnotherCondition', 'BannerShowing')
.setHidden();
expect(
extension.getAllConditions().has('TestExtensionName::AnotherCondition')
).toBe(true);
const copiedCondition = extension
.getAllConditions()
.get('TestExtensionName::AnotherCondition');
expect(copiedCondition.getFullName()).toBe('Banner showing');
expect(copiedCondition.isHidden()).toBe(true);
extension.delete();
});
it('can have expressions and conditions added at the same time', function () {
const extension = makeTestExtension();
extension
.addExpressionAndCondition(
'number',
'PlayerHealth',
'Health of the player',
'The health of the player, from 0 to 100.',
'the health of the player',
'Health Management',
'SomeHealthIcon.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', 'Some stuff', '', false)
.setParameterLongDescription('Blabla')
.setFunctionName('some.method.to.getPlayerHealth')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions());
expect(
extension.getAllConditions().has('TestExtensionName::PlayerHealth')
).toBe(true);
const declaredCondition = extension
.getAllConditions()
.get('TestExtensionName::PlayerHealth');
expect(declaredCondition.getParametersCount()).toBe(4);
expect(
extension.getAllExpressions().has('TestExtensionName::PlayerHealth')
).toBe(true);
const declaredExpression = extension
.getAllExpressions()
.get('TestExtensionName::PlayerHealth');
expect(declaredExpression.getParametersCount()).toBe(2);
});
it('can have expressions, conditions and actions added at the same time', function () {
const extension = makeTestExtension();
extension
.addExpressionAndConditionAndAction(
'number',
'PlayerHealth',
'Health of the player',
'The health of the player, from 0 to 100.',
'the health of the player',
'Health Management',
'SomeHealthIcon.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', 'Some stuff', '', false)
.setParameterLongDescription('Blabla')
.setFunctionName('some.method.to.getPlayerHealth')
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions());
expect(
extension.getAllConditions().has('TestExtensionName::PlayerHealth')
).toBe(true);
const declaredCondition = extension
.getAllConditions()
.get('TestExtensionName::PlayerHealth');
expect(declaredCondition.getParametersCount()).toBe(4);
expect(
extension.getAllActions().has('TestExtensionName::SetPlayerHealth')
).toBe(true);
const declaredAction = extension
.getAllActions()
.get('TestExtensionName::SetPlayerHealth');
expect(declaredAction.getParametersCount()).toBe(4);
expect(
extension.getAllExpressions().has('TestExtensionName::PlayerHealth')
).toBe(true);
const declaredExpression = extension
.getAllExpressions()
.get('TestExtensionName::PlayerHealth');
expect(declaredExpression.getParametersCount()).toBe(2);
});
it('can have conditions and actions added at the same time for booleans', function () {
const extension = makeTestExtension();
extension
.addExpressionAndConditionAndAction(
'boolean',
'Disabled',
'Disabled',
'the object is disabled',
'disabled',
'',
'Disable.png'
)
.addParameter('object', 'My object', 'MyObject', false)
.useStandardParameters('boolean', gd.ParameterOptions.makeNewOptions())
.setFunctionName('setDisabled')
.setGetter('isDisabled');
expect(
extension.getAllConditions().has('TestExtensionName::Disabled')
).toBe(true);
const declaredCondition = extension
.getAllConditions()
.get('TestExtensionName::Disabled');
expect(
extension.getAllActions().has('TestExtensionName::SetDisabled')
).toBe(true);
const declaredAction = extension
.getAllActions()
.get('TestExtensionName::SetDisabled');
expect(declaredAction.getParametersCount()).toBe(2);
});
});
describe('gd.Platform (using gd.JsPlatform)', function () {
it('can have extension added and removed', function () {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'MyNewExtension',
'Full name of test extension',
'Description of test extension',
'Author of test extension',
'License of test extension'
);
gd.JsPlatform.get().addNewExtension(extension);
expect(gd.JsPlatform.get().isExtensionLoaded('MyNewExtension')).toBe(
true
);
gd.JsPlatform.get().removeExtension('MyNewExtension');
expect(gd.JsPlatform.get().isExtensionLoaded('MyNewExtension')).toBe(
false
);
gd.JsPlatform.get().addNewExtension(extension);
gd.JsPlatform.get().addNewExtension(extension);
expect(gd.JsPlatform.get().isExtensionLoaded('MyNewExtension')).toBe(
true
);
gd.JsPlatform.get().removeExtension('MyNewExtension');
expect(gd.JsPlatform.get().isExtensionLoaded('MyNewExtension')).toBe(
false
);
extension.delete();
});
it('has a namespace separator', function () {
expect(gd.PlatformExtension.getNamespaceSeparator()).toBe('::');
});
});
describe('gd.ParameterMetadata', function () {
it('can tell the type of a parameter', function () {
expect(gd.ParameterMetadata.isObject('object')).toBe(true);
expect(gd.ParameterMetadata.isObject('objectPtr')).toBe(true);
expect(gd.ParameterMetadata.isObject('123')).toBe(false);
expect(gd.ParameterMetadata.isBehavior('behavior')).toBe(true);
expect(gd.ParameterMetadata.isBehavior('behavior34234')).toBe(false);
});
it('can have attributes and be serialized', function () {
const parameter1 = new gd.ParameterMetadata();
parameter1.setType('objectList');
parameter1.setName('MyObjectWithoutType');
parameter1.setDescription('The first object to be used');
parameter1.setLongDescription('A long description');
parameter1.setDefaultValue('Default value');
parameter1.setOptional(true);
const serializerElement = new gd.SerializerElement();
parameter1.serializeTo(serializerElement);
const parameter2 = new gd.ParameterMetadata();
parameter2.unserializeFrom(serializerElement);
expect(parameter1.getType()).toBe('objectList');
expect(parameter1.getName()).toBe('MyObjectWithoutType');
expect(parameter1.getDescription()).toBe('The first object to be used');
expect(parameter1.getLongDescription()).toBe('A long description');
expect(parameter1.getDefaultValue()).toBe('Default value');
expect(parameter1.isOptional()).toBe(true);
});
});
describe('gd.ParameterMetadataTools', function () {
it('can create an object container from parameters', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const parameters = new gd.VectorParameterMetadata();
const parameter1 = new gd.ParameterMetadata();
parameter1.setType('objectList');
parameter1.setName('MyObjectWithoutType');
parameter1.setDescription('The first object to be used');
const parameter2 = new gd.ParameterMetadata();
parameter2.setType('expression');
parameter2.setName('MyNumber');
parameter2.setDescription('Some number');
const parameter3 = new gd.ParameterMetadata();
parameter3.setType('objectList');
// parameter3.setName(''); No name for this parameter
parameter3.setDescription(
'This parameter will be skipped, as it has no name'
);
parameter3.setExtraInfo('Sprite');
const parameter4 = new gd.ParameterMetadata();
parameter4.setType('string');
parameter4.setName('MyString');
parameter4.setDescription('Some string');
const parameter5 = new gd.ParameterMetadata();
parameter5.setType('objectList');
parameter5.setName('MySpriteObject');
parameter5.setDescription('The second object to be used, a sprite');
parameter5.setExtraInfo('Sprite');
parameters.push_back(parameter1);
parameters.push_back(parameter2);
parameters.push_back(parameter3);
parameters.push_back(parameter4);
parameters.push_back(parameter5);
parameters.push_back(parameter5);
expect(parameters.size()).toBe(6);
gd.removeFromVectorParameterMetadata(parameters, 5);
expect(parameters.size()).toBe(5);
objectsContainer = new gd.ObjectsContainer();
gd.ParameterMetadataTools.parametersToObjectsContainer(
project,
parameters,
objectsContainer
);
expect(objectsContainer.getObjectsCount()).toBe(2);
expect(objectsContainer.hasObjectNamed('MyObjectWithoutType')).toBe(true);
expect(objectsContainer.getObject('MyObjectWithoutType').getType()).toBe(
''
);
expect(objectsContainer.hasObjectNamed('MySpriteObject')).toBe(true);
expect(objectsContainer.getObject('MySpriteObject').getType()).toBe(
'Sprite'
);
project.delete();
});
it('can give the previous object parameter', function () {
const parameters = new gd.VectorParameterMetadata();
const parameter1 = new gd.ParameterMetadata();
parameter1.setType('objectList');
const parameter2 = new gd.ParameterMetadata();
parameter2.setType('behavior');
const parameter3 = new gd.ParameterMetadata();
parameter3.setType('objectList');
const parameter4 = new gd.ParameterMetadata();
parameter4.setType('string');
const parameter5 = new gd.ParameterMetadata();
parameter5.setType('objectvar');
const parameter6 = new gd.ParameterMetadata();
parameter6.setType('objectvar');
parameters.push_back(parameter1);
parameters.push_back(parameter2);
parameters.push_back(parameter3);
parameters.push_back(parameter4);
parameters.push_back(parameter5);
parameters.push_back(parameter6);
objectsContainer = new gd.ObjectsContainer();
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 0)
).toBe(0);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 1)
).toBe(0);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 2)
).toBe(2);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 3)
).toBe(2);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 4)
).toBe(2);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 5)
).toBe(2);
expect(
gd.ParameterMetadataTools.getObjectParameterIndexFor(parameters, 999)
).toBe(-1);
});
});
describe('gd.EventsFunction', () => {
it('can store events', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const eventsFunction = new gd.EventsFunction();
const events = eventsFunction.getEvents();
expect(events.getEventsCount()).toBe(0);
let evt = events.insertNewEvent(
project,
'BuiltinCommonInstructions::Standard',
0
);
expect(events.getEventsCount()).toBe(1);
eventsFunction.delete();
project.delete();
});
it('can have a name, fullname and description', function () {
const eventsFunction = new gd.EventsFunction();
eventsFunction.setName('My name');
eventsFunction.setFullName('My descriptive name');
eventsFunction.setDescription('My description');
expect(eventsFunction.getName()).toBe('My name');
expect(eventsFunction.getFullName()).toBe('My descriptive name');
expect(eventsFunction.getDescription()).toBe('My description');
eventsFunction.delete();
});
});
describe('gd.EventsFunctionsExtension', () => {
it('can have a namespace, version, name, fullname, description', function () {
const eventsFunctionsExtension = new gd.EventsFunctionsExtension();
eventsFunctionsExtension.setNamespace('MyExt');
eventsFunctionsExtension.setVersion('1.1');
eventsFunctionsExtension.setName('My name');
eventsFunctionsExtension.setFullName('My descriptive name');
eventsFunctionsExtension.setDescription('My description');
eventsFunctionsExtension.setIconUrl('data:image/png;base64,iVBetcetc');
eventsFunctionsExtension.setPreviewIconUrl(
'http://resources.gdevelop-app.com/test'
);
expect(eventsFunctionsExtension.getNamespace()).toBe('MyExt');
expect(eventsFunctionsExtension.getVersion()).toBe('1.1');
expect(eventsFunctionsExtension.getName()).toBe('My name');
expect(eventsFunctionsExtension.getFullName()).toBe(
'My descriptive name'
);
expect(eventsFunctionsExtension.getDescription()).toBe('My description');
expect(eventsFunctionsExtension.getPreviewIconUrl()).toBe(
'http://resources.gdevelop-app.com/test'
);
const eventsFunction = eventsFunctionsExtension.insertNewEventsFunction(
'MyFunction',
0
);
expect(
eventsFunctionsExtension.hasEventsFunctionNamed('MyFunction')
).toBe(true);
expect(
eventsFunctionsExtension.hasEventsFunctionNamed('MyNotExistingFunction')
).toBe(false);
expect(eventsFunctionsExtension.getEventsFunctionsCount()).toBe(1);
expect(eventsFunctionsExtension.getEventsFunctionAt(0).getName()).toBe(
'MyFunction'
);
expect(
eventsFunctionsExtension.getEventsFunction('MyFunction').getName()
).toBe('MyFunction');
eventsFunctionsExtension.delete();
});
it('can have events based behaviors', function () {
const eventsFunctionsExtension = new gd.EventsFunctionsExtension();
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().getCount()
).toBe(0);
eventsFunctionsExtension
.getEventsBasedBehaviors()
.insertNew('MyBehavior1', 0);
eventsFunctionsExtension
.getEventsBasedBehaviors()
.insertNew('MyBehavior2', 1);
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().getCount()
).toBe(2);
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().has('MyBehavior1')
).toBe(true);
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().has('MyBehavior2')
).toBe(true);
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().has('MyBehavior3')
).toBe(false);
expect(
eventsFunctionsExtension.getEventsBasedBehaviors().getAt(1).getName()
).toBe('MyBehavior2');
expect(
eventsFunctionsExtension
.getEventsBasedBehaviors()
.get('MyBehavior1')
.getName()
).toBe('MyBehavior1');
});
it('can be unserialized, with tags as a (deprecated) string', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const eventsFunctionsExtension = new gd.EventsFunctionsExtension();
// Check that we can unserialize tags from the old format, where they were
// written as a string.
const element = gd.Serializer.fromJSObject({
version: '1.0.0',
extensionNamespace: '',
shortDescription: '',
description: '',
name: 'ExtensionName',
fullName: '',
tags: ' 12,34, 56 , 789',
author: '',
previewIconUrl: '',
iconUrl: '',
helpPath: '',
eventsFunctions: [],
eventsBasedBehaviors: [],
});
eventsFunctionsExtension.unserializeFrom(project, element);
element.delete();
expect(eventsFunctionsExtension.getVersion()).toBe('1.0.0');
expect(eventsFunctionsExtension.getName()).toBe('ExtensionName');
// Verify that tags were properly converted to an array
expect(eventsFunctionsExtension.getTags().toJSArray()).toEqual([
'12',
'34',
'56',
'789',
]);
project.delete();
});
});
describe('gd.EventsBasedBehavior', () => {
it('can have a name, fullname, description', function () {
const eventsBasedBehavior = new gd.EventsBasedBehavior();
eventsBasedBehavior.setName('My name');
eventsBasedBehavior.setFullName('My descriptive name');
eventsBasedBehavior.setDescription('My description');
expect(eventsBasedBehavior.getName()).toBe('My name');
expect(eventsBasedBehavior.getFullName()).toBe('My descriptive name');
expect(eventsBasedBehavior.getDescription()).toBe('My description');
});
});
describe('gd.InstructionMetadata', () => {
it('can have parameters', () => {
const instructionMetadata = new gd.InstructionMetadata();
expect(instructionMetadata.getParametersCount()).toBe(0);
instructionMetadata.addParameter(
'type',
'label',
'AdditionalStuffThatWillBeReplaced',
false
);
instructionMetadata.setParameterLongDescription('Blabla');
instructionMetadata.setParameterExtraInfo(
'AdditionalStuffLikeTypicallyAnObjectOrBehaviorType'
);
expect(instructionMetadata.getParametersCount()).toBe(1);
expect(instructionMetadata.getParameter(0).getType()).toBe('type');
expect(instructionMetadata.getParameter(0).getDescription()).toBe(
'label'
);
expect(instructionMetadata.getParameter(0).getExtraInfo()).toBe(
'AdditionalStuffLikeTypicallyAnObjectOrBehaviorType'
);
expect(instructionMetadata.getParameter(0).getLongDescription()).toBe(
'Blabla'
);
});
});
describe('gd.ExpressionMetadata', () => {
it('can have parameters', () => {
const expressionMetadata = new gd.ExpressionMetadata(
'number',
'extensionNamespace',
'name',
'fullname',
'description',
'group',
'smallicon'
);
expect(expressionMetadata.getReturnType()).toBe('number');
expect(expressionMetadata.getFullName()).toBe('fullname');
expect(expressionMetadata.getDescription()).toBe('description');
expect(expressionMetadata.getGroup()).toBe('group');
expect(expressionMetadata.getSmallIconFilename()).toBe('smallicon');
expect(expressionMetadata.isShown()).toBe(true);
expect(expressionMetadata.getParametersCount()).toBe(0);
expressionMetadata.addParameter('type', 'label', '', false);
expressionMetadata.setParameterLongDescription('Blabla');
expect(expressionMetadata.getParametersCount()).toBe(1);
expect(expressionMetadata.getParameter(0).getType()).toBe('type');
expect(expressionMetadata.getParameter(0).getDescription()).toBe('label');
expect(expressionMetadata.getParameter(0).getLongDescription()).toBe(
'Blabla'
);
});
});
describe('gd.EditorSettings', () => {
it('can store anything', () => {
const element = gd.Serializer.fromJSObject({
test: 1,
anything: {
canBeStored: true,
},
});
const editorSettings = new gd.EditorSettings();
editorSettings.unserializeFrom(element);
const element2 = new gd.SerializerElement();
editorSettings.serializeTo(element2);
expect(element2.getChild('test').getIntValue()).toBe(1);
expect(
element2.getChild('anything').getChild('canBeStored').getBoolValue()
).toBe(true);
});
});
describe('gd.ObjectFolderOrObject (using gd.ObjectsContainer)', () => {
let project = null;
let layout = null;
beforeAll(() => {
project = gd.ProjectHelper.createNewGDJSProject();
});
afterEach(() => {
project.removeLayout('Scene');
});
beforeEach(() => {
layout = project.insertNewLayout('Scene', 0);
});
test('objects container has a root ObjectFolderOrObject', () => {
const rootFolder = layout.getRootFolder();
expect(rootFolder.isFolder()).toBe(true);
expect(rootFolder.isRootFolder()).toBe(true);
expect(rootFolder.getParent().isFolder()).toBe(true);
expect(rootFolder.getParent().getFolderName()).toEqual('__NULL');
expect(rootFolder.getChildrenCount()).toEqual(0);
});
test('an object added to the object container is added to the root ObjectFolderOrObject', () => {
let object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
const rootFolder = layout.getRootFolder();
expect(rootFolder.hasObjectNamed('MyObject')).toBe(true);
expect(rootFolder.isRootFolder()).toBe(true);
expect(rootFolder.getChildrenCount()).toEqual(1);
layout.removeObject('MyObject');
expect(rootFolder.hasObjectNamed('MyObject')).toBe(false);
expect(rootFolder.getChildrenCount()).toEqual(0);
});
test('a folder can be added to the root folder', () => {
const rootFolder = layout.getRootFolder();
const subFolder = rootFolder.insertNewFolder('Enemies', 1);
expect(subFolder.getFolderName()).toEqual('Enemies');
expect(subFolder.isRootFolder()).toBe(false);
subFolder.setFolderName('Players');
expect(subFolder.getFolderName()).toEqual('Players');
expect(subFolder.getParent()).toBe(rootFolder);
expect(rootFolder.getChildrenCount()).toEqual(1);
});
test('an object can be added to a specific folder', () => {
const rootFolder = layout.getRootFolder();
const subFolder = rootFolder.insertNewFolder('Enemies', 0);
const subSubFolder = subFolder.insertNewFolder('Turtles', 0);
layout.insertNewObjectInFolder(
project,
'Sprite',
'RedTurtle',
subSubFolder,
0
);
expect(layout.hasObjectNamed('RedTurtle')).toBe(true);
expect(subSubFolder.hasObjectNamed('RedTurtle')).toBe(true);
});
test('an ObjectFolderOrObject can be serialized and unserialized', () => {
const rootFolder = layout.getRootFolder();
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
const subFolder = rootFolder.insertNewFolder('Enemies', 1);
const object2 = layout.insertNewObject(
project,
'Sprite',
'OtherObject',
1
);
const object3 = layout.insertNewObject(project, 'Sprite', 'SubObject', 2);
rootFolder.moveObjectFolderOrObjectToAnotherFolder(
rootFolder.getObjectChild('SubObject'),
subFolder,
0
);
expect(rootFolder.hasObjectNamed('MyObject')).toBe(true);
expect(rootFolder.hasObjectNamed('OtherObject')).toBe(true);
expect(rootFolder.getChildrenCount()).toEqual(3);
expect(
rootFolder.getChildPosition(rootFolder.getObjectChild('MyObject'))
).toEqual(0);
expect(rootFolder.getChildPosition(subFolder)).toEqual(1);
expect(
rootFolder.getChildPosition(rootFolder.getObjectChild('OtherObject'))
).toEqual(2);
expect(rootFolder.hasObjectNamed('SubObject')).toBe(true);
expect(subFolder.hasObjectNamed('SubObject')).toBe(true);
const element = new gd.SerializerElement();
layout.serializeTo(element);
project.removeLayout('Scene');
const layout2 = project.insertNewLayout('Scene2', 0);
layout2.unserializeFrom(project, element);
expect(layout2.hasObjectNamed('MyObject')).toBe(true);
expect(layout2.hasObjectNamed('OtherObject')).toBe(true);
const rootFolder2 = layout.getRootFolder();
expect(rootFolder2.hasObjectNamed('MyObject')).toBe(true);
expect(rootFolder2.hasObjectNamed('OtherObject')).toBe(true);
expect(rootFolder2.getChildrenCount()).toEqual(3);
const parentEqualities = mapFor(
0,
rootFolder2.getChildrenCount(),
(i) => {
const childObjectFolderOrObject = rootFolder2.getChildAt(i);
return childObjectFolderOrObject.getParent() === rootFolder2;
}
);
expect(parentEqualities.every((equality) => equality)).toBe(true);
const subFolder2 = rootFolder2.getChildAt(1);
expect(subFolder2.isFolder()).toBe(true);
const subObject = subFolder2.getObjectChild('SubObject');
expect(subObject.getParent()).toBe(subFolder2);
});
test('an ObjectFolderOrObject can be serialized and unserialized and missing object folders or objects are added', () => {
const rootFolder = layout.getRootFolder();
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
const subFolder = rootFolder.insertNewFolder('Enemies', 1);
const object2 = layout.insertNewObject(
project,
'Sprite',
'OtherObject',
1
);
const object3 = layout.insertNewObject(project, 'Sprite', 'SubObject', 2);
rootFolder.moveObjectFolderOrObjectToAnotherFolder(
rootFolder.getObjectChild('SubObject'),
subFolder,
0
);
expect(rootFolder.hasObjectNamed('MyObject')).toBe(true);
expect(rootFolder.hasObjectNamed('OtherObject')).toBe(true);
expect(rootFolder.getChildrenCount()).toEqual(3);
expect(rootFolder.hasObjectNamed('SubObject')).toBe(true);
expect(subFolder.hasObjectNamed('SubObject')).toBe(true);
const element = new gd.SerializerElement();
layout.serializeTo(element);
const layoutObject = JSON.parse(gd.Serializer.toJSON(element));
delete layoutObject.objectsFolderStructure;
project.removeLayout('Scene');
const layout2 = project.insertNewLayout('Scene2', 0);
layout2.unserializeFrom(
project,
gd.Serializer.fromJSObject(layoutObject)
);
expect(layout2.hasObjectNamed('MyObject')).toBe(true);
expect(layout2.hasObjectNamed('OtherObject')).toBe(true);
const rootFolder2 = layout.getRootFolder();
expect(rootFolder2.hasObjectNamed('MyObject')).toBe(true);
expect(rootFolder2.hasObjectNamed('OtherObject')).toBe(true);
expect(rootFolder2.getChildrenCount()).toEqual(3);
const parentEqualities = mapFor(
0,
rootFolder2.getChildrenCount(),
(i) => {
const childObjectFolderOrObject = rootFolder2.getChildAt(i);
return childObjectFolderOrObject.getParent() === rootFolder2;
}
);
expect(parentEqualities.every((equality) => equality)).toBe(true);
});
test('a folder can be removed from its parent if empty', () => {
const rootFolder = layout.getRootFolder();
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
let subFolder = rootFolder.insertNewFolder('Enemies', 1);
const object2 = layout.insertNewObject(
project,
'Sprite',
'OtherObject',
2
);
rootFolder.moveObjectFolderOrObjectToAnotherFolder(
rootFolder.getObjectChild('OtherObject'),
subFolder,
0
);
rootFolder.removeFolderChild(subFolder);
// Check subfolder is still here since it was not empty.
expect(rootFolder.getChildrenCount()).toEqual(2);
subFolder = rootFolder.getChildAt(1);
expect(subFolder.isFolder()).toBe(true);
expect(subFolder.getChildrenCount()).toEqual(1);
expect(subFolder.hasObjectNamed('OtherObject')).toBe(true);
// Empty subfolder and remove it.
subFolder.moveObjectFolderOrObjectToAnotherFolder(
subFolder.getObjectChild('OtherObject'),
rootFolder,
0
);
rootFolder.removeFolderChild(subFolder);
expect(rootFolder.getChildrenCount()).toEqual(2);
const objectFolderOrObject = rootFolder.getChildAt(1);
const otherObjectFolderOrObject = rootFolder.getChildAt(0);
expect(otherObjectFolderOrObject.isFolder()).toBe(false);
expect(otherObjectFolderOrObject.isRootFolder()).toBe(false);
expect(otherObjectFolderOrObject.getObject().getName()).toBe(
'OtherObject'
);
expect(objectFolderOrObject.isFolder()).toBe(false);
expect(objectFolderOrObject.isRootFolder()).toBe(false);
expect(objectFolderOrObject.getObject().getName()).toBe('MyObject');
});
test("an ObjectFolderOrObject can test if it's a descendant of another one", () => {
const rootFolder = layout.getRootFolder();
const subFolder = rootFolder.insertNewFolder('Depth1', 0);
const subSubFolder = subFolder.insertNewFolder('Depth2', 0);
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
rootFolder.moveObjectFolderOrObjectToAnotherFolder(
rootFolder.getObjectChild('MyObject'),
subSubFolder,
0
);
const objectFolderOrObject = subSubFolder.getChildAt(0);
expect(objectFolderOrObject.isFolder()).toBe(false);
expect(objectFolderOrObject.getObject().getName()).toEqual('MyObject');
expect(objectFolderOrObject.isADescendantOf(subSubFolder)).toBe(true);
expect(objectFolderOrObject.isADescendantOf(subFolder)).toBe(true);
expect(objectFolderOrObject.isADescendantOf(rootFolder)).toBe(true);
expect(subSubFolder.isADescendantOf(subFolder)).toBe(true);
expect(subSubFolder.isADescendantOf(rootFolder)).toBe(true);
expect(subFolder.isADescendantOf(rootFolder)).toBe(true);
expect(rootFolder.isADescendantOf(objectFolderOrObject)).toBe(false);
expect(rootFolder.isADescendantOf(subSubFolder)).toBe(false);
expect(rootFolder.isADescendantOf(subFolder)).toBe(false);
expect(rootFolder.isADescendantOf(rootFolder)).toBe(false);
expect(subFolder.isADescendantOf(objectFolderOrObject)).toBe(false);
expect(subFolder.isADescendantOf(subSubFolder)).toBe(false);
expect(subFolder.isADescendantOf(subFolder)).toBe(false);
expect(subSubFolder.isADescendantOf(objectFolderOrObject)).toBe(false);
expect(subSubFolder.isADescendantOf(subSubFolder)).toBe(false);
expect(objectFolderOrObject.isADescendantOf(objectFolderOrObject)).toBe(
false
);
});
test('an ObjectFolderOrObject representing an object can be retrieved using the object name only', () => {
const rootFolder = layout.getRootFolder();
const subFolder = rootFolder.insertNewFolder('Depth1', 0);
const subSubFolder = subFolder.insertNewFolder('Depth2', 0);
const object = layout.insertNewObject(project, 'Sprite', 'MyObject', 0);
rootFolder.moveObjectFolderOrObjectToAnotherFolder(
rootFolder.getObjectChild('MyObject'),
subSubFolder,
0
);
const objectFolderOrObject = subSubFolder.getChildAt(0);
expect(objectFolderOrObject.isRootFolder()).toBe(false);
const objectFolderOrObjectFoundByName = rootFolder.getObjectNamed(
'MyObject'
);
expect(objectFolderOrObjectFoundByName.isRootFolder()).toBe(false);
expect(objectFolderOrObjectFoundByName).toBe(objectFolderOrObject);
});
});
});