import { describe, test, expect } from 'vitest';
import React from 'react';
import { renderEditorHarness } from '../../test-utils/editorHarness';
import { EditorConfigProvider } from '../../state/EditorConfigContext';
import { PageProvider, usePages } from '../../state/PageContext';
import { SiteDesignProvider, useSiteDesign, DEFAULT_SITE_DESIGN } from '../../state/SiteDesignContext';
import { SiteDesignPanel } from './SiteDesignPanel';
/** React tracks a controlled ``'s value via a wrapped native setter --
* a plain `input.value = x` assignment doesn't go through it, so React never
* sees the change and skips onChange. */
function setInputValue(input: HTMLInputElement, value: string) {
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')!.set!;
setter.call(input, value);
input.dispatchEvent(new Event('input', { bubbles: true }));
}
/**
* Real-provider integration test -- NO mocks. Exercises the actual
* `replaceAllPages`/`setHeader`/`setFooter`/`resetToDefaults` implementations,
* which route the component's blank trees through the real
* `treeToCraftState` -> `sanitizeAiTree`/`flattenTreeForCraft` pipeline (see
* `state/PageContext.tsx` and `utils/craft-tree.ts`). This is the check the
* task brief calls out specifically: a tree shape that fails sanitisation
* would silently fall back to an empty canvas (masking a bug) rather than
* throwing, so asserting only on mock call arguments (as
* `SiteDesignPanel.reset.test.tsx` does) would not catch a bad tree shape --
* the mocked assertions there only prove the component PASSED a
* `{type:{resolvedName:'Container'}, nodes:[]}`-shaped object; they can't
* prove that object is actually valid input to the real pipeline.
*/
describe('Reset Entire Site -- real PageContext/SiteDesignContext integration', () => {
test('confirming produces a real, valid single blank Home page + header + footer + default design', () => {
let pageCtx: ReturnType | null = null;
let designCtx: ReturnType | null = null;
const Probe: React.FC = () => {
pageCtx = usePages();
designCtx = useSiteDesign();
return null;
};
const harness = renderEditorHarness();
harness.mountChild(
,
);
// Dirty the design tokens first so resetToDefaults has something to undo.
harness.act(() => { designCtx!.updateDesign({ primaryColor: '#000000' }); });
expect(designCtx!.design.primaryColor).toBe('#000000');
harness.act(() => {
(harness.container.querySelector('[data-action="open-site-reset"]') as HTMLButtonElement).click();
});
const input = harness.container.querySelector('[data-testid="site-reset-domain"]') as HTMLInputElement;
harness.act(() => { setInputValue(input, 'example.com'); });
harness.act(() => {
(harness.container.querySelector('[data-action="confirm-site-reset"]') as HTMLButtonElement).click();
});
// Exactly one page, "Home", landing-page slug -- and its stored
// craftState is real, parseable Craft.js state with a Container ROOT
// and no children (i.e. sanitizeAiTree did NOT reject the tree and fall
// back to a silent empty canvas for the wrong reason -- it's genuinely
// ROOT with zero nodes because that's what we asked for).
expect(pageCtx!.pages).toHaveLength(1);
expect(pageCtx!.pages[0].name).toBe('Home');
expect(pageCtx!.pages[0].slug).toBe('index');
const homeState = JSON.parse(pageCtx!.pages[0].craftState!);
expect(homeState.ROOT.type.resolvedName).toBe('Container');
expect(homeState.ROOT.nodes).toEqual([]);
const headerState = JSON.parse(pageCtx!.headerPage.craftState!);
expect(headerState.ROOT.type.resolvedName).toBe('Container');
expect(headerState.ROOT.nodes).toEqual([]);
expect(headerState.ROOT.props.tag).toBe('header');
const footerState = JSON.parse(pageCtx!.footerPage.craftState!);
expect(footerState.ROOT.type.resolvedName).toBe('Container');
expect(footerState.ROOT.nodes).toEqual([]);
expect(footerState.ROOT.props.tag).toBe('footer');
// Design tokens are back to the defaults.
expect(designCtx!.design).toEqual(DEFAULT_SITE_DESIGN);
harness.unmount();
});
});