Files
site-builder/craft/src/state/MobileChromeContext.test.tsx
T

153 lines
4.3 KiB
TypeScript
Raw Normal View History

import { describe, test, expect } from 'vitest';
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { act } from 'react-dom/test-utils';
import { MobileChromeProvider, useMobileChrome, MobileSheetTab } from './MobileChromeContext';
import { SitesmithProvider, useSitesmithModal } from './SitesmithContext';
/**
* Mobile-A2 (review item 5): covers the two invariants item 3 depends on --
* (1) only one sheet is ever open ("tab-bar-always-wins", Phase A's existing
* behavior, now backed by the shared context instead of a private
* useState), and (2) opening a sheet closes any open full-screen modal
* (Templates / Head Code / Sitesmith), so a sheet is never shown stacked
* underneath one.
*
* DOM-harness pattern mirrors PageContext.pure-updaters.test.tsx: bare
* createRoot + act, a Consumer component that stashes the hook result on an
* outer variable so assertions can run outside of render.
*/
let container: HTMLDivElement;
let root: Root;
function render(ui: React.ReactElement) {
container = document.createElement('div');
document.body.appendChild(container);
act(() => {
root = createRoot(container);
root.render(ui);
});
}
function unmount() {
act(() => {
root.unmount();
});
container.remove();
}
let chrome: ReturnType<typeof useMobileChrome> | null = null;
let sitesmith: ReturnType<typeof useSitesmithModal> | null = null;
const Consumer: React.FC = () => {
chrome = useMobileChrome();
sitesmith = useSitesmithModal();
return null;
};
function renderProviders() {
render(
<SitesmithProvider>
<MobileChromeProvider>
<Consumer />
</MobileChromeProvider>
</SitesmithProvider>,
);
}
describe('MobileChromeContext', () => {
test('only one sheet is ever open at a time', () => {
renderProviders();
act(() => chrome!.openSheet('blocks'));
expect(chrome!.activeSheet).toBe('blocks');
// Opening a different sheet replaces the previous one -- never both.
act(() => chrome!.openSheet('styles'));
expect(chrome!.activeSheet).toBe('styles');
act(() => chrome!.closeSheet());
expect(chrome!.activeSheet).toBeNull();
unmount();
});
test('openSheet is idempotent-safe: opening the same tab twice stays open (caller owns toggle-to-close)', () => {
renderProviders();
act(() => chrome!.openSheet('pages'));
expect(chrome!.activeSheet).toBe('pages');
act(() => chrome!.openSheet('pages'));
expect(chrome!.activeSheet).toBe('pages');
unmount();
});
test('openSheet closes an open Templates modal', () => {
renderProviders();
act(() => chrome!.setTemplateModalOpen(true));
expect(chrome!.templateModalOpen).toBe(true);
act(() => chrome!.openSheet('blocks'));
expect(chrome!.templateModalOpen).toBe(false);
expect(chrome!.activeSheet).toBe('blocks');
unmount();
});
test('openSheet closes an open Head Code modal', () => {
renderProviders();
act(() => chrome!.setHeadCodeModalOpen(true));
expect(chrome!.headCodeModalOpen).toBe(true);
act(() => chrome!.openSheet('assets'));
expect(chrome!.headCodeModalOpen).toBe(false);
expect(chrome!.activeSheet).toBe('assets');
unmount();
});
test('openSheet closes an open Sitesmith modal', () => {
renderProviders();
act(() => sitesmith!.open());
expect(sitesmith!.isOpen).toBe(true);
act(() => chrome!.openSheet('layers'));
// sitesmith is a stale snapshot from before the re-render the `act`
// above triggered; re-read via the live ref after settling.
expect(sitesmith!.isOpen).toBe(false);
expect(chrome!.activeSheet).toBe('layers');
unmount();
});
test('closeSheet does not disturb modal state', () => {
renderProviders();
act(() => chrome!.openSheet('styles'));
act(() => chrome!.closeSheet());
expect(chrome!.activeSheet).toBeNull();
expect(chrome!.templateModalOpen).toBe(false);
expect(chrome!.headCodeModalOpen).toBe(false);
unmount();
});
test('sheet tabs are exactly the five mobile panels', () => {
const tabs: MobileSheetTab[] = ['blocks', 'pages', 'layers', 'assets', 'styles'];
renderProviders();
tabs.forEach((tab) => {
act(() => chrome!.openSheet(tab));
expect(chrome!.activeSheet).toBe(tab);
});
unmount();
});
});