feat: Admin Area Blog and Blog Page 1.00
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import '@angular/compiler';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const sourcePath = resolve(
|
||||
__dirname,
|
||||
'../../frontend/src/app/features/admin/blog/blog-form-modal.component.ts',
|
||||
);
|
||||
const source = readFileSync(sourcePath, 'utf8');
|
||||
|
||||
describe('BlogFormModalComponent — Escape-to-close contract', () => {
|
||||
it('imports HostListener from @angular/core', () => {
|
||||
const importBlock = source.match(
|
||||
/import\s*\{[\s\S]*?\}\s*from\s*'@angular\/core';/,
|
||||
);
|
||||
expect(importBlock).not.toBeNull();
|
||||
expect(importBlock![0]).toMatch(/\bHostListener\b/);
|
||||
});
|
||||
|
||||
it('registers a @HostListener for document:keydown.escape', () => {
|
||||
expect(source).toMatch(
|
||||
/@HostListener\(\s*['"]document:keydown\.escape['"]\s*\)/,
|
||||
);
|
||||
});
|
||||
|
||||
it('guards the Escape handler with open() so a closed modal is a no-op', () => {
|
||||
const handler = extractEscapeHandler(source);
|
||||
expect(handler).not.toBeNull();
|
||||
expect(handler).toMatch(/this\.open\(\)/);
|
||||
});
|
||||
|
||||
it('guards the Escape handler against in-flight saves (no cancel while saving)', () => {
|
||||
const handler = extractEscapeHandler(source);
|
||||
expect(handler).not.toBeNull();
|
||||
expect(handler).toMatch(/!this\.saving\(\)/);
|
||||
});
|
||||
|
||||
it('routes Escape to the existing onCancel() so the parent closes the modal without saving', () => {
|
||||
const handler = extractEscapeHandler(source);
|
||||
expect(handler).not.toBeNull();
|
||||
expect(handler).toMatch(/this\.onCancel\(\)/);
|
||||
});
|
||||
|
||||
it('keeps the existing onCancel() emit intact as a regression guard', () => {
|
||||
const onCancel = extractMethod(source, 'onCancel');
|
||||
expect(onCancel).not.toBeNull();
|
||||
expect(onCancel).toMatch(/this\.cancel\.emit\(\s*\)/);
|
||||
});
|
||||
});
|
||||
|
||||
function extractEscapeHandler(src: string): string | null {
|
||||
// Capture the onEscape handler body — must contain the guard + onCancel call.
|
||||
const m = src.match(
|
||||
/@HostListener\(\s*['"]document:keydown\.escape['"]\s*\)\s*onEscape\s*\(\s*\)\s*:\s*void\s*\{([^}]*)\}/,
|
||||
);
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
|
||||
function extractMethod(src: string, name: string): string | null {
|
||||
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// Match the method header (with optional TypeScript return-type annotation) and capture the body.
|
||||
const re = new RegExp(
|
||||
`(?:public\\s+|private\\s+|protected\\s+)?${escaped}\\s*\\([^)]*\\)\\s*(?::\\s*[A-Za-z_<>|[\\]\\s,]+)?\\s*\\{([\\s\\S]*?)\\n\\s*\\}`,
|
||||
);
|
||||
const m = src.match(re);
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
Reference in New Issue
Block a user