diff --git a/test/common/libs/shops.test.js b/test/common/libs/shops.test.js index e7d919fda9..c936884b2d 100644 --- a/test/common/libs/shops.test.js +++ b/test/common/libs/shops.test.js @@ -47,9 +47,9 @@ describe('shops', () => { describe('premium hatching potions', () => { it('contains current scheduled premium hatching potions', async () => { - clock = sinon.useFakeTimers(new Date('2024-04-01')); + clock = sinon.useFakeTimers(new Date('2024-04-01T09:00:00.000Z')); const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions'); - expect(potions.items.length).to.eql(2); + expect(potions.items.length).to.eql(3); }); it('does not contain past scheduled premium hatching potions', async () => { @@ -73,9 +73,9 @@ describe('shops', () => { }); it('does not contain locked quest premium hatching potions', async () => { - clock = sinon.useFakeTimers(new Date('2024-04-01')); + clock = sinon.useFakeTimers(new Date('2024-04-01T09:00:00.000Z')); const potions = shared.shops.getMarketCategories(user).find(x => x.identifier === 'premiumHatchingPotions'); - expect(potions.items.length).to.eql(2); + expect(potions.items.length).to.eql(3); expect(potions.items.filter(x => x.key === 'Bronze' || x.key === 'BlackPearl').length).to.eql(0); }); diff --git a/test/content/schedule.test.js b/test/content/schedule.test.js index 5731a81cdd..6e8c172a98 100644 --- a/test/content/schedule.test.js +++ b/test/content/schedule.test.js @@ -18,12 +18,19 @@ function validateMatcher (matcher, checkedDate) { describe('Content Schedule', () => { let switchoverTime; + let clock; beforeEach(() => { switchoverTime = nconf.get('CONTENT_SWITCHOVER_TIME_OFFSET') || 0; clearCachedMatchers(); }); + afterEach(() => { + if (clock) { + clock.restore(); + } + }); + it('assembles scheduled items on january 15th', () => { const date = new Date('2024-01-15'); const matchers = getAllScheduleMatchingGroups(date); @@ -105,8 +112,14 @@ describe('Content Schedule', () => { expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); }); - it('sets the end date if its on the release day', () => { - const date = new Date('2024-05-07T07:00:00.000Z'); + it('sets the end date if its on the release day before switchover', () => { + const date = new Date('2024-05-07T07:00:00.000+00:00'); + const matchers = getAllScheduleMatchingGroups(date); + expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-05-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); + }); + + it('sets the end date if its on the release day after switchover', () => { + const date = new Date('2024-05-07T09:00:00.000+00:00'); const matchers = getAllScheduleMatchingGroups(date); expect(matchers.backgrounds.end).to.eql(moment.utc(`2024-06-07T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); }); @@ -129,6 +142,42 @@ describe('Content Schedule', () => { expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); }); + it('uses correct date for first hours of the month', () => { + // if the date is checked before CONTENT_SWITCHOVER_TIME_OFFSET, + // it should be considered the previous month + const date = new Date('2024-05-01T02:00:00.000Z'); + const matchers = getAllScheduleMatchingGroups(date); + expect(matchers.petQuests.items).to.contain('snake'); + expect(matchers.petQuests.items).to.not.contain('horse'); + expect(matchers.timeTravelers.match('202304'), '202304').to.be.true; + expect(matchers.timeTravelers.match('202404'), '202404').to.be.false; + expect(matchers.timeTravelers.match('202305'), '202305').to.be.false; + }); + + it('uses correct date after switchover time', () => { + // if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET, + // it should be considered the current + const date = new Date('2024-05-01T09:00:00.000Z'); + const matchers = getAllScheduleMatchingGroups(date); + expect(matchers.petQuests.items).to.contain('snake'); + expect(matchers.petQuests.items).to.not.contain('horse'); + expect(matchers.timeTravelers.match('202304'), '202304').to.be.false; + expect(matchers.timeTravelers.match('202305'), '202305').to.be.true; + expect(matchers.timeTravelers.match('202405'), '202405').to.be.false; + }); + + it('uses UTC timezone', () => { + // if the date is checked after CONTENT_SWITCHOVER_TIME_OFFSET, + // it should be considered the current + clock = sinon.useFakeTimers(new Date('2024-05-01T05:00:00.000-04:00')); + const matchers = getAllScheduleMatchingGroups(); + expect(matchers.petQuests.items).to.contain('snake'); + expect(matchers.petQuests.items).to.not.contain('horse'); + expect(matchers.timeTravelers.match('202304'), '202304').to.be.false; + expect(matchers.timeTravelers.match('202305'), '202305').to.be.true; + expect(matchers.timeTravelers.match('202405'), '202405').to.be.false; + }); + it('contains content for repeating events', () => { const date = new Date('2024-04-15'); const matchers = getAllScheduleMatchingGroups(date); diff --git a/test/content/time-travelers.test.js b/test/content/time-travelers.test.js index 83dc40dd38..9b4e132537 100644 --- a/test/content/time-travelers.test.js +++ b/test/content/time-travelers.test.js @@ -45,7 +45,7 @@ describe('time-travelers store', () => { describe('on may 1st', () => { beforeEach(() => { - date = new Date('2024-05-01'); + date = new Date('2024-05-01T09:00:00.000Z'); }); it('returns the correct gear', () => { const items = timeTravelers.timeTravelerStore(user, date); diff --git a/website/common/script/content/constants/schedule.js b/website/common/script/content/constants/schedule.js index 2fada3f5bc..e1cffb47fc 100644 --- a/website/common/script/content/constants/schedule.js +++ b/website/common/script/content/constants/schedule.js @@ -809,7 +809,9 @@ function getMonth (date) { if (date === undefined) { return 0; } - return date instanceof moment ? date.month() : date.getMonth(); + const checkDate = new Date(date.getTime()); + checkDate.setHours(checkDate.getHours() - SWITCHOVER_TIME); + return checkDate.getMonth(); } function getGalaIndex (date) { @@ -892,7 +894,7 @@ function makeEndDate (checkedDate, matcher) { end.year(checkedDate.getFullYear() + 1); } end.month(matcher.endMonth); - } else if (end.date() <= checkedDate.getDate()) { + } else if (end.valueOf() <= checkedDate.getTime()) { end = moment(end).add(1, 'months'); } return end.toDate(); @@ -904,7 +906,7 @@ export function clearCachedMatchers () { } export function getAllScheduleMatchingGroups (date) { - const checkedDate = date || new Date(); + const checkedDate = date || moment.utc().toDate(); if (cacheDate !== null && (getDay(checkedDate) !== getDay(cacheDate) || getMonth(checkedDate) !== getMonth(cacheDate))) { // Clear cached matchers, since they are old @@ -914,9 +916,14 @@ export function getAllScheduleMatchingGroups (date) { // No matchers exist, make new ones cacheDate = new Date(); cachedScheduleMatchers = {}; + // subtract switchover time for the matcher classes, but + // NOT to decide which matchers to assemble. + // assembly uses getDate and getMonth which already adjust for switchover time + const adjustedDate = new Date(checkedDate.getTime()); + adjustedDate.setHours(adjustedDate.getHours() - SWITCHOVER_TIME); assembleScheduledMatchers(checkedDate).forEach(matcher => { if (!cachedScheduleMatchers[matcher.type]) { - cachedScheduleMatchers[matcher.type] = makeMatcherClass(checkedDate); + cachedScheduleMatchers[matcher.type] = makeMatcherClass(adjustedDate); } cachedScheduleMatchers[matcher.type].end = makeEndDate(checkedDate, matcher); if (matcher.matcher instanceof Function) {