Skip to content

Commit

Permalink
Improve achievements prop handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ajuvonen committed Dec 23, 2024
1 parent 55b6412 commit 8cec249
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 81 deletions.
119 changes: 68 additions & 51 deletions src/components/__tests__/achievements.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import {describe, it, expect} from 'vitest';
import {mount} from '@vue/test-utils';
import {useAchievements} from '@/hooks/achievements';
import {AchievementLevel, Category, type Achievements, type Favorites} from '@/utils/types';
import {
AchievementLevel,
Category,
type AchievementProps,
type Achievements,
type Favorites,
} from '@/utils/types';
import {take} from 'remeda';
import {ALL_VEGGIES, BEANS, FRUITS, GRAINS, LEAFIES, ROOTS, VEGETABLES} from '@/utils/constants';

Expand Down Expand Up @@ -40,6 +46,16 @@ const getFavorites = (amount: number = 0) =>
{} as Favorites,
);

const getProps = (achievementProps: Partial<AchievementProps> = {}): AchievementProps => ({
completedChallenges: 0,
favorites: getFavorites(),
hotStreakLength: 0,
totalWeeks: 0,
uniqueVeggies: [],
veggiesThisWeek: 0,
...achievementProps,
});

describe('achievements', () => {
it('sets initial status', async () => {
const {achievements} = await withSetup(useAchievements);
Expand All @@ -48,153 +64,147 @@ describe('achievements', () => {

it('advances completionist', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [...Array(39)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(39)]}));
expect(achievements.value.completionist).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, [...Array(40)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(40)]}));
expect(achievements.value.completionist).toBe(AchievementLevel.Bronze);
advanceAchievements(0, [...Array(80)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(80)]}));
expect(achievements.value.completionist).toBe(AchievementLevel.Silver);
advanceAchievements(0, [...Array(150)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(150)]}));
expect(achievements.value.completionist).toBe(AchievementLevel.Gold);
});

it('goes straight to silver', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [...Array(80)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(80)]}));
expect(achievements.value.completionist).toBe(AchievementLevel.Silver);
});

it('completionist goes straight to gold', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [...Array(150)], 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: [...Array(150)]}));
expect(achievements.value.completionist).toBe(AchievementLevel.Gold);
});

it('advances experimenterFruit', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(FRUITS, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(FRUITS, 14)}));
expect(achievements.value.experimenterFruit).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(FRUITS, 15), 0, 0, 0, getFavorites());
expect(achievements.value.experimenterFruit).toBe(AchievementLevel.Gold);
});

it('experimenter goes straight to gold', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(FRUITS, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(FRUITS, 15)}));
expect(achievements.value.experimenterFruit).toBe(AchievementLevel.Gold);
});

it('advances experimenterVegetable', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(VEGETABLES, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(VEGETABLES, 14)}));
expect(achievements.value.experimenterVegetable).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(VEGETABLES, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(VEGETABLES, 15)}));
expect(achievements.value.experimenterVegetable).toBe(AchievementLevel.Gold);
});

it('advances experimenterLeafy', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(LEAFIES, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(LEAFIES, 14)}));
expect(achievements.value.experimenterLeafy).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(LEAFIES, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(LEAFIES, 15)}));
expect(achievements.value.experimenterLeafy).toBe(AchievementLevel.Gold);
});

it('advances experimenterBean', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(BEANS, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(BEANS, 14)}));
expect(achievements.value.experimenterBean).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(BEANS, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(BEANS, 15)}));
expect(achievements.value.experimenterBean).toBe(AchievementLevel.Gold);
});

it('advances experimenterRoot', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(ROOTS, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(ROOTS, 14)}));
expect(achievements.value.experimenterRoot).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(ROOTS, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(ROOTS, 15)}));
expect(achievements.value.experimenterRoot).toBe(AchievementLevel.Gold);
});

