243 lines
7.5 KiB
JavaScript
243 lines
7.5 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
|
|
import { createContentSystem, createDefaultPlayerState } from '../js/content/index.js'
|
|
import { createSceneStore } from '../js/ui/scene-store.js'
|
|
|
|
test('scene store starts on the home selection page', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({ contentSystem, playerState })
|
|
|
|
assert.equal(sceneStore.getScene().type, 'home-select')
|
|
})
|
|
|
|
test('scene store opens level selection for unlocked city and returns back', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({ contentSystem, playerState })
|
|
|
|
const openedHome = sceneStore.openHomeTile('asia')
|
|
const opened = sceneStore.openCity('beijing')
|
|
|
|
assert.equal(openedHome.opened, true)
|
|
assert.equal(opened, true)
|
|
assert.equal(sceneStore.getScene().type, 'level-select')
|
|
assert.equal(sceneStore.getScene().cityId, 'beijing')
|
|
|
|
sceneStore.goBack()
|
|
|
|
assert.equal(sceneStore.getScene().type, 'city-select')
|
|
})
|
|
|
|
test('scene store opens gameplay scene from a valid level', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({ contentSystem, playerState })
|
|
|
|
sceneStore.openHomeTile('asia')
|
|
sceneStore.openCity('beijing')
|
|
const opened = sceneStore.openLevel('beijing', 1)
|
|
|
|
assert.equal(opened, true)
|
|
assert.equal(sceneStore.getScene().type, 'gameplay')
|
|
assert.equal(sceneStore.getScene().session.cityId, 'beijing')
|
|
assert.equal(sceneStore.getScene().session.levelId, 1)
|
|
})
|
|
|
|
test('scene store awards magnets, stamps, and unlocks the next city on completion', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({
|
|
contentSystem,
|
|
playerState,
|
|
now: () => new Date('2026-03-28T12:00:00.000Z').getTime(),
|
|
})
|
|
|
|
sceneStore.completeLevel({
|
|
cityId: 'beijing',
|
|
levelId: 1,
|
|
stars: 3,
|
|
})
|
|
|
|
assert.deepEqual(playerState.collectedMagnets, [
|
|
{
|
|
magnetId: 'magnet_beijing_1',
|
|
cityId: 'beijing',
|
|
levelId: 1,
|
|
acquiredDate: '2026-03-28T12:00:00.000Z',
|
|
},
|
|
])
|
|
|
|
for (let levelId = 2; levelId <= 6; levelId += 1) {
|
|
sceneStore.completeLevel({
|
|
cityId: 'beijing',
|
|
levelId,
|
|
stars: 3,
|
|
})
|
|
}
|
|
|
|
assert.equal(playerState.collectedCats.includes('beijing'), true)
|
|
assert.equal(playerState.passportStamps.includes('beijing'), true)
|
|
assert.equal(playerState.unlockedCities.includes('tokyo'), true)
|
|
assert.deepEqual(playerState.collectedStamps, [
|
|
{
|
|
stampId: 'stamp_beijing',
|
|
cityId: 'beijing',
|
|
acquiredDate: '2026-03-28T12:00:00.000Z',
|
|
},
|
|
])
|
|
})
|
|
|
|
test('scene store reports locked and placeholder home tiles without leaving the home scene', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({ contentSystem, playerState })
|
|
|
|
const locked = sceneStore.openHomeTile('europe')
|
|
const soon = sceneStore.openHomeTile('coming_soon')
|
|
|
|
assert.deepEqual(locked, { opened: false, reason: 'locked' })
|
|
assert.deepEqual(soon, { opened: false, reason: 'coming-soon' })
|
|
assert.equal(sceneStore.getScene().type, 'home-select')
|
|
})
|
|
|
|
test('scene store opens the gift zone and switches between MVP tabs', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({ contentSystem, playerState })
|
|
|
|
const opened = sceneStore.openGiftZone()
|
|
|
|
assert.equal(opened, true)
|
|
assert.equal(sceneStore.getScene().type, 'gift-zone')
|
|
assert.equal(sceneStore.getScene().selectedTab, 'magnets')
|
|
|
|
const switched = sceneStore.selectGiftTab('stamps')
|
|
|
|
assert.equal(switched, true)
|
|
assert.equal(sceneStore.getScene().selectedTab, 'stamps')
|
|
|
|
const switchedToCats = sceneStore.selectGiftTab('cats')
|
|
|
|
assert.equal(switchedToCats, true)
|
|
assert.equal(sceneStore.getScene().selectedTab, 'cats')
|
|
|
|
sceneStore.goBack()
|
|
|
|
assert.equal(sceneStore.getScene().type, 'home-select')
|
|
})
|
|
|
|
test('scene store opens city team selection and locks the first chosen team', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({
|
|
contentSystem,
|
|
playerState,
|
|
now: () => new Date('2026-03-29T08:00:00.000Z').getTime(),
|
|
})
|
|
|
|
const opened = sceneStore.openCityTeamSelect()
|
|
|
|
assert.equal(opened, true)
|
|
assert.equal(sceneStore.getScene().type, 'city-team-select')
|
|
assert.deepEqual(sceneStore.getScene().options.map((entry) => entry.cityId), ['beijing'])
|
|
|
|
const selected = sceneStore.chooseCityTeam('beijing')
|
|
|
|
assert.equal(selected, true)
|
|
assert.deepEqual(playerState.cityTeam, {
|
|
teamCityId: 'beijing',
|
|
joinedDate: '2026-03-29T08:00:00.000Z',
|
|
lastSwitchDate: null,
|
|
})
|
|
assert.equal(sceneStore.getScene().type, 'home-select')
|
|
|
|
sceneStore.openCityTeamSelect()
|
|
const reselected = sceneStore.chooseCityTeam('beijing')
|
|
|
|
assert.equal(reselected, false)
|
|
assert.equal(playerState.cityTeam.teamCityId, 'beijing')
|
|
})
|
|
|
|
test('scene store opens mashup gameplay after two completed cities', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({
|
|
contentSystem,
|
|
playerState,
|
|
now: () => 33001,
|
|
})
|
|
|
|
playerState.unlockedCities.push('tokyo')
|
|
playerState.levelProgress.beijing = {
|
|
1: { completed: true, stars: 3 },
|
|
2: { completed: true, stars: 3 },
|
|
3: { completed: true, stars: 3 },
|
|
4: { completed: true, stars: 3 },
|
|
5: { completed: true, stars: 3 },
|
|
6: { completed: true, stars: 3 },
|
|
}
|
|
playerState.levelProgress.tokyo = {
|
|
1: { completed: true, stars: 3 },
|
|
2: { completed: true, stars: 3 },
|
|
3: { completed: true, stars: 3 },
|
|
4: { completed: true, stars: 3 },
|
|
5: { completed: true, stars: 3 },
|
|
6: { completed: true, stars: 3 },
|
|
}
|
|
|
|
const opened = sceneStore.openHomeTile('mashup')
|
|
|
|
assert.deepEqual(opened, { opened: true })
|
|
assert.equal(sceneStore.getScene().type, 'gameplay')
|
|
assert.equal(sceneStore.getScene().mode, 'mashup')
|
|
assert.deepEqual(sceneStore.getScene().sourceCityIds, ['beijing', 'tokyo'])
|
|
})
|
|
|
|
test('scene store awards a deterministic local mashup reward', () => {
|
|
const contentSystem = createContentSystem()
|
|
const playerState = createDefaultPlayerState()
|
|
const sceneStore = createSceneStore({
|
|
contentSystem,
|
|
playerState,
|
|
now: () => 33001,
|
|
})
|
|
|
|
playerState.unlockedCities.push('tokyo')
|
|
playerState.levelProgress.beijing = {
|
|
1: { completed: true, stars: 3 },
|
|
2: { completed: true, stars: 3 },
|
|
3: { completed: true, stars: 3 },
|
|
4: { completed: true, stars: 3 },
|
|
5: { completed: true, stars: 3 },
|
|
6: { completed: true, stars: 3 },
|
|
}
|
|
playerState.levelProgress.tokyo = {
|
|
1: { completed: true, stars: 3 },
|
|
2: { completed: true, stars: 3 },
|
|
3: { completed: true, stars: 3 },
|
|
4: { completed: true, stars: 3 },
|
|
5: { completed: true, stars: 3 },
|
|
6: { completed: true, stars: 3 },
|
|
}
|
|
|
|
sceneStore.openHomeTile('mashup')
|
|
const reward = sceneStore.completeGameplayVictory()
|
|
|
|
assert.deepEqual(reward, {
|
|
type: 'magnet',
|
|
magnetId: 'magnet_beijing_2',
|
|
cityId: 'beijing',
|
|
levelId: 2,
|
|
})
|
|
assert.deepEqual(playerState.collectedMagnets, [
|
|
{
|
|
magnetId: 'magnet_beijing_2',
|
|
cityId: 'beijing',
|
|
levelId: 2,
|
|
acquiredDate: '1970-01-01T00:00:33.001Z',
|
|
},
|
|
])
|
|
})
|