Skip to content

Commit 05c3838

Browse files
arbrandesclaude
andcommitted
fix: resolve type errors surfaced by Verawood bumps
- Replace removed `User` export with local AuthenticatedUser type - Update APP_INIT_ERROR subscriber for (type, data) callback signature - Cast getConfig/getAuthenticatedHttpClient mocks to jest.Mock - Use real IntlShape via renderHook+useIntl in sidebar-social tests - Coerce possibly-undefined values passed to String.replace - Add config to AppContext.Provider value in useMenuItems test Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c72b60f commit 05c3838

9 files changed

Lines changed: 58 additions & 57 deletions

File tree

src/catalog/CatalogPage.test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jest.mock('@edx/frontend-platform/auth', () => ({
3131

3232
const mockUseCourseListSearch = useCourseListSearch as jest.Mock;
3333
const mockGetConfig = getConfig as jest.Mock;
34+
const mockGetAuthenticatedHttpClient = getAuthenticatedHttpClient as jest.Mock;
3435

3536
const actualUseCourseListSearch = jest
3637
.requireActual('../data/course-list-search/hooks').useCourseListSearch;
@@ -1472,7 +1473,7 @@ describe('CatalogPage search integration', () => {
14721473
beforeEach(() => {
14731474
mockPost = jest.fn().mockResolvedValue({ data: mockCourseListSearchResponse });
14741475

1475-
getAuthenticatedHttpClient.mockReturnValue({ post: mockPost });
1476+
mockGetAuthenticatedHttpClient.mockReturnValue({ post: mockPost });
14761477

14771478
mockUseCourseListSearch.mockImplementation(params => actualUseCourseListSearch(params));
14781479

@@ -1483,7 +1484,7 @@ describe('CatalogPage search integration', () => {
14831484
});
14841485

14851486
afterEach(() => {
1486-
getAuthenticatedHttpClient.mockReset();
1487+
mockGetAuthenticatedHttpClient.mockReset();
14871488
mockUseCourseListSearch.mockReset();
14881489
mockGetConfig.mockReset();
14891490
});

src/catalog/__tests__/utils.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ describe('utils', () => {
1111
const intl = createIntl({
1212
locale: 'en',
1313
messages: {
14-
organizations: messages.organizations,
15-
languages: messages.languages,
16-
courseTypes: messages.courseTypes,
14+
organizations: messages.organizations.defaultMessage,
15+
languages: messages.languages.defaultMessage,
16+
courseTypes: messages.courseTypes.defaultMessage,
1717
},
1818
});
1919

src/course-about/course-intro/hooks/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import { User } from '@edx/frontend-platform/auth';
2-
31
import type { CourseAboutDataPartial } from '../../types';
42

53
export interface UseEnrollmentActionsTypes {
64
courseId: string;
75
ecommerceCheckoutLink?: string | null;
86
}
97

8+
export interface AuthenticatedUser {
9+
username: string;
10+
}
11+
1012
export interface UseEnrollmentStatusTypes {
1113
courseAboutData: CourseAboutDataPartial;
12-
authenticatedUser: User;
14+
authenticatedUser: AuthenticatedUser | null;
1315
enrollmentError: string | null;
1416
isEnrollmentPending: boolean;
1517
handleChangeEnrollment: () => void;

src/course-about/course-sidebar/sidebar-social/__tests__/utils.test.ts

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { createElement, type ReactNode } from 'react';
12
import { getConfig } from '@edx/frontend-platform';
3+
import { IntlProvider, useIntl } from '@edx/frontend-platform/i18n';
24

5+
import { renderHook } from '@src/setupTest';
36
import { mockCourseAboutResponse } from '@src/__mocks__';
47
import {
58
getTwitterShareUrl,
@@ -26,18 +29,11 @@ Object.defineProperty(window, 'location', {
2629
});
2730

2831
describe('Social Sharing Utils', () => {
29-
const mockIntl = {
30-
formatMessage: jest.fn().mockImplementation((message, values) => {
31-
if (!values) {
32-
return message.defaultMessage;
33-
}
34-
35-
return Object.entries(values).reduce(
36-
(str, [key, value]) => str.replace(`{${key}}`, value || ''),
37-
message.defaultMessage,
38-
);
39-
}),
40-
};
32+
const wrapper = ({ children }: { children: ReactNode }) => (
33+
createElement(IntlProvider, { locale: 'en', messages: {} }, children)
34+
);
35+
const intl = renderHook(() => useIntl(), { wrapper }).result.current;
36+
let formatMessageSpy: jest.SpyInstance;
4137

4238
const createCourseData = (overrides = {}) => ({
4339
...mockCourseAboutResponse,
@@ -46,6 +42,7 @@ describe('Social Sharing Utils', () => {
4642

4743
beforeEach(() => {
4844
jest.clearAllMocks();
45+
formatMessageSpy = jest.spyOn(intl, 'formatMessage');
4946
window.location.href = mockLocation.href;
5047
});
5148

@@ -56,7 +53,7 @@ describe('Social Sharing Utils', () => {
5653
name: 'Introduction to Computer Science',
5754
});
5855

59-
const result = getTwitterShareUrl(courseData, mockIntl);
56+
const result = getTwitterShareUrl(courseData, intl);
6057

6158
expect(result).toContain('https://twitter.com/intent/tweet?text=');
6259
expect(result).toContain(encodeURIComponent(courseData.displayNumberWithDefault));
@@ -70,9 +67,9 @@ describe('Social Sharing Utils', () => {
7067
displayNumberWithDefault: 'CS101',
7168
name: 'Test Course',
7269
});
73-
getTwitterShareUrl(courseData, mockIntl);
70+
getTwitterShareUrl(courseData, intl);
7471

75-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(
72+
expect(formatMessageSpy).toHaveBeenCalledWith(
7673
messages.socialSharingTwitterText,
7774
{
7875
courseNumber: courseData.displayNumberWithDefault,
@@ -91,7 +88,7 @@ describe('Social Sharing Utils', () => {
9188
name: 'Advanced Mathematics',
9289
});
9390

94-
const result = getEmailShareUrl(courseData, mockIntl);
91+
const result = getEmailShareUrl(courseData, intl);
9592

9693
expect(result).toContain('mailto:?subject=');
9794
expect(result).toContain('&body=');
@@ -106,13 +103,13 @@ describe('Social Sharing Utils', () => {
106103
displayNumberWithDefault: 'MATH201',
107104
name: 'Advanced Mathematics',
108105
});
109-
getEmailShareUrl(courseData, mockIntl);
106+
getEmailShareUrl(courseData, intl);
110107

111-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(
108+
expect(formatMessageSpy).toHaveBeenCalledWith(
112109
messages.socialSharingEmailSubject,
113110
{ siteName: getConfig().SITE_NAME },
114111
);
115-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(
112+
expect(formatMessageSpy).toHaveBeenCalledWith(
116113
messages.socialSharingEmailBody,
117114
{
118115
courseNumber: courseData.displayNumberWithDefault,
@@ -129,8 +126,8 @@ describe('Social Sharing Utils', () => {
129126
name: 'Test Course',
130127
});
131128

132-
const twitterUrl = getTwitterShareUrl(courseData, mockIntl);
133-
const emailUrl = getEmailShareUrl(courseData, mockIntl);
129+
const twitterUrl = getTwitterShareUrl(courseData, intl);
130+
const emailUrl = getEmailShareUrl(courseData, intl);
134131

135132
expect(twitterUrl).toContain(encodeURIComponent(
136133
messages.socialSharingTwitterText.defaultMessage
@@ -168,7 +165,7 @@ describe('Social Sharing Utils', () => {
168165

169166
describe('getSocialLinks', () => {
170167
it('returns array of social link configurations', () => {
171-
const result = getSocialLinks(mockIntl);
168+
const result = getSocialLinks(intl);
172169

173170
expect(result).toHaveLength(3);
174171
expect(result[0].id).toBe('twitter');
@@ -177,19 +174,19 @@ describe('Social Sharing Utils', () => {
177174
});
178175

179176
it('includes correct icons for each social platform', () => {
180-
const result = getSocialLinks(mockIntl);
177+
const result = getSocialLinks(intl);
181178

182179
expect(result[0].icon).toBeDefined();
183180
expect(result[1].icon).toBeDefined();
184181
expect(result[2].icon).toBeDefined();
185182
});
186183

187184
it('includes screen reader text for each platform', () => {
188-
getSocialLinks(mockIntl);
185+
getSocialLinks(intl);
189186

190-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(messages.socialSharingTwitter);
191-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(messages.socialSharingFacebook);
192-
expect(mockIntl.formatMessage).toHaveBeenCalledWith(messages.socialSharingEmail);
187+
expect(formatMessageSpy).toHaveBeenCalledWith(messages.socialSharingTwitter);
188+
expect(formatMessageSpy).toHaveBeenCalledWith(messages.socialSharingFacebook);
189+
expect(formatMessageSpy).toHaveBeenCalledWith(messages.socialSharingEmail);
193190
});
194191
});
195192

@@ -201,8 +198,8 @@ describe('Social Sharing Utils', () => {
201198
});
202199

203200
expect(() => {
204-
getTwitterShareUrl(courseData, mockIntl);
205-
getEmailShareUrl(courseData, mockIntl);
201+
getTwitterShareUrl(courseData, intl);
202+
getEmailShareUrl(courseData, intl);
206203
}).not.toThrow();
207204
});
208205

@@ -212,8 +209,8 @@ describe('Social Sharing Utils', () => {
212209
name: '',
213210
});
214211

215-
const twitterUrl = getTwitterShareUrl(courseData, mockIntl);
216-
const emailUrl = getEmailShareUrl(courseData, mockIntl);
212+
const twitterUrl = getTwitterShareUrl(courseData, intl);
213+
const emailUrl = getEmailShareUrl(courseData, intl);
217214

218215
expect(twitterUrl).toContain('https://twitter.com/intent/tweet?text=');
219216
expect(emailUrl).toContain('mailto:?subject=');
@@ -226,8 +223,8 @@ describe('Social Sharing Utils', () => {
226223
});
227224

228225
const courseData = createCourseData();
229-
const twitterUrl = getTwitterShareUrl(courseData, mockIntl);
230-
const emailUrl = getEmailShareUrl(courseData, mockIntl);
226+
const twitterUrl = getTwitterShareUrl(courseData, intl);
227+
const emailUrl = getEmailShareUrl(courseData, intl);
231228

232229
expect(twitterUrl).toContain('https://twitter.com/intent/tweet?text=');
233230
expect(emailUrl).toContain('mailto:?subject=');
@@ -241,8 +238,8 @@ describe('Social Sharing Utils', () => {
241238
name: 'Programming & Algorithms: "Advanced" Topics',
242239
});
243240

244-
const twitterUrl = getTwitterShareUrl(courseData, mockIntl);
245-
const emailUrl = getEmailShareUrl(courseData, mockIntl);
241+
const twitterUrl = getTwitterShareUrl(courseData, intl);
242+
const emailUrl = getEmailShareUrl(courseData, intl);
246243

247244
expect(twitterUrl).toContain(encodeURIComponent(courseData.displayNumberWithDefault));
248245
expect(twitterUrl).toContain(encodeURIComponent(courseData.name));

src/generic/course-card/CourseCard.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('CourseCard', () => {
3232
renderComponent();
3333

3434
expect(screen.getByText(
35-
messages.startDate.defaultMessage.replace('{startDate}', mockCourseResponse.data.advertisedStart),
35+
messages.startDate.defaultMessage.replace('{startDate}', mockCourseResponse.data.advertisedStart ?? ''),
3636
)).toBeInTheDocument();
3737
});
3838

src/header/hooks/useMenuItems.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode } from 'react';
1+
import React, { ReactNode } from 'react';
22
import { useLocation } from 'react-router-dom';
33
import { AppContext } from '@edx/frontend-platform/react';
44
import { getConfig } from '@edx/frontend-platform';
@@ -26,11 +26,11 @@ jest.mock('@edx/frontend-platform', () => ({
2626
}));
2727

2828
const renderWithAppContext = (authenticatedUser: Pick<AuthenticatedUserTypes, 'username'> | null) => {
29-
const contextValue = { authenticatedUser };
29+
const contextValue = { authenticatedUser, config: DEFAULT_CONFIG };
3030

3131
return renderHook(() => useMenuItems(), {
3232
wrapper: ({ children }: { children: ReactNode }) => (
33-
<AppContext.Provider value={contextValue}>
33+
<AppContext.Provider value={contextValue as unknown as React.ContextType<typeof AppContext>}>
3434
<IntlProvider locale="en">
3535
{children}
3636
</IntlProvider>

src/home/HomePage.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('HomePage', () => {
4747
render(<HomePage />);
4848

4949
expect(screen.getByText(
50-
messages.title.defaultMessage.replace('{siteName}', process.env.SITE_NAME),
50+
messages.title.defaultMessage.replace('{siteName}', process.env.SITE_NAME ?? ''),
5151
)).toBeInTheDocument();
5252
expect(screen.getByText(messages.subtitle.defaultMessage)).toBeInTheDocument();
5353
expect(screen.getByRole('button', { name: messages.videoButton.defaultMessage })).toBeInTheDocument();
@@ -97,7 +97,7 @@ describe('HomePage', () => {
9797
});
9898

9999
it('should not pass enableCourseDiscovery to HomeBanner', () => {
100-
getConfig.mockReturnValue({
100+
(getConfig as jest.Mock).mockReturnValue({
101101
ENABLE_COURSE_DISCOVERY: !process.env.ENABLE_COURSE_DISCOVERY,
102102
});
103103

src/home/components/courses-list/CoursesList.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('<CoursesList />', () => {
5555
data: null,
5656
});
5757

58-
getConfig.mockReturnValue({
58+
(getConfig as jest.Mock).mockReturnValue({
5959
HOMEPAGE_COURSE_MAX: 2,
6060
});
6161

@@ -74,7 +74,7 @@ describe('<CoursesList />', () => {
7474
data: null,
7575
});
7676

77-
getConfig.mockReturnValue({
77+
(getConfig as jest.Mock).mockReturnValue({
7878
HOMEPAGE_COURSE_MAX: undefined,
7979
});
8080

@@ -127,7 +127,7 @@ describe('<CoursesList />', () => {
127127
data: mockCourseListSearchResponse,
128128
});
129129

130-
getConfig.mockReturnValue({
130+
(getConfig as jest.Mock).mockReturnValue({
131131
HOMEPAGE_COURSE_MAX: 1,
132132
ENABLE_COURSE_SORTING_BY_START_DATE: false,
133133
NON_BROWSABLE_COURSES: false,
@@ -148,7 +148,7 @@ describe('<CoursesList />', () => {
148148
data: mockCourseListSearchResponse,
149149
});
150150

151-
getConfig.mockReturnValue({
151+
(getConfig as jest.Mock).mockReturnValue({
152152
HOMEPAGE_COURSE_MAX: 3,
153153
ENABLE_COURSE_SORTING_BY_START_DATE: false,
154154
NON_BROWSABLE_COURSES: false,
@@ -165,7 +165,7 @@ describe('<CoursesList />', () => {
165165
data: null,
166166
});
167167

168-
getConfig.mockReturnValue({
168+
(getConfig as jest.Mock).mockReturnValue({
169169
INFO_EMAIL: process.env.INFO_EMAIL,
170170
});
171171

@@ -185,7 +185,7 @@ describe('<CoursesList />', () => {
185185
data: mockCourseListSearchResponse,
186186
});
187187

188-
getConfig.mockReturnValue({
188+
(getConfig as jest.Mock).mockReturnValue({
189189
NON_BROWSABLE_COURSES: true,
190190
});
191191

src/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ subscribe(APP_READY, () => {
1919
root.render(<App />);
2020
});
2121

22-
subscribe(APP_INIT_ERROR, (error: { message: any; }) => {
23-
root.render(<ErrorPage message={error.message} />);
22+
subscribe(APP_INIT_ERROR, (_type, data) => {
23+
const { message } = data as { message: string };
24+
root.render(<ErrorPage message={message} />);
2425
});
2526

2627
initialize({

0 commit comments

Comments
 (0)