it('advances experimenterGrain', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, take(GRAINS, 14), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(GRAINS, 14)}));
expect(achievements.value.experimenterGrain).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, take(GRAINS, 15), 0, 0, 0, getFavorites());
advanceAchievements(getProps({uniqueVeggies: take(GRAINS, 15)}));
expect(achievements.value.experimenterGrain).toBe(AchievementLevel.Gold);
});

it('advances hot streak', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [], 4, 0, 0, getFavorites());
advanceAchievements(getProps({hotStreakLength: 4}));
expect(achievements.value.hotStreak).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, [], 5, 0, 0, getFavorites());
advanceAchievements(getProps({hotStreakLength: 5}));
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Bronze);
advanceAchievements(0, [], 10, 0, 0, getFavorites());
advanceAchievements(getProps({hotStreakLength: 10}));
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Silver);
advanceAchievements(0, [], 20, 0, 0, getFavorites());
advanceAchievements(getProps({hotStreakLength: 20}));
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Gold);
});

it('advances committed', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [], 0, 11, 0, getFavorites());
advanceAchievements(getProps({totalWeeks: 11}));
expect(achievements.value.committed).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, [], 0, 12, 0, getFavorites());
advanceAchievements(getProps({totalWeeks: 12}));
expect(achievements.value.committed).toEqual(AchievementLevel.Bronze);
advanceAchievements(0, [], 0, 26, 0, getFavorites());
advanceAchievements(getProps({totalWeeks: 26}));
expect(achievements.value.committed).toEqual(AchievementLevel.Silver);
advanceAchievements(0, [], 0, 52, 0, getFavorites());
advanceAchievements(getProps({totalWeeks: 52}));
expect(achievements.value.committed).toEqual(AchievementLevel.Gold);
});

it('advances challengeAccepted', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [], 0, 0, 4, getFavorites());
advanceAchievements(getProps({completedChallenges: 4}));
expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, [], 0, 0, 5, getFavorites());
advanceAchievements(getProps({completedChallenges: 5}));
expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Bronze);
advanceAchievements(0, [], 0, 0, 10, getFavorites());
advanceAchievements(getProps({completedChallenges: 10}));
expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Silver);
advanceAchievements(0, [], 0, 0, 20, getFavorites());
advanceAchievements(getProps({completedChallenges: 20}));
expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Gold);
});

it('advances thirtyVeggies', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(29, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 29}));
expect(achievements.value.thirtyVeggies).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(30, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 30}));
expect(achievements.value.thirtyVeggies).toBe(AchievementLevel.Gold);
advanceAchievements(40, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 40}));
expect(achievements.value.thirtyVeggies).toBe(AchievementLevel.Platinum);
});

it('resets thirtyVeggies', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(30, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 30}));
expect(achievements.value.thirtyVeggies).toEqual(AchievementLevel.Gold);
advanceAchievements(0, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 0}));
expect(achievements.value.thirtyVeggies).toBe(AchievementLevel.NoAchievement);
advanceAchievements(40, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 40}));
expect(achievements.value.thirtyVeggies).toEqual(AchievementLevel.Platinum);
advanceAchievements(0, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 0}));
expect(achievements.value.thirtyVeggies).toBe(AchievementLevel.NoAchievement);
advanceAchievements(40, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 40}));
expect(achievements.value.thirtyVeggies).toEqual(AchievementLevel.Platinum);
advanceAchievements(39, [], 0, 0, 0, getFavorites());
advanceAchievements(getProps({veggiesThisWeek: 39}));
expect(achievements.value.thirtyVeggies).toBe(AchievementLevel.Gold);
});

