add tests on import / export settings

This commit is contained in:
prateau 2024-10-06 14:28:30 +02:00
parent 6a0605c781
commit 9312545deb
2 changed files with 132 additions and 64 deletions

View File

@ -1353,6 +1353,26 @@ export class GameData {
});
}
public getDataToExport(dataType: GameDataType, slotId: integer = 0): Promise<string | null> {
return new Promise<string | null>(resolve => {
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
Utils.apiFetch(`savedata/${dataType === GameDataType.SYSTEM ? "system" : "session"}/get?clientSessionId=${clientSessionId}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ""}`, true)
.then(response => response.text())
.then(response => {
if (!response.length || response[0] !== "{") {
console.error(response);
resolve(null);
}
resolve(response);
});
} else {
const dataKey: string = getDataTypeKey(dataType, slotId, loggedInUser?.username);
const data = localStorage.getItem(dataKey);
resolve((!data || dataType === GameDataType.SETTINGS) ? data : decrypt(data, bypassLogin));
}
});
}
public tryExportData(dataType: GameDataType, slotId: integer = 0): Promise<boolean> {
return new Promise<boolean>(resolve => {
const dataKey: string = getDataTypeKey(dataType, slotId, loggedInUser?.username);
@ -1370,33 +1390,56 @@ export class GameData {
link.click();
link.remove();
};
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
Utils.apiFetch(`savedata/${dataType === GameDataType.SYSTEM ? "system" : "session"}/get?clientSessionId=${clientSessionId}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ""}`, true)
.then(response => response.text())
.then(response => {
if (!response.length || response[0] !== "{") {
console.error(response);
resolve(false);
return;
}
handleData(response);
resolve(true);
});
} else {
const data = localStorage.getItem(dataKey);
if (data) {
const decryptedData = (dataType === GameDataType.SETTINGS) ? data : decrypt(data, bypassLogin);
handleData(decryptedData);
}
resolve(!!data);
}
this.getDataToExport(dataType, slotId)
.then(data => {
if (data) {
handleData(data);
}
resolve(!!data);
});
});
}
public importData(dataType: GameDataType, slotId: integer = 0): void {
const dataKey = getDataTypeKey(dataType, slotId, loggedInUser?.username);
public validateDataToImport(dataStr: string, dataType: GameDataType): boolean {
try {
switch (dataType) {
case GameDataType.SYSTEM:
dataStr = this.convertSystemDataStr(dataStr);
const systemData = this.parseSystemData(dataStr);
return !!systemData.dexData && !!systemData.timestamp;
case GameDataType.SESSION:
const sessionData = this.parseSessionData(dataStr);
return !!sessionData.party && !!sessionData.enemyParty && !!sessionData.timestamp;
case GameDataType.RUN_HISTORY:
const data = JSON.parse(dataStr);
const keys = Object.keys(data);
return keys.every((key) => {
const entryKeys = Object.keys(data[key]);
return [ "isFavorite", "isVictory", "entry" ].every(v => entryKeys.includes(v)) && entryKeys.length === 3;
});
case GameDataType.SETTINGS:
return Object.entries(JSON.parse(dataStr))
.every(([ k, v ]: [string, number]) => {
const index: number = settingIndex(k);
return index === -1 || Setting[index].options.length > v;
});
case GameDataType.TUTORIALS:
case GameDataType.SEEN_DIALOGUES:
return true;
}
} catch (ex) {
console.error(ex);
return false;
}
}
public setImportedData(dataStr: string, dataType: GameDataType, slotId: integer = 0) {
const dataKey = getDataTypeKey(dataType, slotId, loggedInUser?.username);
const encryptedData = (dataType === GameDataType.SETTINGS) ? dataStr : encrypt(dataStr, bypassLogin);
localStorage.setItem(dataKey, encryptedData);
}
public importData(dataType: GameDataType, slotId: integer = 0): void {
let saveFile: any = document.getElementById("saveFile");
if (saveFile) {
saveFile.remove();
@ -1413,47 +1456,13 @@ export class GameData {
reader.onload = (_ => {
return e => {
let dataName: string;
let dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct?
let valid = false;
try {
dataName = GameDataType[dataType].toLowerCase();
switch (dataType) {
case GameDataType.SYSTEM:
dataStr = this.convertSystemDataStr(dataStr);
const systemData = this.parseSystemData(dataStr);
valid = !!systemData.dexData && !!systemData.timestamp;
break;
case GameDataType.SESSION:
const sessionData = this.parseSessionData(dataStr);
valid = !!sessionData.party && !!sessionData.enemyParty && !!sessionData.timestamp;
break;
case GameDataType.RUN_HISTORY:
const data = JSON.parse(dataStr);
const keys = Object.keys(data);
dataName = i18next.t("menuUiHandler:RUN_HISTORY").toLowerCase();
keys.forEach((key) => {
const entryKeys = Object.keys(data[key]);
valid = [ "isFavorite", "isVictory", "entry" ].every(v => entryKeys.includes(v)) && entryKeys.length === 3;
});
break;
case GameDataType.SETTINGS:
valid = Object.entries(JSON.parse(dataStr))
.every(([ k, v ]: [string, number]) => {
const index: number = settingIndex(k);
return index === -1 || Setting[index].options.length > v;
});
break;
case GameDataType.TUTORIALS:
valid = true;
break;
}
} catch (ex) {
console.error(ex);
}
const dataName = (dataType === GameDataType.RUN_HISTORY)
? i18next.t("menuUiHandler:RUN_HISTORY").toLowerCase()
: GameDataType[dataType].toLowerCase();
const dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct?
const valid = this.validateDataToImport(dataStr, dataType);
const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500));
dataName = dataName!; // tell TS compiler that dataName is defined!
if (!valid) {
return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500));
@ -1461,8 +1470,7 @@ export class GameData {
this.scene.ui.showText(`Your ${dataName} data will be overridden and the page will reload. Proceed?`, null, () => {
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
const encryptedData = (dataType === GameDataType.SETTINGS) ? dataStr : encrypt(dataStr, bypassLogin);
localStorage.setItem(dataKey, encryptedData);
this.setImportedData(dataStr, dataType, slotId);
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
updateUserInfo().then(success => {

View File

@ -1,6 +1,7 @@
import * as BattleScene from "#app/battle-scene";
import { SessionSaveData } from "#app/system/game-data";
import { Abilities } from "#enums/abilities";
import { GameDataType } from "#enums/game-data-type";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import { http, HttpResponse } from "msw";
@ -86,4 +87,63 @@ describe("System - Game Data", () => {
expect(account.updateUserInfo).toHaveBeenCalled();
});
});
describe("getDataToExport", () => {
it("should get default settings", async () => {
const defaultSettings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
localStorage.setItem("settings", defaultSettings);
const result = await game.scene.gameData.getDataToExport(GameDataType.SETTINGS);
expect(result).toEqual(defaultSettings);
});
it("should get undefined when there is no settings", async () => {
const result = await game.scene.gameData.getDataToExport(GameDataType.SETTINGS);
expect(result).toBeUndefined();
});
});
describe("setImportedData", () => {
it("should set settings in local storage", () => {
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
game.scene.gameData.setImportedData(settings, GameDataType.SETTINGS);
expect(localStorage.getItem("settings")).toEqual(settings);
});
it("should override default settings", () => {
const defaultSettings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
localStorage.setItem("settings", defaultSettings);
const newSettings = "{\"PLAYER_GENDER\":1,\"gameVersion\":\"1.0.7\",\"GAME_SPEED\":7}";
game.scene.gameData.setImportedData(newSettings, GameDataType.SETTINGS);
expect(localStorage.getItem("settings")).toEqual(newSettings);
});
});
describe("validateDataToImport", () => {
it("should be true when the setting data is valid", async () => {
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
expect(result).toBeTruthy();
});
it("should be false when the setting data is an invalid JSON", async () => {
const settings = "";
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
expect(result).toBeFalsy();
});
it("should be false when the setting data contains an unknow value", async () => {
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\",\"GAME_SPEED\":999}";
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
expect(result).toBeFalsy();
});
});
});