Skip to content

Commit

Permalink
fix: ensure relevant queries are pre-seeded for video and program pro…
Browse files Browse the repository at this point in the history
…gress routes (#1260)
  • Loading branch information
adamstankiewicz authored Jan 24, 2025
1 parent 14402eb commit cf26017
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/* eslint-disable react/jsx-filename-extension */
import { when } from 'jest-when';
import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import { renderWithRouterProvider } from '../../../utils/tests';
import makeVideosLoader from './videosLoader';
import { ensureAuthenticatedUser } from '../../app/routes/data';
import { extractEnterpriseCustomer, queryVideoDetail } from '../../app/data';
import {
extractEnterpriseCustomer,
queryCourseMetadata,
queryCourseReviews,
queryVideoDetail,
} from '../../app/data';
import { authenticatedUserFactory, enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';

jest.mock('../../app/routes/data', () => ({
Expand All @@ -26,7 +31,7 @@ const mockVideoUUID = 'test-video-uuid';
const mockVideosURL = `/${mockEnterpriseSlug}/videos/${mockVideoUUID}/`;

const mockQueryClient = {
ensureQueryData: jest.fn().mockResolvedValue({}),
ensureQueryData: jest.fn().mockResolvedValue(),
};

describe('videosLoader', () => {
Expand All @@ -40,7 +45,7 @@ describe('videosLoader', () => {
ensureAuthenticatedUser.mockResolvedValue(null);
renderWithRouterProvider(
{
path: '/:enterpriseSlug/videos/:videoUUID/',
path: '/:enterpriseSlug/videos/:videoUUID',
element: <div>hello world</div>,
loader: makeVideosLoader(mockQueryClient),
},
Expand All @@ -54,10 +59,34 @@ describe('videosLoader', () => {
expect(mockQueryClient.ensureQueryData).not.toHaveBeenCalled();
});

it('ensures the requisite video data is resolved', async () => {
it.each([
{ hasVideoDetailData: true },
{ hasVideoDetailData: false },
])('ensures the requisite video data is resolved', async ({ hasVideoDetailData }) => {
const mockCourseKey = 'test-course-key';
const videoDetailQuery = queryVideoDetail(mockVideoUUID, mockEnterpriseId);
const courseMetadataQuery = queryCourseMetadata(mockCourseKey);
const courseReviewsQuery = queryCourseReviews(mockCourseKey);

// Mock the resolved data for the queries
when(mockQueryClient.ensureQueryData).calledWith(
expect.objectContaining({
queryKey: videoDetailQuery.queryKey,
}),
).mockResolvedValue(hasVideoDetailData ? { courseKey: mockCourseKey } : null);
when(mockQueryClient.ensureQueryData).calledWith(
expect.objectContaining(courseMetadataQuery),
).mockResolvedValue({
key: mockCourseKey,
});
when(mockQueryClient.ensureQueryData).calledWith(
expect.objectContaining(courseReviewsQuery),
).mockResolvedValue([]);

// Render the route with videosLoader
renderWithRouterProvider(
{
path: '/:enterpriseSlug/videos/:videoUUID/',
path: '/:enterpriseSlug/videos/:videoUUID',
element: <div>hello world</div>,
loader: makeVideosLoader(mockQueryClient),
},
Expand All @@ -68,7 +97,8 @@ describe('videosLoader', () => {

expect(await screen.findByText('hello world')).toBeInTheDocument();

expect(mockQueryClient.ensureQueryData).toHaveBeenCalledTimes(1);
const expectedQueryCount = hasVideoDetailData ? 3 : 1;
expect(mockQueryClient.ensureQueryData).toHaveBeenCalledTimes(expectedQueryCount);
expect(mockQueryClient.ensureQueryData).toHaveBeenCalledWith(
expect.objectContaining({
queryKey: queryVideoDetail(mockVideoUUID, mockEnterpriseId).queryKey,
Expand Down
16 changes: 14 additions & 2 deletions src/components/microlearning/data/videosLoader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { ensureAuthenticatedUser } from '../../app/routes/data';
import { extractEnterpriseCustomer, queryVideoDetail } from '../../app/data';
import {
extractEnterpriseCustomer,
queryCourseMetadata,
queryCourseReviews,
queryVideoDetail,
} from '../../app/data';

type VideoRouteParams<Key extends string = string> = Types.RouteParams<Key> & {
readonly videoUUID: string;
Expand All @@ -25,7 +30,14 @@ const makeVideosLoader: Types.MakeRouteLoaderFunctionWithQueryClient = function
enterpriseSlug,
});

await queryClient.ensureQueryData(queryVideoDetail(videoUUID, enterpriseCustomer.uuid));
const videoData = await queryClient.ensureQueryData(queryVideoDetail(videoUUID, enterpriseCustomer.uuid));
if (videoData) {
const { courseKey } = videoData;
await Promise.all([
queryClient.ensureQueryData(queryCourseMetadata(courseKey)),
queryClient.ensureQueryData(queryCourseReviews(courseKey)),
]);
}

return null;
};
Expand Down
17 changes: 14 additions & 3 deletions src/components/program-progress/data/programProgressLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { queryLearnerProgramProgressData } from '../../app/data';
import { extractEnterpriseCustomer, queryEnterpriseCourseEnrollments, queryLearnerProgramProgressData } from '../../app/data';
import { ensureAuthenticatedUser } from '../../app/routes/data';

type ProgramProgressRouteParams<Key extends string = string> = Types.RouteParams<Key> & {
Expand All @@ -19,8 +19,19 @@ const makeProgramProgressLoader: Types.MakeRouteLoaderFunctionWithQueryClient =
return null;
}

const { programUUID } = params;
await queryClient.ensureQueryData(queryLearnerProgramProgressData(programUUID));
const { enterpriseSlug, programUUID } = params;

// Extract enterprise customer.
const enterpriseCustomer = await extractEnterpriseCustomer({
queryClient,
authenticatedUser,
enterpriseSlug,
});
const queriesToResolve = [
queryClient.ensureQueryData(queryLearnerProgramProgressData(programUUID)),
queryClient.ensureQueryData(queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid)),
];
await Promise.all(queriesToResolve);

return null;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
/* eslint-disable react/jsx-filename-extension */
import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import { renderWithRouterProvider } from '../../../../utils/tests';
import makeProgramProgressLoader from '../programProgressLoader';
import { ensureAuthenticatedUser } from '../../../app/routes/data';
import { queryLearnerProgramProgressData } from '../../../app/data';
import {
extractEnterpriseCustomer,
queryEnterpriseCourseEnrollments,
queryLearnerProgramProgressData,
} from '../../../app/data';
import { authenticatedUserFactory, enterpriseCustomerFactory } from '../../../app/data/services/data/__factories__';

jest.mock('../../../app/routes/data', () => ({
...jest.requireActual('../../../app/routes/data'),
ensureAuthenticatedUser: jest.fn(),
}));

const mockEnterpriseId = 'test-enterprise-uuid';
jest.mock('../../../app/data', () => ({
...jest.requireActual('../../../app/data'),
extractEnterpriseCustomer: jest.fn(),
}));

const mockAuthenticatedUser = authenticatedUserFactory();
const mockEnterpriseCustomer = enterpriseCustomerFactory();
const mockProgramUUID = 'test-program-uuid';
const mockProgramProgressURL = `/${mockEnterpriseId}/program/${mockProgramUUID}/progress`;
const mockProgramProgressURL = `/${mockEnterpriseCustomer.slug}/program/${mockProgramUUID}/progress`;

const mockQueryClient = {
ensureQueryData: jest.fn().mockResolvedValue({}),
ensureQueryData: jest.fn().mockResolvedValue(),
};

describe('programProgressLoader', () => {
beforeEach(() => {
jest.clearAllMocks();
ensureAuthenticatedUser.mockResolvedValue({ userId: 3 });
ensureAuthenticatedUser.mockResolvedValue(mockAuthenticatedUser);
extractEnterpriseCustomer.mockResolvedValue(mockEnterpriseCustomer);
});

it('does nothing with unauthenticated users', async () => {
Expand Down Expand Up @@ -58,12 +69,18 @@ describe('programProgressLoader', () => {

expect(await screen.findByText('hello world')).toBeInTheDocument();

expect(mockQueryClient.ensureQueryData).toHaveBeenCalledTimes(1);
expect(mockQueryClient.ensureQueryData).toHaveBeenCalledTimes(2);
expect(mockQueryClient.ensureQueryData).toHaveBeenCalledWith(
expect.objectContaining({
queryKey: queryLearnerProgramProgressData(mockProgramUUID).queryKey,
queryFn: expect.any(Function),
}),
);
expect(mockQueryClient.ensureQueryData).toHaveBeenCalledWith(
expect.objectContaining({
queryKey: queryEnterpriseCourseEnrollments(mockEnterpriseCustomer.uuid).queryKey,
queryFn: expect.any(Function),
}),
);
});
});

0 comments on commit cf26017

Please sign in to comment.