it('advances favorite', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
advanceAchievements(0, [], 0, 0, 0, getFavorites(4));
advanceAchievements(getProps({favorites: getFavorites(4)}));
expect(achievements.value.favorite).toEqual(AchievementLevel.NoAchievement);
advanceAchievements(0, [], 0, 0, 0, getFavorites(5));
advanceAchievements(getProps({favorites: getFavorites(5)}));
expect(achievements.value.favorite).toEqual(AchievementLevel.Bronze);
advanceAchievements(0, [], 0, 0, 0, getFavorites(20));
advanceAchievements(getProps({favorites: getFavorites(20)}));
expect(achievements.value.favorite).toEqual(AchievementLevel.Silver);
advanceAchievements(0, [], 0, 0, 0, getFavorites(50));
advanceAchievements(getProps({favorites: getFavorites(50)}));
expect(achievements.value.favorite).toEqual(AchievementLevel.Gold);
});

Expand All @@ -214,7 +224,14 @@ describe('achievements', () => {
thirtyVeggies: AchievementLevel.Platinum,
};
const {advanceAchievements, achievements, resetAchievements} = await withSetup(useAchievements);
advanceAchievements(40, ALL_VEGGIES, 30, 52, 20, getFavorites(50));
advanceAchievements({
completedChallenges: 20,
favorites: getFavorites(50),
hotStreakLength: 30,
totalWeeks: 52,
uniqueVeggies: ALL_VEGGIES,
veggiesThisWeek: 40,
});
expect(achievements.value).toEqual(expectedAchievements);
resetAchievements();
expect(achievements.value).toEqual(defaultAchievements);
Expand Down
26 changes: 4 additions & 22 deletions src/hooks/achievements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,11 @@ import {intersection, isDeepEqual, mapValues} from 'remeda';
import {createActor, setup, type MachineContext} from 'xstate';
import type {GuardArgs} from 'xstate/guards';
import {BEANS, FRUITS, GRAINS, LEAFIES, ROOTS, VEGETABLES} from '@/utils/constants';
import type {Achievements, Favorites} from '@/utils/types';
import type {AchievementProps, Achievements} from '@/utils/types';

type AdvanceEvent = {
type: 'ADVANCE';
veggiesThisWeek: number;
uniqueVeggies: string[];
completedChallenges: number;
hotStreakLength: number;
totalWeeks: number;
favorites: Favorites;
};
} & AchievementProps;

type ResetEvent = {
type: 'RESET';
Expand Down Expand Up @@ -448,22 +442,10 @@ export function useAchievements() {

return {
achievements,
advanceAchievements: (
veggiesThisWeek: number,
uniqueVeggies: string[],
hotStreakLength: number,
totalWeeks: number,
completedChallenges: number,
favorites: Favorites,
) =>
advanceAchievements: (achievementProps: AchievementProps) =>
actor.send({
type: 'ADVANCE',
veggiesThisWeek,
uniqueVeggies,
hotStreakLength,
totalWeeks,
completedChallenges,
favorites,
...achievementProps,
}),
resetAchievements: () => actor.send({type: 'RESET'}),
};
Expand Down
16 changes: 8 additions & 8 deletions src/stores/appStateStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ export const useAppStateStore = defineStore('appState', () => {
storeToRefs(useActivityStore());

watchEffect(() =>
advanceAchievements(
currentVeggies.value.length,
uniqueVeggies.value,
hotStreak.value,
weeks.value.length,
completedChallenges.value,
favorites.value,
),
advanceAchievements({
completedChallenges: completedChallenges.value,
favorites: favorites.value,
hotStreakLength: hotStreak.value,
totalWeeks: weeks.value.length,
uniqueVeggies: uniqueVeggies.value,
veggiesThisWeek: currentVeggies.value.length,
}),
);

// State refs
Expand Down
9 changes: 9 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import type {DateTime} from 'luxon';

export type Locale = 'en' | 'fi';

export type AchievementProps = {
completedChallenges: number;
favorites: Favorites;
hotStreakLength: number;
totalWeeks: number;
uniqueVeggies: string[];
veggiesThisWeek: number;
};

export enum Category {
Fruit = 'Fruit',
Vegetable = 'Vegetable',
Expand Down

0 comments on commit 8cec249

Please sign in to comment.