Skip to content

Commit

Permalink
refactor: added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PKulkoRaccoonGang committed Nov 6, 2024
1 parent ab69d84 commit f6b4000
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 23 deletions.
15 changes: 15 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,18 @@ export const REGEX_RULES = {
specialCharsRule: /^[a-zA-Z0-9_\-.'*~\s]+$/,
noSpaceRule: /^\S*$/,
};

export const SANDBOX_OPTIONS = [
'allow-forms',
'allow-modals',
'allow-popups',
'allow-popups-to-escape-sandbox',
'allow-presentation',
'allow-same-origin',
'allow-scripts',
'allow-top-navigation-by-user-activation',
].join(' ');

export const IFRAME_FEATURE_POLICY = (
'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *'
);
24 changes: 24 additions & 0 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,30 @@ describe('<CourseUnit />', () => {
});
});

it('renders the edit modal when edit message is received and closes it upon receiving the close message', async () => {
const { getByTitle, queryByTitle } = render(<RootWrapper />);

await waitFor(() => {
getByTitle(xblockContainerIframeMessages.xblockIframeTitle.defaultMessage);

simulatePostMessageEvent(messageTypes.editXBlock, {
id: courseVerticalChildrenMock.children[0].block_id,
});

expect(
getByTitle(xblockContainerIframeMessages.editModalIframeTitle.defaultMessage),
).toBeInTheDocument();
});

simulatePostMessageEvent(messageTypes.closeXBlockEditorModal, {});

await waitFor(() => {
expect(
queryByTitle(xblockContainerIframeMessages.editModalIframeTitle.defaultMessage),
).not.toBeInTheDocument();
});
});

it('handles CourseUnit header action buttons', async () => {
const { open } = window;
window.open = jest.fn();
Expand Down
4 changes: 0 additions & 4 deletions src/course-unit/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,3 @@ export const messageTypes = {
closeXBlockEditorModal: 'closeXBlockEditorModal',
saveEditedXBlockData: 'saveEditedXBlockData',
};

export const IFRAME_FEATURE_POLICY = (
'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *'
);
8 changes: 5 additions & 3 deletions src/course-unit/xblock-container-iframe/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import DeleteModal from '../../generic/delete-modal/DeleteModal';
import ConfigureModal from '../../generic/configure-modal/ConfigureModal';
import ModalIframe from '../../generic/modal-iframe/ModalIframe';
import { copyToClipboard } from '../../generic/data/thunks';
import { COURSE_BLOCK_NAMES } from '../../constants';
import { IFRAME_FEATURE_POLICY, messageTypes } from '../constants';
import { COURSE_BLOCK_NAMES, IFRAME_FEATURE_POLICY } from '../../constants';
import { messageTypes } from '../constants';
import { useIframe } from '../context/hooks';
import { useIFrameBehavior } from './hooks';
import messages from './messages';
Expand Down Expand Up @@ -164,6 +164,8 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
const handleMessage = (event: MessageEvent) => {
const { type, payload } = event.data || {};

console.log('========================= IFRAME MSGS =========================', { type, payload });

if (type && messageHandlers[type]) {
messageHandlers[type](payload);
}
Expand Down Expand Up @@ -200,7 +202,7 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
<>
{showLegacyEditModal && (
<ModalIframe
title="Xblock Edit Modal"
title={intl.formatMessage(messages.editModalIframeTitle)}
src={editXblockModalUrl}
/>
)}
Expand Down
5 changes: 5 additions & 0 deletions src/course-unit/xblock-container-iframe/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const messages = defineMessages({
id: 'course-authoring.course-unit.xblock.iframe.label',
defaultMessage: '{xblockCount} xBlocks inside the frame',
},
editModalIframeTitle: {
id: 'course-authoring.course-unit.modal.edit.iframe.title',
defaultMessage: 'Xblock Edit Modal',
description: 'Title for the iframe that is used to edit an xblock',
},
});

export default messages;
50 changes: 50 additions & 0 deletions src/generic/modal-iframe/ModalIframe.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ModalIframe from './ModalIframe';

import { IFRAME_FEATURE_POLICY, SANDBOX_OPTIONS } from '../../constants';

describe('ModalIframe', () => {
it('renders correctly with required props', () => {
render(<ModalIframe title="Test Modal" />);

const iframe = screen.getByTitle('Test Modal') as HTMLIFrameElement;

expect(iframe).toBeInTheDocument();
expect(iframe.title).toBe('Test Modal');
expect(iframe.className).toContain('modal-iframe');
expect(iframe.getAttribute('allow')).toBe(IFRAME_FEATURE_POLICY);
expect(iframe.getAttribute('referrerpolicy')).toBe('origin');
expect(iframe.getAttribute('sandbox')).toBe(SANDBOX_OPTIONS);
expect(iframe.getAttribute('frameborder')).toBe('0');
expect(iframe.getAttribute('scrolling')).toBe('no');
});

it('applies custom className', () => {
const { getByTitle } = render(
<ModalIframe title="Test Modal" className="custom-class" />,
);

const iframe = getByTitle('Test Modal') as HTMLIFrameElement;

expect(iframe.className).toContain('modal-iframe');
expect(iframe.className).toContain('custom-class');
});

it('passes additional props to iframe', () => {
const { getByTitle } = render(
<ModalIframe title="Test Modal" src="https://example.com" />,
);

const iframe = getByTitle('Test Modal') as HTMLIFrameElement;

expect(iframe.src).toBe('https://example.com/');
});

it('forwards ref to iframe element', () => {
const ref = React.createRef<HTMLIFrameElement>();
render(<ModalIframe title="Test Modal" ref={ref} />);

expect(ref.current).toBeInstanceOf(HTMLIFrameElement);
});
});
30 changes: 14 additions & 16 deletions src/generic/modal-iframe/ModalIframe.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
import { forwardRef, ForwardedRef, IframeHTMLAttributes } from 'react';
import classNames from 'classnames';

import { IFRAME_FEATURE_POLICY, SANDBOX_OPTIONS } from '../../constants';

interface ModalIframeProps extends IframeHTMLAttributes<HTMLIFrameElement> {
title: string;
className?: string;
labelledBy?: string;
describedBy?: string;
}

const SANDBOX_OPTIONS = [
'allow-forms',
'allow-modals',
'allow-popups',
'allow-popups-to-escape-sandbox',
'allow-presentation',
'allow-same-origin',
'allow-scripts',
'allow-top-navigation-by-user-activation',
].join(' ');

export const IFRAME_FEATURE_POLICY = (
'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *'
);

const ModalIframe = forwardRef<HTMLIFrameElement, ModalIframeProps>(
({ title, className, ...props }, ref: ForwardedRef<HTMLIFrameElement>) => (
({
title, className, labelledBy, describedBy, ...props
}, ref: ForwardedRef<HTMLIFrameElement>) => (
<iframe
title={title}
className={classNames('modal-iframe', className)}
data-testid="modal-iframe"
allow={IFRAME_FEATURE_POLICY}
referrerPolicy="origin"
frameBorder="0"
scrolling="no"
ref={ref}
sandbox={SANDBOX_OPTIONS}
aria-modal="true"
role="dialog"
aria-labelledby={labelledBy}
aria-describedby={describedBy}
{...props}
/>
),
);

ModalIframe.defaultProps = {
className: 'modal-iframe',
labelledBy: 'modal-title',
describedBy: 'modal-description',
};

export default ModalIframe;

0 comments on commit f6b4000

Please sign in to comment.