diff --git a/craft/src/utils/html-export.head-parity.test.ts b/craft/src/utils/html-export.head-parity.test.ts
index 1e2ec4a..c17baa0 100644
--- a/craft/src/utils/html-export.head-parity.test.ts
+++ b/craft/src/utils/html-export.head-parity.test.ts
@@ -14,10 +14,11 @@ import { exportToHtml, exportBodyHtml, buildFontsLink, type ExportOptions } from
import { DEFAULT_SITE_DESIGN, type SiteDesign } from '../state/SiteDesignContext';
const PINNED_HEAD: Record = {
- A_defaults: "\n\n\n \n \n Joe's Pizza & Grill\n \n \n \n \n \n \n \n",
- B_full: "\n\n\n \n \n \n About "Us"\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n",
- C_unknown_font: "\n\n\n \n \n Contact\n \n \n \n \n \n \n \n \n \n \n",
- D_body_font_and_blank_tokens: "\n\n\n \n \n Services\n \n \n \n \n \n \n \n",
+ A_defaults: "\n\n\n \n \n Joe's Pizza & Grill\n \n \n \n \n \n \n \n",
+ B_full: "\n\n\n \n \n \n About "Us"\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n",
+ C_unknown_font: "\n\n\n \n \n Contact\n \n \n \n \n \n \n \n \n \n \n",
+ D_body_font_and_blank_tokens: "\n\n\n \n \n Services\n \n \n \n \n \n \n \n",
+ E_icons: "\n\n\n \n \n Icons\n \n \n \n \n \n \n \n \n \n",
};
const PINNED_BODY: Record = {
@@ -25,6 +26,7 @@ const PINNED_BODY: Record = {
B_full: "",
C_unknown_font: "",
D_body_font_and_blank_tokens: "",
+ E_icons: "",
};
@@ -77,6 +79,10 @@ const CASES: Record = {
design: merged({ accentColor: '', textColor: ' #111111 ' }),
},
},
+ E_icons: {
+ state: root({}),
+ opts: { title: 'Icons', includeFonts: true, canonicalUrl: U + 'icons', fontScanHtml: '' },
+ },
};
const headOf = (doc: string) => doc.slice(0, doc.indexOf('') + ''.length);
@@ -84,6 +90,8 @@ const headOf = (doc: string) => doc.slice(0, doc.indexOf('') + ''.
describe('Preview head === pinned published head (WHP test-site-builder-head-parity.php)', () => {
for (const [name, { state, opts }] of Object.entries(CASES)) {
test(`${name}: body input matches the backend case`, () => {
+ // E_icons feeds the icon markup through fontScanHtml (the composed page), as the PHP case passes it as the body.
+ if (name === 'E_icons') return expect(opts.fontScanHtml).toBe(PINNED_BODY[name]);
expect(exportBodyHtml(state).html).toBe(PINNED_BODY[name]);
});
test(`${name}: COMPLETE head is byte-identical`, () => {
@@ -159,8 +167,8 @@ describe('composed preview: animation head gate covers header+body+footer', () =
* only in the header), default design, canonical https://example.com/
*/
const BACKEND_PINNED_HEAD: Record = {
- E_headcode_font: "\n\n\n \n \n Notes\n \n \n \n \n \n \n \n \n",
- F_header_animation: "\n\n\n \n \n Home\n \n \n \n \n \n \n \n \n",
+ E_headcode_font: "\n\n\n \n \n Notes\n \n \n \n \n \n \n \n \n",
+ F_header_animation: "\n\n\n \n \n Home\n \n \n \n \n \n \n \n \n",
};
describe('Preview head === backend head for the head-code font scan and header/footer animation gate', () => {
@@ -194,3 +202,44 @@ describe('Preview head === backend head for the head-code font scan and header/f
expect(headOf(html)).toBe(BACKEND_PINNED_HEAD.F_header_animation);
});
});
+
+// Font Awesome links: same decisions as the backend's sb_font_awesome_link() (scripts/test-site-builder-head-parity.php "FA:" block).
+describe('Font Awesome head links (Preview === Published)', () => {
+ const FA = 'font-awesome/7.3.1/css/all.min.css';
+ const SHIM = 'font-awesome/7.3.1/css/v4-shims.min.css';
+ const count = (s: string, needle: string) => s.split(needle).length - 1;
+ const head = (body: string, headCode = '') => headOf(exportToHtml(root({}), { title: 'T', includeFonts: true, fontScanHtml: body, headCode }).html);
+
+ test('no icon => no link', () => { expect(head('x
')).not.toContain('font-awesome'); });
+ test('v7 markup => all.min.css once, no shims', () => {
+ const h = head('');
+ expect([count(h, FA), count(h, SHIM)]).toEqual([1, 0]);
+ });
+ test('legacy `fa fa-star` => all.min.css + v4-shims once each', () => {
+ const h = head('');
+ expect([count(h, FA), count(h, SHIM)]).toEqual([1, 1]);
+ });
+ test('order: fonts link, then Font Awesome, then ${animationNoscript}${headCode}