Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/optimizer-page/CourseOptimizerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,7 @@ const CourseOptimizerPage = () => {
)}
<Container size="xl" className="mt-4 px-4 export">
<section className="setting-items mb-4">
<Layout
lg={[{ span: 12 }, { span: 0 }]}
>
<Layout>
<Layout.Element>
<article>
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3 p-3">
Expand Down
23 changes: 15 additions & 8 deletions src/optimizer-page/scan-results/BrokenLinkTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fireEvent,
waitFor,
} from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
Expand All @@ -13,6 +14,10 @@ import BrokenLinkTable from './BrokenLinkTable';
import { Unit, Filters } from '../types';
import initializeStore from '../../store';

jest.mock('@src/CourseAuthoringContext', () => ({
useCourseAuthoringContext: jest.fn(() => ({ courseId: 'course-v1:TestX+Test101+2024' })),
}));

let store: any;

const mockOnUpdateLink = jest.fn();
Expand Down Expand Up @@ -82,9 +87,11 @@ const BrokenLinkTableWrapper: React.FC<BrokenLinkTableWrapperProps> = ({

const intlWrapper = (ui: React.ReactElement) =>
render(
<IntlProvider locale="en" messages={{}}>
{ui}
</IntlProvider>,
<MemoryRouter>
<IntlProvider locale="en" messages={{}}>
{ui}
</IntlProvider>
</MemoryRouter>,
);

describe('BrokenLinkTable', () => {
Expand Down Expand Up @@ -709,11 +716,11 @@ describe('BrokenLinkTable', () => {

const goToAnchor = screen.getByText('Test Block');

fireEvent.click(goToAnchor);

await waitFor(() => {
expect(window.open).toHaveBeenCalledWith('https://example.com/block', '_blank');
});
expect(goToAnchor.closest('a')).toHaveAttribute(
'href',
'/course/course-v1:TestX+Test101+2024/container/unit-1#block-1',
);
expect(goToAnchor.closest('a')).toHaveAttribute('target', '_blank');
});

it('BrokenLinkHref anchor opens the href URL', async () => {
Expand Down
34 changes: 23 additions & 11 deletions src/optimizer-page/scan-results/BrokenLinkTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import {
LOCKED,
MANUAL,
} from '../../constants';
import { buildBlockContainerUrl } from '../utils';
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import { Link } from 'react-router-dom';

const BrokenLinkHref: FC<{ href: string; }> = ({ href }) => {
const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
Expand All @@ -40,16 +43,11 @@ const BrokenLinkHref: FC<{ href: string; }> = ({ href }) => {
};

const GoToBlock: FC<{ block: { url: string; displayName?: string; }; }> = ({ block }) => {
const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
window.open(block.url, '_blank');
};

return (
<div className="go-to-block-link-container">
<a href={block.url} onClick={handleClick} className="broken-link" rel="noreferrer">
<Link to={block.url} className="broken-link" rel="noreferrer" target="_blank">
{block.displayName}
</a>
</Link>
</div>
);
};
Expand Down Expand Up @@ -181,6 +179,7 @@ const BrokenLinkTable: FC<BrokenLinkTableProps> = ({
updatedLinkMap = {},
updatedLinkInProgress = {},
}) => {
const { courseId } = useCourseAuthoringContext();
const brokenLinkList = unit.blocks.reduce(
(
acc: TableData,
Expand Down Expand Up @@ -208,7 +207,11 @@ const BrokenLinkTable: FC<BrokenLinkTableProps> = ({
return {
Links: (
<LinksCol
block={{ url: block.url, displayName: block.displayName || 'Go to block', id: block.id }}
block={{
url: buildBlockContainerUrl(courseId, unit.id, block.id),
displayName: block.displayName || 'Go to block',
id: block.id,
}}
href={displayLink}
showIcon={false}
showUpdateButton
Expand Down Expand Up @@ -237,7 +240,10 @@ const BrokenLinkTable: FC<BrokenLinkTableProps> = ({
const blockBrokenLinks = block.brokenLinks.map((link) => ({
Links: (
<LinksCol
block={{ url: block.url, displayName: block.displayName || 'Go to block' }}
block={{
url: buildBlockContainerUrl(courseId, unit.id, block.id),
displayName: block.displayName || 'Go to block',
}}
href={link}
linkType={BROKEN}
/>
Expand All @@ -253,7 +259,10 @@ const BrokenLinkTable: FC<BrokenLinkTableProps> = ({
const blockLockedLinks = block.lockedLinks.map((link) => ({
Links: (
<LinksCol
block={{ url: block.url, displayName: block.displayName || 'Go to block' }}
block={{
url: buildBlockContainerUrl(courseId, unit.id, block.id),
displayName: block.displayName || 'Go to block',
}}
href={link}
linkType={LOCKED}
/>
Expand All @@ -270,7 +279,10 @@ const BrokenLinkTable: FC<BrokenLinkTableProps> = ({
const externalForbiddenLinks = block.externalForbiddenLinks.map((link) => ({
Links: (
<LinksCol
block={{ url: block.url, displayName: block.displayName || 'Go to block' }}
block={{
url: buildBlockContainerUrl(courseId, unit.id, block.id),
displayName: block.displayName || 'Go to block',
}}
href={link}
linkType={MANUAL}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/optimizer-page/scan-results/ScanResults.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ jest.mock('../../data/apiHooks', () => ({
})),
}));

jest.mock('@src/CourseAuthoringContext', () => ({
useCourseAuthoringContext: jest.fn(() => ({ courseId: 'test-course-id' })),
}));

// Mock the thunks
jest.mock('../data/thunks', () => ({
updateSinglePreviousRunLink: jest.fn(() => () => Promise.resolve({ status: 'Succeeded' })),
Expand Down
16 changes: 15 additions & 1 deletion src/optimizer-page/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mockApiResponse } from './mocks/mockApiResponse';
import { countBrokenLinks, isDataEmpty } from './utils';
import { countBrokenLinks, isDataEmpty, buildBlockContainerUrl } from './utils';

describe('countBrokenLinks', () => {
it('should return the count of broken links', () => {
Expand Down Expand Up @@ -104,3 +104,17 @@ describe('isDataEmpty', () => {
expect(isDataEmpty(data)).toBe(false);
});
});

describe('buildBlockContainerUrl', () => {
it('should build a correct internal route for block container', () => {
const courseId = 'course-v1:Test+Course+2024';
const unitId = 'unit123';
const blockId = 'block456';

const result = buildBlockContainerUrl(courseId, unitId, blockId);

expect(result).toBe(
`/course/${courseId}/container/${unitId}#${blockId}`,
);
});
});
9 changes: 8 additions & 1 deletion src/optimizer-page/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
/* eslint-disable import/prefer-default-export */
import { LinkCheckResult } from './types';

export const buildBlockContainerUrl = (
courseId: string,
unitId: string,
blockId: string,
): string => {
return `/course/${courseId}/container/${unitId}#${blockId}`;
};

export const countBrokenLinks = (
data: LinkCheckResult | null,
): {
Expand Down