import test from "node:test"; import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; test("client stores migration state and exposes all transfer actions", async () => { const [gameManager, gameConfig, utils, miniGameSdk] = await Promise.all([ readFile(new URL("../../../assets/Script/GameManager.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/module/Config/GameConfig.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/module/Pay/Utils.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/Sdk/MiniGameSdk.ts", import.meta.url), "utf8"), ]); assert.match(gameConfig, /wucai_type:\s*number/); assert.match(gameConfig, /wucai_type:\s*-1/); assert.match(gameConfig, /wucai_preview:\s*any/); assert.match(gameConfig, /wucai_reward_popup_pending:\s*boolean/); assert.match(gameManager, /GM_INFO\.wucai_type\s*=\s*Number\.isInteger/); assert.match(gameManager, /GM_INFO\.wucai_preview\s*=\s*data\.data\.wucai_preview \|\| null/); assert.match(gameManager, /GM_INFO\.wucai_reward_popup_pending\s*=/); assert.match(gameManager, /GM_INFO\.wucai_type === 1/); assert.match(utils, /"claim_reward"\s*\|\s*"choose_wucai"\s*\|\s*"choose_current"\s*\|\s*"ack_reward_popup"/); assert.match(utils, /Utils\.POST\("wucaiTransfer"/); assert.match(utils, /action === "claim_reward"[\s\S]*GM_INFO\.coin = coinAmount/); assert.match(utils, /StorageMessage\.setStorage\("coin"/); assert.match(utils, /StorageMessage\.setStorage\("prop"/); assert.match(utils, /action === "choose_wucai" \|\| action === "choose_current"[\s\S]*MiniGameSdk\.API\.reboot\(\)/); assert.match(miniGameSdk, /userSet\(\{ transferred_from_wucai: transferred \}\)/); }); test("Wucai transfer analytics report the final choice and both archive levels", async () => { const [gameManager, utils, miniGameSdk] = await Promise.all([ readFile(new URL("../../../assets/Script/GameManager.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/module/Pay/Utils.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/Sdk/MiniGameSdk.ts", import.meta.url), "utf8"), ]); assert.match(miniGameSdk, /shushu_Track\("transfer_from_wucai",\s*\{/); assert.match(miniGameSdk, /already_xiaoxiao:\s*alreadyXiaoxiao/); assert.match(miniGameSdk, /choose_xiaoxiao:\s*chooseXiaoxiao/); assert.match(miniGameSdk, /wucai_level:\s*toCurrentLevel\(wucaiLevelAmount\)/); assert.match(miniGameSdk, /xiaoxiao_level:\s*toCurrentLevel\(xiaoxiaoLevelAmount\)/); assert.match(miniGameSdk, /levelAmount !== null[\s\S]*level \+ 1[\s\S]*-1/); const updateIndex = gameManager.indexOf("update(dt)"); const loginIndex = gameManager.indexOf("this.loginShushuOnce();", updateIndex); const pendingChoiceIndex = gameManager.indexOf("GM_INFO.wucai_type === 1", updateIndex); assert.ok(updateIndex >= 0 && loginIndex > updateIndex && pendingChoiceIndex > loginIndex); assert.match(gameManager, /loginShushuOnce\(\)[\s\S]*shushu_Login\(\)/); assert.match(gameManager, /const transferred = wucaiType === 0 \|\| wucaiType === 2 \|\| wucaiType === 3/); assert.match(gameManager, /shushu_userSetTransferredFromWucai\(transferred\)/); assert.match(gameManager, /shushu_TrackWucaiTransfer\(0, 0, this\.autoWucaiLevelAmount, null\)/); assert.match(utils, /action === "choose_current" \? 1 : 0/); assert.match(utils, /res\.code === 1 && isProgressChoice && wasPendingChoice/); assert.match(utils, /shushu_TrackWucaiTransfer\([\s\S]*wucaiLevelAmount,[\s\S]*xiaoxiaoLevelAmount/); assert.match(utils, /shushu_userSetTransferredFromWucai\(true\)/); }); test("new-user registration is independent of local cache and does not wait for analytics login", async () => { const gameManager = await readFile( new URL("../../../assets/Script/GameManager.ts", import.meta.url), "utf8" ); assert.doesNotMatch(gameManager, /pendingRegisterEvent/); assert.match(gameManager, /if \(data\.data\.isFirst === true\)\s*\{[\s\S]*?this\.setFirstInfo\(\);\s*\}/); assert.equal((gameManager.match(/this\.setFirstInfo\(\);/g) || []).length, 1); const analyticsInitIndex = gameManager.indexOf("MiniGameSdk.API.shushu_Init();"); const loginRequestIndex = gameManager.indexOf("Utils.getUserInfo(", analyticsInitIndex); const cacheBranchIndex = gameManager.indexOf("this.getUserDataToLocal(data.data);", loginRequestIndex); const registerIndex = gameManager.indexOf("this.setFirstInfo();", cacheBranchIndex); const loginMethodIndex = gameManager.indexOf("private loginShushuOnce()"); assert.ok(analyticsInitIndex >= 0); assert.ok(loginRequestIndex > analyticsInitIndex); assert.ok(cacheBranchIndex > loginRequestIndex); assert.ok(registerIndex > cacheBranchIndex); assert.ok(loginMethodIndex > registerIndex); }); test("a completed progress choice shows the Home reward popup once after reboot", async () => { const [transfer, home, gameManager] = await Promise.all([ readFile(new URL("../../../assets/Transfer/script/Transfer.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/JiaZai.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/GameManager.ts", import.meta.url), "utf8"), ]); assert.match(gameManager, /wucai_reward_popup_pending\s*=\s*[\s\S]*data\.data\.wucai_reward_popup_pending === true/); assert.match(home, /wucaiType === 0[\s\S]*wucai_reward_popup_pending === true/); assert.match(transfer, /this\.submit\("ack_reward_popup"\)/); assert.match(transfer, /action === "claim_reward" \|\| action === "ack_reward_popup"[\s\S]*this\.node\.active = false/); assert.match(transfer, /action === "claim_reward"[\s\S]*home\.updateCoin\(\)/); }); test("Home automatic popups wait until the Wucai Transfer popup closes", async () => { const [transfer, home] = await Promise.all([ readFile(new URL("../../../assets/Transfer/script/Transfer.ts", import.meta.url), "utf8"), readFile(new URL("../../../assets/Script/JiaZai.ts", import.meta.url), "utf8"), ]); assert.match(home, /isWucaiTransferBlockingPopups\(\)/); assert.match(home, /deferHomePopupWhileTransfer\("order"/); assert.match(home, /deferHomePopupWhileTransfer\("monthlyReward"/); assert.match(home, /deferHomePopupWhileTransfer\("monthlyCard"/); assert.match(home, /deferHomePopupWhileTransfer\("starterPack"/); assert.match(home, /deferHomePopupWhileTransfer\("share"/); assert.match(home, /deferHomePopupWhileTransfer\("passCheck"/); assert.match(home, /"jungleTreasure"[\s\S]*initializeJungleTreasureEntryFromLogin/); assert.match(home, /resumeDeferredHomePopups\(\)/); assert.match(transfer, /this\.node\.active = false;[\s\S]*this\.notifyHomeTransferClosed\(\)/); assert.match(transfer, /home\.resumeDeferredHomePopups\(\)/); }); test("Home Transfer reward glows rotate continuously", async () => { const transfer = await readFile( new URL("../../../assets/Transfer/script/Transfer.ts", import.meta.url), "utf8" ); for (const nodeName of ["coin_guang", "hammer_guang", "magic_guang", "ice_guang"]) { assert.match(transfer, new RegExp(nodeName)); } assert.match(transfer, /cc\.rotateBy\(5, 360\)\.repeatForever\(\)/); assert.match(transfer, /this\.playHomePopupAnimation\(\)/); assert.match(transfer, /homeRewardContent\.scale = 0\.8/); assert.match(transfer, /scale: 1\.05[\s\S]*scale: 1/); }); test("forcedUpdate is acknowledged only from the completed load gate", async () => { const gameManager = await readFile( new URL("../../../assets/Script/GameManager.ts", import.meta.url), "utf8" ); const updateIndex = gameManager.indexOf("update(dt)"); const clearIndex = gameManager.indexOf("Utils.setForcedUpdate("); assert.ok(updateIndex >= 0); assert.ok(clearIndex > updateIndex); assert.match(gameManager, /data\.forcedUpdate === true[\s\S]*this\.applyForcedProgress\(data\)/); assert.match(gameManager, /data\.data\.forcedUpdate === true[\s\S]*effectivePowerTime/); assert.match(gameManager, /data\.data\.forcedUpdate === true[\s\S]*StorageMessage\.setStorage\("health"/); assert.match(gameManager, /winStreak !== undefined && data\.data\.winStreak !== null/); }); test("forcedUpdate backend requires a token and checks the update result", async () => { const source = await readFile( new URL("../functions/setForcedUpdate.ts", import.meta.url), "utf8" ); assert.match(source, /!res\.data \|\| !res\.data\.token \|\| !token1/); assert.match(source, /res1\.ok !== false && res1\.updated !== 0/); assert.doesNotMatch(source, /if \(res\.ok && res1\.updated/); });