// The FP version works better for `format()` /* eslint-disable import/no-duplicates */ import { endOfDay, subDays } from 'date-fns'; import { format } from 'date-fns/fp'; /* eslint-enable import/no-duplicates */ import dashboardRoutes from '#src/routes/dashboard.js'; import { createRequester } from '#src/utils/test-utils.js'; const totalUserCount = 1000; const countUsers = jest.fn(async () => ({ count: totalUserCount })); const getDailyNewUserCountsByTimeInterval = jest.fn( async (startTimeExclusive: number, endTimeInclusive: number) => mockDailyNewUserCounts ); const formatToQueryDate = format('yyyy-MM-dd'); jest.mock('#src/queries/user.js', () => ({ countUsers: async () => countUsers(), getDailyNewUserCountsByTimeInterval: async ( startTimeExclusive: number, endTimeInclusive: number ) => getDailyNewUserCountsByTimeInterval(startTimeExclusive, endTimeInclusive), })); const mockDailyNewUserCounts = [ { date: '2022-05-01', count: 1 }, { date: '2022-05-02', count: 2 }, { date: '2022-05-03', count: 3 }, { date: '2022-05-06', count: 6 }, { date: '2022-05-07', count: 7 }, { date: '2022-05-08', count: 8 }, { date: '2022-05-09', count: 9 }, { date: '2022-05-10', count: 10 }, { date: '2022-05-13', count: 13 }, { date: '2022-05-14', count: 14 }, ]; const mockDailyActiveUserCounts = [ { date: '2022-05-01', count: 501 }, { date: '2022-05-23', count: 523 }, { date: '2022-05-29', count: 529 }, { date: '2022-05-30', count: 530 }, ]; const mockActiveUserCount = 1000; const getDailyActiveUserCountsByTimeInterval = jest.fn( async (startTimeExclusive: number, endTimeInclusive: number) => mockDailyActiveUserCounts ); const countActiveUsersByTimeInterval = jest.fn( async (startTimeExclusive: number, endTimeInclusive: number) => ({ count: mockActiveUserCount }) ); jest.mock('#src/queries/log.js', () => ({ getDailyActiveUserCountsByTimeInterval: async ( startTimeExclusive: number, endTimeInclusive: number ) => getDailyActiveUserCountsByTimeInterval(startTimeExclusive, endTimeInclusive), countActiveUsersByTimeInterval: async (startTimeExclusive: number, endTimeInclusive: number) => countActiveUsersByTimeInterval(startTimeExclusive, endTimeInclusive), })); describe('dashboardRoutes', () => { const logRequest = createRequester({ authedRoutes: dashboardRoutes }); afterEach(() => { jest.clearAllMocks(); }); describe('GET /dashboard/users/total', () => { it('should call countUsers with no parameters', async () => { await logRequest.get('/dashboard/users/total'); expect(countUsers).toHaveBeenCalledWith(); }); it('/dashboard/users/total should return correct response', async () => { const response = await logRequest.get('/dashboard/users/total'); expect(response.status).toEqual(200); expect(response.body).toEqual({ totalUserCount }); }); }); describe('GET /dashboard/users/new', () => { beforeEach(() => { jest.useFakeTimers().setSystemTime(new Date('2022-05-14')); }); it('should call getDailyNewUserCountsByTimeInterval with the time interval (14 days ago 23:59:59.999, today 23:59:59.999]', async () => { await logRequest.get('/dashboard/users/new'); expect(getDailyNewUserCountsByTimeInterval).toHaveBeenCalledWith( subDays(endOfDay(Date.now()), 14).valueOf(), endOfDay(Date.now()).valueOf() ); }); it('should return correct response', async () => { const response = await logRequest.get('/dashboard/users/new'); expect(response.status).toEqual(200); expect(response.body).toEqual({ today: { count: 14, delta: 1, }, last7Days: { count: 54, delta: 35, }, }); }); }); describe('GET /dashboard/users/active', () => { const mockToday = new Date(2022, 4, 30); beforeEach(() => { jest.useFakeTimers().setSystemTime(mockToday); }); it('should fail when the parameter `date` does not match the date regex', async () => { const response = await logRequest.get('/dashboard/users/active?date=2022.5.1'); expect(response.status).toEqual(400); }); it('should call getDailyActiveUserCountsByTimeInterval with the time interval (2022-05-31, 2022-06-30] when the parameter `date` is 2022-06-30', async () => { const targetDate = new Date(2022, 5, 30); await logRequest.get(`/dashboard/users/active?date=${formatToQueryDate(targetDate)}`); expect(getDailyActiveUserCountsByTimeInterval).toHaveBeenCalledWith( endOfDay(new Date(2022, 4, 31)).valueOf(), endOfDay(targetDate).valueOf() ); }); it('should call getDailyActiveUserCountsByTimeInterval with the time interval (30 days ago, tomorrow] when there is no parameter `date`', async () => { await logRequest.get('/dashboard/users/active'); expect(getDailyActiveUserCountsByTimeInterval).toHaveBeenCalledWith( endOfDay(new Date(2022, 3, 30)).valueOf(), endOfDay(mockToday).valueOf() ); }); it('should call countActiveUsersByTimeInterval with correct parameters when the parameter `date` is 2022-06-30', async () => { const targetDate = new Date(2022, 5, 30); await logRequest.get(`/dashboard/users/active?date=${formatToQueryDate(targetDate)}`); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 1, endOfDay(new Date(2022, 5, 16)).valueOf(), endOfDay(new Date(2022, 5, 23)).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 2, endOfDay(new Date(2022, 5, 23)).valueOf(), endOfDay(targetDate).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 3, endOfDay(new Date(2022, 4, 1)).valueOf(), endOfDay(new Date(2022, 4, 31)).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 4, endOfDay(new Date(2022, 4, 31)).valueOf(), endOfDay(targetDate).valueOf() ); }); it('should call countActiveUsersByTimeInterval with correct parameters when there is no parameter `date`', async () => { await logRequest.get('/dashboard/users/active'); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 1, endOfDay(new Date(2022, 4, 16)).valueOf(), endOfDay(new Date(2022, 4, 23)).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 2, endOfDay(new Date(2022, 4, 23)).valueOf(), endOfDay(mockToday).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 3, endOfDay(new Date(2022, 2, 31)).valueOf(), endOfDay(new Date(2022, 3, 30)).valueOf() ); expect(countActiveUsersByTimeInterval).toHaveBeenNthCalledWith( 4, endOfDay(new Date(2022, 3, 30)).valueOf(), endOfDay(mockToday).valueOf() ); }); it('should return correct response', async () => { const response = await logRequest.get('/dashboard/users/active'); expect(response.status).toEqual(200); expect(response.body).toEqual({ dauCurve: [ { date: '2022-05-01', count: 501 }, { date: '2022-05-02', count: 0 }, { date: '2022-05-03', count: 0 }, { date: '2022-05-04', count: 0 }, { date: '2022-05-05', count: 0 }, { date: '2022-05-06', count: 0 }, { date: '2022-05-07', count: 0 }, { date: '2022-05-08', count: 0 }, { date: '2022-05-09', count: 0 }, { date: '2022-05-10', count: 0 }, { date: '2022-05-11', count: 0 }, { date: '2022-05-12', count: 0 }, { date: '2022-05-13', count: 0 }, { date: '2022-05-14', count: 0 }, { date: '2022-05-15', count: 0 }, { date: '2022-05-16', count: 0 }, { date: '2022-05-17', count: 0 }, { date: '2022-05-18', count: 0 }, { date: '2022-05-19', count: 0 }, { date: '2022-05-20', count: 0 }, { date: '2022-05-21', count: 0 }, { date: '2022-05-22', count: 0 }, { date: '2022-05-23', count: 523 }, { date: '2022-05-24', count: 0 }, { date: '2022-05-25', count: 0 }, { date: '2022-05-26', count: 0 }, { date: '2022-05-27', count: 0 }, { date: '2022-05-28', count: 0 }, { date: '2022-05-29', count: 529 }, { date: '2022-05-30', count: 530 }, ], dau: { count: 530, delta: 1 }, wau: { count: 1000, delta: 0 }, mau: { count: 1000, delta: 0 }, }); }); }); });