UNPKG

@jeremyckahn/farmhand

Version:
258 lines (223 loc) 7.57 kB
import { testState } from '../../test-utils/index.js' import { toolLevel, toolType } from '../../enums.js' import { INFINITE_STORAGE_LIMIT } from '../../constants.js' import { chopForestPlot } from './chopForestPlot.js' vitest.mock('../../data/items.js') const toolLevelsWithAxe = (level: farmhand.toolLevel) => ({ [toolType.AXE]: level, [toolType.HOE]: toolLevel.DEFAULT, [toolType.SCYTHE]: toolLevel.DEFAULT, [toolType.SHOVEL]: toolLevel.DEFAULT, [toolType.WATERING_CAN]: toolLevel.DEFAULT, }) // sample-tree-1's treeTimeline is [1, 2] (GROWN from daysOld 3) with a // lifespan of 100 (DEAD from daysOld 103), and fruitTimeline is [1, 1] // (fruit GROWN from daysSinceLastHarvest 2). describe('chopForestPlot', () => { beforeEach(() => { vitest.spyOn(Math, 'random').mockReturnValue(0) }) describe('empty plot', () => { test('no-ops', () => { const inputState = testState({ forest: [[null]], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }) const state = chopForestPlot(inputState, 0, 0) expect(state).toEqual(inputState) }) }) describe('axe is unavailable', () => { test('no-ops', () => { const inputState = testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 2 }], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.UNAVAILABLE), }) const state = chopForestPlot(inputState, 0, 0) expect(state).toEqual(inputState) }) }) describe('non-tree plot content', () => { test('no-ops', () => { const inputState = testState({ forest: [[{ forageableId: 'mushroom', daysOld: 0 }]], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }) const state = chopForestPlot(inputState, 0, 0) expect(state).toEqual(inputState) }) }) describe('inventory is full', () => { test('no-ops', () => { const inputState = testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 2 }], ], inventory: [], inventoryLimit: 0, toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }) const state = chopForestPlot(inputState, 0, 0) expect(state).toEqual(inputState) }) }) describe('grown tree with ripe fruit', () => { test('harvests the fruit as a bonus, adds wood, and clears the plot', () => { const { forest, inventory } = chopForestPlot( testState({ forest: [ [ { itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 2, }, ], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }), 0, 0 ) expect(inventory).toEqual([ { id: 'sample-tree-1', quantity: 1 }, { id: 'wood', quantity: 1 }, ]) expect(forest[0][0]).toBeNull() }) }) describe('grown tree, fruit not yet ripe', () => { test('adds wood without any fruit, and clears the plot', () => { const { forest, inventory } = chopForestPlot( testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 0 }], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }), 0, 0 ) expect(inventory).toEqual([{ id: 'wood', quantity: 1 }]) expect(forest[0][0]).toBeNull() }) }) describe('immature tree', () => { test('yields a halved wood range and no fruit', () => { vitest.mocked(Math.random).mockReturnValue(0.999) const { forest, inventory } = chopForestPlot( testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 0, daysSinceLastHarvest: 0 }], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, // DEFAULT axe tier's full range is [1, 2], so an immature tree's // halved range is [1, 1]. toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }), 0, 0 ) expect(inventory).toEqual([{ id: 'wood', quantity: 1 }]) expect(forest[0][0]).toBeNull() }) }) describe('dead tree', () => { test('yields the full (not halved) wood range and no fruit bonus, even with ripe-looking fruit', () => { const { forest, inventory } = chopForestPlot( testState({ forest: [ [ { itemId: 'sample-tree-1', // Past growthDuration + lifespan (3 + 100 = 103) - dead. daysOld: 103, // Would read as ripe (GROWN) fruit if the tree were alive. daysSinceLastHarvest: 2, }, ], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, // DEFAULT axe tier's full range is [1, 2]; a dead tree gets the // full range, not the halved immature-tree range of [1, 1]. toolLevels: toolLevelsWithAxe(toolLevel.DEFAULT), }), 0, 0 ) expect(inventory).toEqual([{ id: 'wood', quantity: 1 }]) expect(forest[0][0]).toBeNull() }) test('rolls the full range up to its maximum, distinguishing it from a halved range', () => { vitest.mocked(Math.random).mockReturnValue(0.999) const { inventory } = chopForestPlot( testState({ forest: [ [ { itemId: 'sample-tree-1', daysOld: 103, daysSinceLastHarvest: 0, }, ], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.GOLD), }), 0, 0 ) // GOLD tier's full range is [8, 10]; a halved range would cap at 5. expect(inventory).toEqual([{ id: 'wood', quantity: 10 }]) }) }) describe('wood yield range per axe tier', () => { test('rolls the minimum of the tier range when random() is 0', () => { vitest.mocked(Math.random).mockReturnValue(0) const { inventory } = chopForestPlot( testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 0 }], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.GOLD), }), 0, 0 ) // GOLD tier's range is [8, 10]. expect(inventory).toEqual([{ id: 'wood', quantity: 8 }]) }) test('rolls close to the maximum of the tier range when random() is just under 1', () => { vitest.mocked(Math.random).mockReturnValue(0.999) const { inventory } = chopForestPlot( testState({ forest: [ [{ itemId: 'sample-tree-1', daysOld: 3, daysSinceLastHarvest: 0 }], ], inventory: [], inventoryLimit: INFINITE_STORAGE_LIMIT, toolLevels: toolLevelsWithAxe(toolLevel.GOLD), }), 0, 0 ) // GOLD tier's range is [8, 10]. expect(inventory).toEqual([{ id: 'wood', quantity: 10 }]) }) }) })