correctly memoize conent api

This commit is contained in:
Phillip Thelen
2024-06-20 12:23:24 +02:00
parent e0f6f79c5b
commit 877fe48225
6 changed files with 584 additions and 343 deletions

154
test/content/index.test.js Normal file
View File

@@ -0,0 +1,154 @@
import content from '../../website/common/script/content';
describe('content index', () => {
let clock;
afterEach(() => {
if (clock) {
clock.restore();
}
});
it('Releases eggs when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const mayEggs = content.eggs;
expect(mayEggs.Chameleon).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-20'));
const juneEggs = content.eggs;
expect(juneEggs.Chameleon).to.exist;
expect(Object.keys(mayEggs).length, '').to.equal(Object.keys(juneEggs).length - 1);
});
it('Releases hatching potions when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-05-20'));
const mayHatchingPotions = content.hatchingPotions;
expect(mayHatchingPotions.Koi).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const juneHatchingPotions = content.hatchingPotions;
expect(juneHatchingPotions.Koi).to.exist;
expect(Object.keys(mayHatchingPotions).length, '').to.equal(Object.keys(juneHatchingPotions).length - 1);
});
it('Releases armoire gear when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const juneGear = content.gear.flat;
expect(juneGear.armor_armoire_corsairsCoatAndCape).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-10'));
const julyGear = content.gear.flat;
expect(julyGear.armor_armoire_corsairsCoatAndCape).to.exist;
expect(Object.keys(juneGear).length, '').to.equal(Object.keys(julyGear).length - 3);
});
it('Releases pets gear when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const junePets = content.petInfo;
expect(junePets['Chameleon-Base']).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-10'));
const julyPets = content.petInfo;
expect(julyPets['Chameleon-Base']).to.exist;
expect(Object.keys(junePets).length, '').to.equal(Object.keys(julyPets).length - 10);
});
it('Releases mounts gear when appropriate without needing restarting', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const juneMounts = content.mountInfo;
expect(juneMounts['Chameleon-Base']).to.not.exist;
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-07-10'));
const julyMounts = content.mountInfo;
expect(julyMounts['Chameleon-Base']).to.exist;
expect(Object.keys(juneMounts).length, '').to.equal(Object.keys(julyMounts).length - 10);
});
it('marks regular food as buyable and droppable without any events', () => {
clock = sinon.useFakeTimers(new Date('2024-06-20'));
const { food } = content;
Object.keys(food).forEach(key => {
if (key === 'Saddle') {
expect(food[key].canBuy(), `${key} canBuy`).to.be.true;
expect(food[key].canDrop, `${key} canDrop`).to.be.false;
return;
}
let expected = true;
if (key.startsWith('Cake_')) {
expected = false;
} else if (key.startsWith('Candy_')) {
expected = false;
} else if (key.startsWith('Pie_')) {
expected = false;
}
expect(food[key].canBuy(), `${key} canBuy`).to.equal(expected);
expect(food[key].canDrop, `${key} canDrop`).to.equal(expected);
});
});
it('marks candy as buyable and droppable during habitoween', () => {
clock = sinon.useFakeTimers(new Date('2024-10-31'));
const { food } = content;
Object.keys(food).forEach(key => {
if (key === 'Saddle') {
expect(food[key].canBuy(), `${key} canBuy`).to.be.true;
expect(food[key].canDrop, `${key} canDrop`).to.be.false;
return;
}
let expected = false;
if (key.startsWith('Cake_')) {
expected = false;
} else if (key.startsWith('Candy_')) {
expected = true;
} else if (key.startsWith('Pie_')) {
expected = false;
}
expect(food[key].canBuy(), `${key} canBuy`).to.equal(expected);
expect(food[key].canDrop, `${key} canDrop`).to.equal(expected);
});
});
it('marks cake as buyable and droppable during birthday', () => {
clock = sinon.useFakeTimers(new Date('2024-01-31'));
const { food } = content;
Object.keys(food).forEach(key => {
if (key === 'Saddle') {
expect(food[key].canBuy(), `${key} canBuy`).to.be.true;
expect(food[key].canDrop, `${key} canDrop`).to.be.false;
return;
}
let expected = false;
if (key.startsWith('Cake_')) {
expected = true;
} else if (key.startsWith('Candy_')) {
expected = false;
} else if (key.startsWith('Pie_')) {
expected = false;
}
expect(food[key].canBuy(), `${key} canBuy`).to.equal(expected);
expect(food[key].canDrop, `${key} canDrop`).to.equal(expected);
});
});
it('marks pie as buyable and droppable during pi day', () => {
clock = sinon.useFakeTimers(new Date('2024-03-14'));
const { food } = content;
Object.keys(food).forEach(key => {
if (key === 'Saddle') {
expect(food[key].canBuy(), `${key} canBuy`).to.be.true;
expect(food[key].canDrop, `${key} canDrop`).to.be.false;
return;
}
let expected = false;
if (key.startsWith('Cake_')) {
expected = false;
} else if (key.startsWith('Candy_')) {
expected = false;
} else if (key.startsWith('Pie_')) {
expected = true;
}
expect(food[key].canBuy(), `${key} canBuy`).to.equal(expected);
expect(food[key].canDrop, `${key} canDrop`).to.equal(expected);
});
});
});

