Files
frontend-app-authoring/src/course-unit/hooks.test.jsx
Navin Karkera a43027b328 feat: library unit page skeleton [FC-0083] (#1779)
* View a unit page, which has its own URL
* Components appear within a unit as full previews. Their top bar shows type icon and title on the left, and draft status (if any), tag count, overflow menu, and drag handle on the right.
* Components have an overflow menu within a unit
* Components can be selected within a unit
* When components are selected, the standard component sidebar appears. The preview tab is hidden, since component previews are visible in the main content area.
* Components within a unit full-page view have hover and selected states
* Unit sidebar preview.
* Frontend implementation Drag-n-drop components to reorder them in unit.
2025-04-11 13:50:40 -05:00

182 lines
5.9 KiB
JavaScript

import React from 'react';
import { act, renderHook } from '@testing-library/react';
import { useScrollToLastPosition, useLayoutGrid } from './hooks';
import { iframeMessageTypes } from '../constants';
jest.useFakeTimers();
describe('useLayoutGrid', () => {
it('returns fullWidth layout when isUnitLibraryType is true', () => {
const { result } = renderHook(() => useLayoutGrid('someCategory', true));
expect(result.current).toEqual({
lg: [{ span: 12 }, { span: 0 }],
md: [{ span: 12 }, { span: 0 }],
sm: [{ span: 12 }, { span: 0 }],
xs: [{ span: 12 }, { span: 0 }],
xl: [{ span: 12 }, { span: 0 }],
});
});
it('returns default layout when isUnitLibraryType is false', () => {
const { result } = renderHook(() => useLayoutGrid('someCategory', false));
expect(result.current).toEqual({
lg: [{ span: 8 }, { span: 4 }],
md: [{ span: 8 }, { span: 4 }],
sm: [{ span: 8 }, { span: 3 }],
xs: [{ span: 9 }, { span: 3 }],
xl: [{ span: 9 }, { span: 3 }],
});
});
it('does not recompute layout if unitCategory remains the same', () => {
const { result, rerender } = renderHook(
({ unitCategory, isUnitLibraryType }) => useLayoutGrid(unitCategory, isUnitLibraryType),
{ initialProps: { unitCategory: 'category1', isUnitLibraryType: false } },
);
const firstResult = result.current;
rerender({ unitCategory: 'category1', isUnitLibraryType: false });
expect(result.current).toBe(firstResult);
});
it('recomputes layout when unitCategory changes', () => {
const { result, rerender } = renderHook(
({ unitCategory, isUnitLibraryType }) => useLayoutGrid(unitCategory, isUnitLibraryType),
{ initialProps: { unitCategory: 'category1', isUnitLibraryType: false } },
);
const firstResult = result.current;
rerender({ unitCategory: 'category2', isUnitLibraryType: false });
expect(result.current).not.toBe(firstResult);
});
});
describe('useScrollToLastPosition', () => {
const storageKey = 'createXBlockLastYPosition';
let scrollToSpy;
let clearTimeoutSpy;
let setTimeoutSpy;
let setStateSpy;
beforeEach(() => {
localStorage.clear();
scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
setTimeoutSpy = jest.spyOn(global, 'setTimeout');
setStateSpy = jest.spyOn(React, 'useState');
});
afterEach(() => {
jest.clearAllTimers();
scrollToSpy.mockRestore();
clearTimeoutSpy.mockRestore();
setTimeoutSpy.mockRestore();
setStateSpy.mockRestore();
});
it('should not add event listener if no lastYPosition is in localStorage', () => {
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
renderHook(() => useScrollToLastPosition(storageKey));
expect(addEventListenerSpy).not.toHaveBeenCalledWith('message', expect.any(Function));
addEventListenerSpy.mockRestore();
});
it('should add event listener if lastYPosition exists in localStorage', () => {
localStorage.setItem(storageKey, '500');
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
renderHook(() => useScrollToLastPosition(storageKey));
expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
addEventListenerSpy.mockRestore();
});
it('should scroll to saved position on message event', () => {
localStorage.setItem(storageKey, '500');
const { unmount } = renderHook(() => useScrollToLastPosition(storageKey));
act(() => {
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
jest.advanceTimersByTime(1000);
});
expect(window.scrollTo).toHaveBeenCalledWith({ top: 500, behavior: 'smooth' });
expect(localStorage.getItem(storageKey)).toBeNull();
unmount();
});
it('should clear timeout and remove event listener on unmount', () => {
localStorage.setItem(storageKey, '500');
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
const { unmount } = renderHook(() => useScrollToLastPosition(storageKey));
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
});
it('should clear previous timeout before setting a new one on multiple message events', () => {
localStorage.setItem(storageKey, '500');
renderHook(() => useScrollToLastPosition(storageKey));
act(() => {
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
});
expect(clearTimeoutSpy).toHaveBeenCalled();
expect(setTimeoutSpy).toHaveBeenCalledTimes(2);
});
it('should not scroll if multiple message events prevent execution of setTimeout callback', () => {
localStorage.setItem(storageKey, '500');
renderHook(() => useScrollToLastPosition(storageKey));
act(() => {
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
jest.advanceTimersByTime(500);
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
});
expect(window.scrollTo).not.toHaveBeenCalled();
});
it('should scroll only after the final timeout', () => {
localStorage.setItem(storageKey, '500');
renderHook(() => useScrollToLastPosition(storageKey));
act(() => {
window.dispatchEvent(new MessageEvent('message', { data: { type: iframeMessageTypes.resize } }));
jest.advanceTimersByTime(1000);
});
expect(window.scrollTo).toHaveBeenCalledWith({ top: 500, behavior: 'smooth' });
});
it('should NOT set hasLastPosition to false if lastYPosition exists and is valid', () => {
localStorage.setItem(storageKey, '500');
renderHook(() => useScrollToLastPosition(storageKey));
expect(setStateSpy).not.toHaveBeenCalledWith(false);
});
});