Fix exported WebManifest file having a syntax error when the game description has line breaks

Fix #4180
This commit is contained in:
Florian Rival
2022-08-05 09:13:23 +02:00
parent 1d77f2da23
commit bdbc665d2e
2 changed files with 92 additions and 14 deletions

View File

@@ -929,7 +929,7 @@ const gd::String ExporterHelper::GenerateWebManifest(
icons += icons +=
gd::String(R"({ gd::String(R"({
"src": "{FILE}", "src": "{FILE}",
"sizes": "{SIZE}x{SIZE}" "sizes": "{SIZE}x{SIZE}"
},)") },)")
.FindAndReplace("{SIZE}", gd::String::From(sizeAndFile.first)) .FindAndReplace("{SIZE}", gd::String::From(sizeAndFile.first))
.FindAndReplace("{FILE}", sizeAndFile.second); .FindAndReplace("{FILE}", sizeAndFile.second);
@@ -938,11 +938,18 @@ const gd::String ExporterHelper::GenerateWebManifest(
icons = icons.RightTrim(",") + "]"; icons = icons.RightTrim(",") + "]";
gd::String jsonName =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetName()));
gd::String jsonPackageName =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetPackageName()));
gd::String jsonDescription =
gd::Serializer::ToJSON(gd::SerializerElement(project.GetDescription()));
return gd::String(R"webmanifest({ return gd::String(R"webmanifest({
"name": "{NAME}", "name": {NAME},
"short_name": "{NAME}", "short_name": {NAME},
"id": "{PACKAGE_ID}", "id": {PACKAGE_ID},
"description": "{DESCRIPTION}", "description": {DESCRIPTION},
"orientation": "{ORIENTATION}", "orientation": "{ORIENTATION}",
"start_url": "./index.html", "start_url": "./index.html",
"display": "standalone", "display": "standalone",
@@ -950,9 +957,9 @@ const gd::String ExporterHelper::GenerateWebManifest(
"categories": ["games", "entertainment"], "categories": ["games", "entertainment"],
"icons": {ICONS} "icons": {ICONS}
})webmanifest") })webmanifest")
.FindAndReplace("{NAME}", project.GetName()) .FindAndReplace("{NAME}", jsonName)
.FindAndReplace("{PACKAGE_ID}", project.GetPackageName()) .FindAndReplace("{PACKAGE_ID}", jsonPackageName)
.FindAndReplace("{DESCRIPTION}", project.GetDescription()) .FindAndReplace("{DESCRIPTION}", jsonDescription)
.FindAndReplace("{ORIENTATION}", .FindAndReplace("{ORIENTATION}",
orientation == "default" ? "any" : orientation) orientation == "default" ? "any" : orientation)
.FindAndReplace("{ICONS}", icons); .FindAndReplace("{ICONS}", icons);

View File

@@ -17,7 +17,17 @@ describe('libGD.js - GDJS related tests', function () {
describe('Exporter', () => { describe('Exporter', () => {
// Fake files to simulate a file system (see tests below). // Fake files to simulate a file system (see tests below).
const fakeIndexHtmlContent = ''; const fakeIndexHtmlContent = `<html>
<head>
<style>
/* GDJS_CUSTOM_STYLE */
</style>
<!-- GDJS_CODE_FILES -->
<body>
<!-- GDJS_CUSTOM_HTML -->
</body>
</head>
</html>`;
const fakeConfigXmlContent = ` const fakeConfigXmlContent = `
<widget id="GDJS_PACKAGENAME" version="GDJS_PROJECTVERSION"> <widget id="GDJS_PACKAGENAME" version="GDJS_PROJECTVERSION">
<name>GDJS_PROJECTNAME</name> <name>GDJS_PROJECTNAME</name>
@@ -38,6 +48,56 @@ describe('libGD.js - GDJS related tests', function () {
"author": "GDJS_GAME_AUTHOR" "author": "GDJS_GAME_AUTHOR"
}`; }`;
it('properly exports HTML5 files', () => {
// Create a simple project
const project = gd.ProjectHelper.createNewGDJSProject();
project.setName('My great project with spaces and "quotes"!');
project.setDescription(
'A nice project\nwith line breaks in the description.'
);
project.setVersion('1.2.3');
// Prepare a fake file system
var fs = makeFakeAbstractFileSystem(gd, {
'/fake-gdjs-root/Runtime/index.html': fakeIndexHtmlContent,
});
// Export and check the content of written files.
const exporter = new gd.Exporter(fs, '/fake-gdjs-root');
const exportOptions = new gd.MapStringBoolean();
expect(
exporter.exportWholePixiProject(
project,
'/fake-export-dir',
exportOptions
)
).toBe(true);
exportOptions.delete();
exporter.delete();
// Check the index.html contains the include files.
expect(fs.writeToFile.mock.calls[2][0]).toBe('/fake-export-dir/index.html');
expect(fs.writeToFile.mock.calls[2][1]).toContain('gd.js');
expect(fs.writeToFile.mock.calls[2][1]).toContain('affinetransformation.js');
expect(fs.writeToFile.mock.calls[2][1]).toContain('pixi-renderers/runtimescene-pixi-renderer.js');
expect(fs.writeToFile.mock.calls[2][1]).toContain('data.js');
// Check the webmanifest was properly generated.
expect(fs.writeToFile.mock.calls[1][0]).toBe('/fake-export-dir/manifest.webmanifest');
expect(() => JSON.parse(fs.writeToFile.mock.calls[1][1])).not.toThrow();
expect(JSON.parse(fs.writeToFile.mock.calls[1][1])).toEqual({
"name": "My great project with spaces and \"quotes\"!",
"short_name": "My great project with spaces and \"quotes\"!",
"id": "com.example.gamename",
"description": "A nice project\nwith line breaks in the description.",
"orientation": "landscape",
"start_url": "./index.html",
"display": "standalone",
"background_color": "black",
"categories": ["games", "entertainment"],
"icons": []
});
});
it('properly exports Cordova files', () => { it('properly exports Cordova files', () => {
// Create a simple project // Create a simple project
const project = gd.ProjectHelper.createNewGDJSProject(); const project = gd.ProjectHelper.createNewGDJSProject();
@@ -242,10 +302,15 @@ describe('libGD.js - GDJS related tests', function () {
const unsupportedCapabilityAction = new gd.Instruction(); const unsupportedCapabilityAction = new gd.Instruction();
unsupportedCapabilityAction.setType('EnableEffect'); unsupportedCapabilityAction.setType('EnableEffect');
unsupportedCapabilityAction.setParametersCount(3); unsupportedCapabilityAction.setParametersCount(3);
unsupportedCapabilityAction.setParameter(0, 'MyFakeObjectWithUnsupportedCapability'); unsupportedCapabilityAction.setParameter(
0,
'MyFakeObjectWithUnsupportedCapability'
);
unsupportedCapabilityAction.setParameter(1, '"MyEffect"'); unsupportedCapabilityAction.setParameter(1, '"MyEffect"');
unsupportedCapabilityAction.setParameter(2, 'yes'); unsupportedCapabilityAction.setParameter(2, 'yes');
gd.asStandardEvent(evt).getActions().insert(unsupportedCapabilityAction, 4); gd.asStandardEvent(evt)
.getActions()
.insert(unsupportedCapabilityAction, 4);
const layoutCodeGenerator = new gd.LayoutCodeGenerator(project); const layoutCodeGenerator = new gd.LayoutCodeGenerator(project);
const code = layoutCodeGenerator.generateLayoutCompleteCode( const code = layoutCodeGenerator.generateLayoutCompleteCode(
@@ -269,7 +334,9 @@ describe('libGD.js - GDJS related tests', function () {
expect(code).toMatch('/* Unknown instruction - skipped. */'); expect(code).toMatch('/* Unknown instruction - skipped. */');
// Check that the action for an object not having the required capability was not generated. // Check that the action for an object not having the required capability was not generated.
expect(code).toMatch('/* Object with unsupported capability - skipped. */'); expect(code).toMatch(
'/* Object with unsupported capability - skipped. */'
);
action.delete(); action.delete();
}); });
@@ -306,7 +373,9 @@ describe('libGD.js - GDJS related tests', function () {
unsupportedCapabilityAction.setParameter(0, 'MyGroup'); unsupportedCapabilityAction.setParameter(0, 'MyGroup');
unsupportedCapabilityAction.setParameter(1, '"MyEffect"'); unsupportedCapabilityAction.setParameter(1, '"MyEffect"');
unsupportedCapabilityAction.setParameter(2, 'yes'); unsupportedCapabilityAction.setParameter(2, 'yes');
gd.asStandardEvent(evt).getActions().insert(unsupportedCapabilityAction, 1); gd.asStandardEvent(evt)
.getActions()
.insert(unsupportedCapabilityAction, 1);
const layoutCodeGenerator = new gd.LayoutCodeGenerator(project); const layoutCodeGenerator = new gd.LayoutCodeGenerator(project);
const code = layoutCodeGenerator.generateLayoutCompleteCode( const code = layoutCodeGenerator.generateLayoutCompleteCode(
@@ -329,7 +398,9 @@ describe('libGD.js - GDJS related tests', function () {
expect(code).toMatch( expect(code).toMatch(
'gdjs.SceneCode.GDMySpriteObjects1[i].enableEffect("MyEffect", true);' 'gdjs.SceneCode.GDMySpriteObjects1[i].enableEffect("MyEffect", true);'
); );
expect(code).toMatch('/* Object with unsupported capability - skipped. */'); expect(code).toMatch(
'/* Object with unsupported capability - skipped. */'
);
action.delete(); action.delete();
}); });