Site builder: security & data-loss hardening + unified asset picker + audit backlog #3
@@ -121,6 +121,38 @@ describe('PageProvider slug dedupe', () => {
|
|||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('M-4: a punctuation-only page name ("!!!") falls back to slug "page", never empty string', () => {
|
||||||
|
let ctx: ReturnType<typeof usePages> | null = null;
|
||||||
|
const Consumer: React.FC = () => {
|
||||||
|
ctx = usePages();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<PageProvider>
|
||||||
|
<Consumer />
|
||||||
|
</PageProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
ctx!.addPage('!!!', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
const added = ctx!.pages.find((p) => p.name === '!!!')!;
|
||||||
|
expect(added.slug).toBe('page');
|
||||||
|
expect(added.slug).not.toBe('');
|
||||||
|
|
||||||
|
// A second punctuation-only-named page dedupes to 'page-2', not ''.
|
||||||
|
act(() => {
|
||||||
|
ctx!.addPage('???', '');
|
||||||
|
});
|
||||||
|
const second = ctx!.pages.find((p) => p.name === '???')!;
|
||||||
|
expect(second.slug).toBe('page-2');
|
||||||
|
expect(second.slug).not.toBe('');
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
test('landing page slug always stays "index" even if renamed to collide', () => {
|
test('landing page slug always stays "index" even if renamed to collide', () => {
|
||||||
let ctx: ReturnType<typeof usePages> | null = null;
|
let ctx: ReturnType<typeof usePages> | null = null;
|
||||||
const Consumer: React.FC = () => {
|
const Consumer: React.FC = () => {
|
||||||
|
|||||||
@@ -181,12 +181,17 @@ const PageContext = createContext<PageContextValue>({
|
|||||||
export const usePages = () => useContext(PageContext);
|
export const usePages = () => useContext(PageContext);
|
||||||
|
|
||||||
function slugify(name: string): string {
|
function slugify(name: string): string {
|
||||||
return name
|
const slug = name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
.replace(/-+/g, '-');
|
.replace(/-+/g, '-');
|
||||||
|
// M-4: a punctuation-only name (e.g. "!!!") strips down to '' -- without a
|
||||||
|
// fallback, buildSavePayload would write filename = '' + '.html' for that
|
||||||
|
// page. uniqueSlug's existing dedupe logic then applies on top of this
|
||||||
|
// fallback the same way it does for any other base ('page', 'page-2', ...).
|
||||||
|
return slug || 'page';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user