View File

@@ -6,7 +6,7 @@ import {
} from '../helpers/content.helper'; } from '../helpers/content.helper';
import t from '../../website/common/script/content/translation'; import t from '../../website/common/script/content/translation';
import * as stable from '../../website/common/script/content/stable'; import stable from '../../website/common/script/content/stable';
import eggs from '../../website/common/script/content/eggs'; import eggs from '../../website/common/script/content/eggs';
import potions from '../../website/common/script/content/hatching-potions'; import potions from '../../website/common/script/content/hatching-potions';

View File

@@ -320,7 +320,7 @@
<script> <script>
import each from 'lodash/each'; import each from 'lodash/each';
import * as quests from '@/../../common/script/content/quests'; import * as quests from '@/../../common/script/content/quests';
import { mountInfo, petInfo } from '@/../../common/script/content/stable'; import stable from '@/../../common/script/content/stable';
import content from '@/../../common/script/content'; import content from '@/../../common/script/content';
import gear from '@/../../common/script/content/gear'; import gear from '@/../../common/script/content/gear';
import styleHelper from '@/mixins/styleHelper'; import styleHelper from '@/mixins/styleHelper';
@@ -330,6 +330,8 @@ import userLink from '../userLink';
import PurchaseHistoryTable from '../ui/purchaseHistoryTable.vue'; import PurchaseHistoryTable from '../ui/purchaseHistoryTable.vue';
import { userStateMixin } from '../../mixins/userState'; import { userStateMixin } from '../../mixins/userState';
const { mountInfo, petInfo } = stable;
export default { export default {
components: { components: {
userLink, userLink,

View File

@@ -80,7 +80,7 @@
</style> </style>
<script> <script>
import { mountInfo } from '@/../../common/script/content/stable'; import stable from '@/../../common/script/content/stable';
import markdownDirective from '@/directives/markdown'; import markdownDirective from '@/directives/markdown';
export default { export default {
@@ -105,7 +105,7 @@ export default {
}, },
methods: { methods: {
openDialog (mountKey) { openDialog (mountKey) {
this.mount = mountInfo[mountKey]; this.mount = stable.mountInfo[mountKey];
this.$root.$emit('bv::show::modal', 'mount-raised-modal'); this.$root.$emit('bv::show::modal', 'mount-raised-modal');
}, },
close () { close () {

View File

@@ -20,7 +20,7 @@ import achievements from './achievements';
import eggs from './eggs'; import eggs from './eggs';
import hatchingPotions from './hatching-potions'; import hatchingPotions from './hatching-potions';
import * as stable from './stable'; import stable from './stable';
import gear from './gear'; import gear from './gear';
import { quests, questsByLevel, userCanOwnQuestCategories } from './quests'; import { quests, questsByLevel, userCanOwnQuestCategories } from './quests';
@@ -38,6 +38,7 @@ import { REPEATING_EVENTS, getRepeatingEvents } from './constants/events';
import loginIncentives from './loginIncentives'; import loginIncentives from './loginIncentives';
import officialPinnedItems from './officialPinnedItems'; import officialPinnedItems from './officialPinnedItems';
import memoize from '../fns/datedMemoize';
const api = {}; const api = {};
@@ -165,9 +166,15 @@ api.cardTypes = {
api.special = api.spells.special; api.special = api.spells.special;
api.dropEggs = eggs.drops; Object.defineProperty(api, 'dropEggs', {
api.questEggs = eggs.quests; get () { return eggs.drops; },
api.eggs = eggs.all; });
Object.defineProperty(api, 'questEggs', {
get () { return eggs.quests; },
});
Object.defineProperty(api, 'eggs', {
get () { return eggs.all; },
});
api.timeTravelStable = { api.timeTravelStable = {
pets: { pets: {
@@ -186,25 +193,56 @@ api.timeTravelStable = {
}, },
}; };
api.dropHatchingPotions = hatchingPotions.drops; Object.defineProperty(api, 'dropHatchingPotions', {
api.premiumHatchingPotions = hatchingPotions.premium; get () { return hatchingPotions.drops; },
api.wackyHatchingPotions = hatchingPotions.wacky; });
api.hatchingPotions = hatchingPotions.all; Object.defineProperty(api, 'premiumHatchingPotions', {
get () { return hatchingPotions.premium; },
});
Object.defineProperty(api, 'wackyHatchingPotions', {
get () { return hatchingPotions.wacky; },
});
Object.defineProperty(api, 'hatchingPotions', {
get () { return hatchingPotions.all; },
});
api.pets = stable.dropPets; Object.defineProperty(api, 'dropPets', {
api.premiumPets = stable.premiumPets; get () { return stable.dropPets; },
api.questPets = stable.questPets; });
api.specialPets = stable.specialPets; Object.defineProperty(api, 'premiumPets', {
api.wackyPets = stable.wackyPets; get () { return stable.premiumPets; },
api.petInfo = stable.petInfo; });
Object.defineProperty(api, 'questPets', {
get () { return stable.questPets; },
});
Object.defineProperty(api, 'specialPets', {
get () { return stable.specialPets; },
});
Object.defineProperty(api, 'wackyPets', {
get () { return stable.wackyPets; },
});
Object.defineProperty(api, 'petInfo', {
get () { return stable.petInfo; },
});
api.mounts = stable.dropMounts; Object.defineProperty(api, 'dropMounts', {
api.questMounts = stable.questMounts; get () { return stable.dropMounts; },
api.premiumMounts = stable.premiumMounts; });
api.specialMounts = stable.specialMounts; Object.defineProperty(api, 'premiumMounts', {
api.mountInfo = stable.mountInfo; get () { return stable.premiumMounts; },
});
Object.defineProperty(api, 'questMounts', {
get () { return stable.questMounts; },
});
Object.defineProperty(api, 'specialMounts', {
get () { return stable.specialMounts; },
});
Object.defineProperty(api, 'mountInfo', {
get () { return stable.mountInfo; },
});
api.food = { function buildFood() {
const food = {
Meat: { Meat: {
text: t('foodMeat'), text: t('foodMeat'),
textA: t('foodMeatA'), textA: t('foodMeatA'),
@@ -270,6 +308,7 @@ api.food = {
text: t('foodSaddleText'), text: t('foodSaddleText'),
value: 5, value: 5,
notes: t('foodSaddleNotes'), notes: t('foodSaddleNotes'),
canBuy: () => true,
canDrop: false, canDrop: false,
}, },
/* eslint-disable camelcase */ /* eslint-disable camelcase */
@@ -462,7 +501,7 @@ getRepeatingEvents(moment()).forEach(event => {
FOOD_SEASON = event.foodSeason; FOOD_SEASON = event.foodSeason;
} }
}); });
each(api.food, (food, key) => { each(food, (foodItem, key) => {
let foodType = 'Normal'; let foodType = 'Normal';
if (key.startsWith('Cake_')) { if (key.startsWith('Cake_')) {
foodType = 'Cake'; foodType = 'Cake';
@@ -471,7 +510,7 @@ each(api.food, (food, key) => {
} else if (key.startsWith('Pie_')) { } else if (key.startsWith('Pie_')) {
foodType = 'Pie'; foodType = 'Pie';
} }
defaults(food, { defaults(foodItem, {
value: 1, value: 1,
key, key,
notes: t('foodNotes'), notes: t('foodNotes'),
@@ -480,6 +519,15 @@ each(api.food, (food, key) => {
}); });
}); });
return food;
}
const memoizedBuildFood = memoize(buildFood);
Object.defineProperty(api, 'food', {
get () { return memoizedBuildFood(); },
});
api.appearances = appearances; api.appearances = appearances;
api.backgrounds = backgroundsTree(); api.backgrounds = backgroundsTree();

View File

@@ -4,11 +4,9 @@ import { EVENTS } from './constants/events';
import allEggs from './eggs'; import allEggs from './eggs';
import allPotions from './hatching-potions'; import allPotions from './hatching-potions';
import t from './translation'; import t from './translation';
import memoize from '../fns/datedMemoize';
const petInfo = {}; function constructSet (type, eggs, potions, petInfo, mountInfo, hasMounts = true) {
const mountInfo = {};
function constructSet (type, eggs, potions, hasMounts = true) {
const pets = {}; const pets = {};
const mounts = {}; const mounts = {};
@@ -123,11 +121,6 @@ const canFindSpecial = {
}, },
}; };
const [dropPets, dropMounts] = constructSet('drop', allEggs.drops, allPotions.drops);
const [premiumPets, premiumMounts] = constructSet('premium', allEggs.drops, allPotions.premium);
const [questPets, questMounts] = constructSet('quest', allEggs.quests, allPotions.drops);
const wackyPets = constructSet('wacky', allEggs.drops, allPotions.wacky, false);
const specialPets = { const specialPets = {
'Wolf-Veteran': 'veteranWolf', 'Wolf-Veteran': 'veteranWolf',
'Wolf-Cerberus': 'cerberusPup', 'Wolf-Cerberus': 'cerberusPup',
@@ -178,6 +171,15 @@ const specialMounts = {
'JackOLantern-RoyalPurple': 'royalPurpleJackolantern', 'JackOLantern-RoyalPurple': 'royalPurpleJackolantern',
}; };
function buildInfo () {
const petInfo = {};
const mountInfo = {};
const [dropPets, dropMounts] = constructSet('drop', allEggs.drops, allPotions.drops, petInfo, mountInfo);
const [premiumPets, premiumMounts] = constructSet('premium', allEggs.drops, allPotions.premium, petInfo, mountInfo);
const [questPets, questMounts] = constructSet('quest', allEggs.quests, allPotions.drops, petInfo, mountInfo);
const wackyPets = constructSet('wacky', allEggs.drops, allPotions.wacky, petInfo, mountInfo, false);
each(specialPets, (translationString, key) => { each(specialPets, (translationString, key) => {
petInfo[key] = { petInfo[key] = {
key, key,
@@ -206,7 +208,7 @@ each(specialMounts, (translationString, key) => {
}; };
}); });
export { return {
dropPets, dropPets,
premiumPets, premiumPets,
questPets, questPets,
@@ -219,3 +221,38 @@ export {
petInfo, petInfo,
mountInfo, mountInfo,
}; };
}
const memoizedBuildInfo = memoize(buildInfo);
export default {
get dropPets () {
return memoizedBuildInfo().dropPets;
},
get premiumPets () {
return memoizedBuildInfo().premiumPets;
},
get questPets () {
return memoizedBuildInfo().questPets;
},
get wackyPets () {
return memoizedBuildInfo().wackyPets;
},
get dropMounts () {
return memoizedBuildInfo().dropMounts;
},
get questMounts () {
return memoizedBuildInfo().questMounts;
},
get premiumMounts () {
return memoizedBuildInfo().premiumMounts;
},
get petInfo () {
return memoizedBuildInfo().petInfo;
},
get mountInfo () {
return memoizedBuildInfo().mountInfo;
},
specialPets,
specialMounts,
};