Skip to content

Commit

Permalink
Merge patch commit
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] committed Feb 3, 2024
2 parents 5b48165 + f588c7b commit 2544531
Show file tree
Hide file tree
Showing 30 changed files with 509 additions and 136 deletions.
1 change: 1 addition & 0 deletions .env.development.local
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
REACT_APP_API_ROOT=http://localhost:3001/
REACT_APP_ENABLE_KEGS=true
REACT_APP_ENABLE_FOREST=true

# Silence warnings from dev server
# https://stackoverflow.com/a/70834076
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jeremyckahn/farmhand",
"version": "1.18.8",
"version": "1.18.9",
"publishConfig": {
"access": "public"
},
Expand All @@ -24,7 +24,7 @@
"postversion": "git push && git push --tags",
"start": "react-scripts --openssl-legacy-provider start",
"start:api": "NODE_OPTIONS=--openssl-legacy-provider vercel dev --listen localhost:3001",
"start:backend": "docker-compose up",
"start:backend": "docker compose up",
"start:tracker": "bittorrent-tracker",
"test": "react-scripts test",
"test:debug": "react-scripts --inspect-brk test --runInBand --no-cache",
Expand Down
20 changes: 18 additions & 2 deletions src/components/Farmhand/Farmhand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* @typedef {import("../../index").farmhand.item} farmhand.item
* @typedef {import("../../index").farmhand.cow} farmhand.cow
* @typedef {import("../../index").farmhand.cowBreedingPen} farmhand.cowBreedingPen
* @typedef {import("../../index").farmhand.forestForageable} farmhand.forestForageable
* @typedef {import("../../index").farmhand.keg} farmhand.keg
* @typedef {import("../../index").farmhand.plantedTree} farmhand.plantedTree
* @typedef {import("../../index").farmhand.plotContent} farmhand.plotContent
* @typedef {import("../../index").farmhand.peerMessage} farmhand.peerMessage
* @typedef {import("../../index").farmhand.peerMetadata} farmhand.peerMetadata
Expand Down Expand Up @@ -60,6 +62,7 @@ import { levelAchieved } from '../../utils/levelAchieved'
import {
computeMarketPositions,
createNewField,
createNewForest,
doesMenuObstructStage,
generateCow,
getAvailableShopInventory,
Expand Down Expand Up @@ -116,7 +119,7 @@ import {
SERVER_ERROR,
UPDATE_AVAILABLE,
} from '../../strings'
import { endpoints, rtcConfig, trackerUrls } from '../../config'
import { endpoints, features, rtcConfig, trackerUrls } from '../../config'

import { scarecrow } from '../../data/items'

Expand Down Expand Up @@ -214,6 +217,7 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => {
* @property {number} experience
* @property {string} farmName
* @property {(?farmhand.plotContent)[][]} field
* @property {(farmhand.plantedTree | farmhand.forestForageable | null)[][]} forest
* @property {farmhand.fieldMode} fieldMode
* @property {Function?} getCowAccept https://github.com/dmotz/trystero#receiver
* @property {Function?} getCowReject https://github.com/dmotz/trystero#receiver
Expand Down Expand Up @@ -272,6 +276,7 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => {
* @property {number} purchasedCowPen
* @property {number} purchasedCellar
* @property {number} purchasedField
* @property {number} purchasedForest
* @property {number} purchasedSmelter
* @property {number} profitabilityStreak
* @property {number} record7dayProfitAverage
Expand All @@ -287,6 +292,7 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => {
* @property {boolean} showHomeScreen Option to show the Home Screen
* @property {boolean} showNotifications
* @property {farmhand.stageFocusType} stageFocus
* indicating if the stage has been unlocked
* @property {Array.<farmhand.notification>} todaysNotifications
* @property {number} todaysLosses Should always be a negative number.
* @property {Object} todaysPurchases Keys are item names, values are their
Expand Down Expand Up @@ -364,13 +370,17 @@ export default class Farmhand extends FarmhandReducers {
}

get viewList() {
const { CELLAR, COW_PEN, HOME, WORKSHOP } = stageFocusType
const { CELLAR, COW_PEN, HOME, WORKSHOP, FOREST } = stageFocusType
const viewList = [...STANDARD_VIEW_LIST]

if (this.state.showHomeScreen) {
viewList.unshift(HOME)
}

if (this.isForestUnlocked && features.FOREST) {
viewList.push(FOREST)
}

if (this.state.purchasedCowPen) {
viewList.push(COW_PEN)
}
Expand Down Expand Up @@ -409,6 +419,10 @@ export default class Farmhand extends FarmhandReducers {
return isOnline && room !== DEFAULT_ROOM
}

get isForestUnlocked() {
return this.levelEntitlements.stageFocusType[stageFocusType.FOREST]
}

/**
* @returns {farmhand.state}
*/
Expand Down Expand Up @@ -437,6 +451,7 @@ export default class Farmhand extends FarmhandReducers {
farmName: 'Unnamed',
field: createNewField(),
fieldMode: OBSERVE,
forest: createNewForest(),
getCowAccept: noop,
getCowReject: noop,
getCowTradeRequest: noop,
Expand Down Expand Up @@ -490,6 +505,7 @@ export default class Farmhand extends FarmhandReducers {
purchasedCowPen: 0,
purchasedCellar: 0,
purchasedField: 0,
purchasedForest: 0,
purchasedSmelter: 0,
sendCowTradeRequest: noop,
showHomeScreen: true,
Expand Down
4 changes: 4 additions & 0 deletions src/components/Farmhand/FarmhandReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ export class FarmhandReducers extends Component {
throw new Error('Unimplemented')
}
/** @type BoundReducer */
purchaseForest() {
throw new Error('Unimplemented')
}
/** @type BoundReducer */
purchaseItem() {
throw new Error('Unimplemented')
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/Forest/Forest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'

export const Forest = () => {
return <div>'welcome to da forest'</div>
}
1 change: 1 addition & 0 deletions src/components/Forest/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Forest } from './Forest'
26 changes: 26 additions & 0 deletions src/components/Item/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,19 @@ export const Item = ({
)}
</p>
)}
{isPurchaseView && (
<p>
Total:{' '}
{purchaseQuantity ? (
<AnimatedNumber
{...{
number: purchaseQuantity * adjustedValue,
formatter: moneyString,
}}
/>
) : null}
</p>
)}
{isSellView && (
<p>
<Tooltip
Expand Down Expand Up @@ -242,6 +255,19 @@ export const Item = ({
)}
</p>
)}
{isSellView && (
<p>
Total:{' '}
{sellQuantity ? (
<AnimatedNumber
{...{
number: sellQuantity * sellPrice,
formatter: moneyString,
}}
/>
) : null}
</p>
)}
{showQuantity && (
<p>
<strong>In inventory:</strong>{' '}
Expand Down
172 changes: 100 additions & 72 deletions src/components/Item/Item.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import CardHeader from '@mui/material/CardHeader'
import { shallow } from 'enzyme'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import { testItem } from '../../test-utils'

Expand All @@ -10,95 +10,123 @@ import { Item } from './Item'

jest.mock('../../data/maps')

let component

beforeEach(() => {
component = shallow(
<Item
{...{
completedAchievements: {},
historicalValueAdjustments: [],
inventory: [],
inventoryLimit: INFINITE_STORAGE_LIMIT,
item: testItem({ name: '' }),
money: 0,
playerInventoryQuantities: {},
valueAdjustments: {},
adjustedValue: 0,
previousDayAdjustedValue: 0,
}}
/>
)
})

describe('static UI', () => {
beforeEach(() => {
component.setProps({ item: testItem({ name: 'an-item' }) })
})

test('renders the name', () => {
expect(component.find(CardHeader).props().title).toEqual('an-item')
})
})

describe('conditional UI', () => {
describe('class names', () => {
beforeEach(() => {
component.setProps({ isSelected: true })
describe('Item', () => {
const baseProps = {
completedAchievements: {},
historicalValueAdjustments: [],
inventory: [],
inventoryLimit: INFINITE_STORAGE_LIMIT,
item: testItem(),
money: 0,
playerInventoryQuantities: {},
valueAdjustments: {},
adjustedValue: 0,
previousDayAdjustedValue: 0,
}

describe('static UI', () => {
test('renders the name', () => {
const itemName = 'Cool Item'
render(<Item {...{ ...baseProps, item: testItem({ name: itemName }) }} />)
expect(screen.getByText(itemName)).toBeInTheDocument()
})
})

test('supports is-selected', () => {
expect(component.hasClass('is-selected')).toBeTruthy()
describe('conditional UI', () => {
describe('class names', () => {
test('supports is-selected', () => {
const { container } = render(
<Item {...{ ...baseProps, isSelected: true }} />
)
expect(container.firstChild).toHaveClass('is-selected')
})
})
})

describe('isPurchaseView', () => {
beforeEach(() => {
component.setProps({
describe('isPurchaseView', () => {
const props = {
...baseProps,
isPurchaseView: true,
item: testItem({ name: 'an-item' }),
adjustedValue: 10,
adjustedValue: 10.42,
}

describe('user has enough money', () => {
beforeEach(() => {
render(<Item {...{ ...props, money: 20 }} />)
})

test('enables purchase buttons', () => {
expect(screen.getByRole('button', { name: 'Buy' })).not.toBeDisabled()
})
})
})

describe('user has enough money', () => {
beforeEach(() => {
component.setProps({
money: 20,
describe('user does not have enough money', () => {
beforeEach(() => {
render(<Item {...{ ...props, money: 5 }} />)
})

test('disables purchase buttons', () => {
expect(screen.getByRole('button', { name: 'Buy' })).toBeDisabled()
})
})

test('enables purchase buttons', () => {
expect(component.find('.purchase').props().disabled).toEqual(false)
describe('prices', () => {
beforeEach(() => {
render(<Item {...{ ...props, money: 100 }} />)
})

test('displays item price', () => {
const buyPrice = screen.getByText('Price:')
expect(within(buyPrice).getByText('$10.42')).toBeInTheDocument()
})

test('displays total price', async () => {
const increment = screen.getByRole('button', { name: 'Increment' })
userEvent.click(increment)
const total = screen.getByText('Total:')
await waitFor(() =>
expect(within(total).getByText('$20.84')).toBeInTheDocument()
)
})
})
})

describe('user does not have enough money', () => {
describe('isSellView', () => {
beforeEach(() => {
component.setProps({
money: 5,
})
const id = 'an-item'
render(
<Item
{...{
...baseProps,
isSellView: true,
adjustedValue: 10.42,
item: testItem({ id }),
playerInventoryQuantities: { [id]: 4 },
}}
/>
)
})

test('disables purchase buttons', () => {
expect(component.find('.purchase').props().disabled).toEqual(true)
test('renders sell buttons', () => {
expect(screen.getByRole('button', { name: 'Sell' })).toBeInTheDocument()
})
})
})

describe('isSellView', () => {
beforeEach(() => {
component.setProps({
isSellView: true,
item: testItem({ id: 'an-item' }),
playerInventoryQuantities: {
'an-item': 1,
},
})
})
describe('prices', () => {
test('displays item price', () => {
const sellPrice = screen.getByText('Sell price:')
expect(within(sellPrice).getByText('$10.42')).toBeInTheDocument()
})

test('renders sell buttons', () => {
expect(component.find('.sell')).toHaveLength(1)
test('displays total price', async () => {
const increment = screen.getByRole('button', { name: 'Increment' })
userEvent.click(increment)
userEvent.click(increment)
userEvent.click(increment)
const total = screen.getByText('Total:')
await waitFor(() =>
expect(within(total).getByText('$41.68')).toBeInTheDocument()
)
})
})
})
})
})
Loading

0 comments on commit 2544531

Please sign in to comment.