feat(landing): integrate campaign and early intervention pages

This commit is contained in:
DeCentN2Madness committed 2026-10-20 17:50:56 -07:00
1 parent 537f9864a0
commit f98576f3ae
63 files changed
+4230 -50

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
/** Guarded loopback-only campaign verification. Optional source build for visual comparison.
* PLAYWRIGHT_MODULE=/path/to/playwright/index.mjs node tools/campaign-browser.mjs
* CAMPAIGN_REFERENCE_DIST=/tmp/reference/dist CAMPAIGN_BASELINE_DIST=/tmp/baseline ...
* Screenshots and evidence remain outside the repository. No submission tests.
*/
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import http from 'node:http';
import { createHash } from 'node:crypto';
import { fileURLToPath, pathToFileURL } from 'node:url';
const root = fileURLToPath(new URL('../', import.meta.url));
const { chromium } = await import(process.env.PLAYWRIGHT_MODULE ? pathToFileURL(process.env.PLAYWRIGHT_MODULE).href : 'playwright');
const output = await fs.mkdtemp('/tmp/aia-campaign-browser-');
const evidence = { date: new Date().toISOString(), output, status: 'pending', error: '', browser: '', checks: [], blocked: [], screenshots: [], errors: [] };
const servers = [];
let browser;
const mime = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.svg': 'image/svg+xml', '.woff2': 'font/woff2', '.webp': 'image/webp', '.png': 'image/png', '.json': 'application/json' };
async function serve(directory) {
const base=path.resolve(directory);
const server=http.createServer(async (req,res)=>{
try {
assert.ok(['GET','HEAD'].includes(req.method));
let file=path.resolve(base,'.'+decodeURIComponent(new URL(req.url,'http://localhost').pathname));
assert.ok(file.startsWith(base+'/') || file===base);
if((await fs.stat(file)).isDirectory()) file=path.join(file,'index.html');
res.setHeader('Content-Type',mime[path.extname(file)] || 'application/octet-stream');res.end(await fs.readFile(file));
} catch {res.statusCode=404;res.end('Not found');}
});
await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));servers.push(server);
return `http://127.0.0.1:${server.address().port}`;
}
const routes=['/lp/aba-therapy','/lp/aba-insurance','/lp/aba-ahcccs','/lp/aba-private-pay'];
try {
const origin=await serve(path.join(root,'dist'));
const reference=process.env.CAMPAIGN_REFERENCE_DIST ? await serve(process.env.CAMPAIGN_REFERENCE_DIST) : null;
const baseline=process.env.CAMPAIGN_BASELINE_DIST ? await serve(process.env.CAMPAIGN_BASELINE_DIST) : null;
const allowed=new Set([origin,reference,baseline].filter(Boolean));
browser=await chromium.launch({headless:true,...(process.env.CHROMIUM_EXECUTABLE ? {executablePath:process.env.CHROMIUM_EXECUTABLE}: {})});
evidence.browser=browser.version();
async function context(javaScriptEnabled=true) {
const ctx=await browser.newContext({javaScriptEnabled,serviceWorkers:'block',acceptDownloads:false,deviceScaleFactor:1});
await ctx.routeWebSocket('**/*',socket=>socket.close());
await ctx.route('**/*',route=>{
const r=route.request(),u=new URL(r.url());
if(!allowed.has(u.origin) || !['GET','HEAD'].includes(r.method())) {evidence.blocked.push({origin:u.origin,path:u.pathname,method:r.method()});return route.abort();}
return route.continue();
});
return ctx;
}
const ctx=await context();const page=await ctx.newPage();
page.on('pageerror',err=>evidence.errors.push(err.message));
async function visit(base,route,width) {
await page.setViewportSize({width,height:900});
const response=await page.goto(base+route,{waitUntil:'networkidle'});assert.equal(response.status(),200);
await page.evaluate(async()=>{
await document.fonts.ready;
document.querySelectorAll('img[loading="lazy"]').forEach(img=>img.loading='eager');
await Promise.all([...document.images].map(img=>img.decode().catch(()=>{})));
});
}
async function capture(label,route,width) {
const filename=`${label}-${route.replaceAll('/','_')}-${width}.png`;
const buffer=await page.screenshot({path:path.join(output,filename),fullPage:true});
evidence.screenshots.push({file:filename,sha256:createHash('sha256').update(buffer).digest('hex')});
}
async function design() {return page.evaluate(()=>({
title:document.title,h1:document.querySelector('h1').textContent,
sections:[...document.querySelectorAll('main > section:not(.consultation)')].map(el=>{
const r=el.getBoundingClientRect(),s=getComputedStyle(el);
return {text:el.innerText.replace(/\s+/g,' ').trim(),x:r.x,y:r.y,width:r.width,height:r.height,background:s.backgroundColor};
}),
headingFont:getComputedStyle(document.querySelector('h1')).fontFamily,
bodyFont:getComputedStyle(document.body).fontFamily,
overflow:document.documentElement.scrollWidth>innerWidth,
images:[...document.images].map(i=>({alt:i.alt,width:i.naturalWidth,loaded:i.complete&&i.naturalWidth>0}))
}));}
for(const width of [320,390,768,1440]) for(const route of routes) {
let source;
if(reference){await visit(reference,route,width);source=await design();await capture('source',route,width);}
await visit(origin,route,width);const result=await design();
assert.equal(result.overflow,false,route+' overflow');assert.ok(result.images.every(i=>i.loaded));
assert.equal(await page.locator('meta[name="robots"]').getAttribute('content'),'noindex,nofollow');
assert.equal(await page.locator('link[rel="canonical"]').getAttribute('href'),'https://www.azinstitute4autism.com'+route);
assert.equal(await page.locator('form').count(),0);assert.equal(await page.locator('#lead-form [name]:enabled').count(),0);
assert.equal(await page.locator('#lead-form button').isDisabled(),true);
if(source) {
assert.deepEqual(result.sections,source.sections,'Source content, section geometry and backgrounds');
assert.equal(result.title,source.title);assert.equal(result.h1,source.h1);
assert.equal(result.headingFont,source.headingFont);assert.equal(result.bodyFont,source.bodyFont);
assert.deepEqual(result.images,source.images);
}
await capture('integrated',route,width);
const faq=page.locator('.faq details').first();await faq.locator('summary').focus();await page.keyboard.press('Enter');assert.equal(await faq.getAttribute('open'),'');
const focus=await faq.locator('summary').evaluate(el=>getComputedStyle(el).outlineStyle);assert.notEqual(focus,'none');
await faq.locator('summary').press('Enter');
await page.locator('.hero__actions a').first().click();assert.equal(new URL(page.url()).hash,'#consultation');
evidence.checks.push({route,width,sourceParity:!!source,overflow:false,assets:true,faqKeyboard:true,cta:true,forms:'off'});
}
// Source payer links are retained; arbitrary attribution parameters are not forwarded.
await visit(origin,routes[0]+'?utm_term=synthetic&gclid=synthetic',390);
for(const a of await page.locator('[data-payment-intent]').all())assert.ok(!(await a.getAttribute('href')).includes('?'));
const nojs=await context(false);const np=await nojs.newPage();
for(const route of routes){await np.goto(origin+route);assert.ok(await np.locator('h1').isVisible());assert.equal(await np.locator('form').count(),0);assert.ok(await np.locator('#campaign-form-status').isVisible());await np.locator('.faq summary').first().click();assert.equal(await np.locator('.faq details').first().getAttribute('open'),'');}
evidence.noJavaScriptRoutes=routes;await nojs.close();
// Existing site's style sheets and geometry must not inherit the campaign CSS.
if(baseline) for(const route of ['/','/es','/library','/aba-therapy']) for(const width of [390,1440]) {
const snapshots=[];
for(const base of [baseline,origin]) {
await visit(base,route,width);
snapshots.push(await page.evaluate(()=>({title:document.title,css:[...document.querySelectorAll('link[rel="stylesheet"]')].map(l=>new URL(l.href).pathname),font:getComputedStyle(document.body).fontFamily,h1:getComputedStyle(document.querySelector('h1')).fontSize,width:document.documentElement.scrollWidth})));
}
assert.deepEqual(snapshots[0],snapshots[1]);evidence.checks.push({route,width,mainSiteUnchanged:true});
}
assert.deepEqual(evidence.blocked,[],'Unexpected request attempt');assert.deepEqual(evidence.errors,[]);
evidence.status='passed';await ctx.close();
} catch(error){evidence.status='failed';evidence.error=String(error.stack);process.exitCode=1;}
finally{await browser?.close();await Promise.all(servers.map(s=>new Promise(resolve=>s.close(resolve))));await fs.writeFile(path.join(output,'evidence.json'),JSON.stringify(evidence,null,2)+'\n');console.log(JSON.stringify({status:evidence.status,error:evidence.error,checks:evidence.checks.length,screenshots:evidence.screenshots.length,output},null,2));}
+142
View File
@@ -0,0 +1,142 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { load } from 'cheerio';
import matter from 'gray-matter';
import fg from 'fast-glob';
import { landingPages, paymentPaths } from '../src/data/campaign/landingPages.ts';
import { createPublicationManifest, robotsFor } from '../src/utils/publication-policy.ts';
const root = fileURLToPath(new URL('../', import.meta.url));
const read = p => fs.readFileSync(path.join(root, p), 'utf8');
const pages = Object.values(landingPages);
const entries = pages.map(page => ({
collection: 'pages', id: `en/${page.slug.slice(1)}`,
data: matter(read(`src/content/pages/en/${page.slug.slice(1)}.md`)).data
}));
const manifest = createPublicationManifest(entries, 'https://www.azinstitute4autism.com');
const html = page => load(read(`dist${page.slug}/index.html`));
test('campaign content IDs cannot replace existing MDX publications', async () => {
const ids = (await fg('src/content/pages/**/*.{md,mdx}', { cwd: root }))
.map(file => file.replace(/\.mdx?$/, ''));
assert.equal(new Set(ids).size, ids.length, 'Duplicate collection ID across Markdown and MDX');
assert.ok(fs.existsSync(path.join(root, 'dist/aba-therapy/index.html')));
const main = load(read('dist/aba-therapy/index.html'));
assert.equal(main('link[rel="canonical"]').attr('href'), 'https://www.azinstitute4autism.com/aba-therapy');
assert.equal(main('link[hreflang="es"]').length,
main('meta[name="robots"]').attr('content') === 'index,follow' ? 1 : 0);
});
test('four campaign identities match CMS metadata and retain noindex publication', () => {
assert.equal(pages.length, 4);
assert.equal(new Set(pages.map(p => p.slug)).size, 4);
for (const [i, page] of pages.entries()) {
const entry = entries[i];
assert.equal(entry.data.title, page.title);
assert.equal(entry.data.description, page.description);
assert.equal(entry.data.displayH1, page.h1);
assert.equal(entry.data.campaign, page.intent);
const pub = manifest.find(p => p.route === page.slug);
assert.ok(pub.eligible);
assert.equal(pub.sitemapEligible, false);
assert.deepEqual(pub.translations, []);
assert.equal(robotsFor(pub, false), 'noindex,nofollow');
assert.equal(robotsFor(pub, true), 'noindex,follow');
}
});
test('generated campaign metadata and FAQ content match the imported configuration', () => {
for (const page of pages) {
const $ = html(page);
assert.equal($('h1').length, 1);
assert.equal($('h1').text(), page.h1);
assert.equal($('title').text(), page.title);
assert.equal($('meta[name="description"]').attr('content'), page.description);
assert.equal($('link[rel="canonical"]').attr('href'), 'https://www.azinstitute4autism.com' + page.slug);
assert.equal($('html').attr('lang'), 'en');
assert.equal($('html').attr('dir'), 'ltr');
assert.equal($('link[hreflang]').length, 0);
assert.deepEqual($('.faq summary').map((_, el) => $(el).text()).get(), page.faq.map(f => f.question));
assert.deepEqual($('.faq details p').map((_, el) => $(el).text()).get(), page.faq.map(f => f.answer));
}
});
test('photos, responsive variants and licensed fonts are self-hosted', () => {
for (const page of pages) {
const $ = html(page);
$('img').each((_, el) => {
const img = $(el);
assert.ok(fs.existsSync(path.join(root, 'public', img.attr('src'))));
for (const item of (img.attr('srcset') || '').split(',').filter(Boolean)) {
assert.ok(fs.existsSync(path.join(root, 'public', item.trim().split(/\s/)[0])));
}
assert.ok(img.attr('width') && img.attr('height'));
});
}
for (const name of ['rubik-400.woff2', 'rubik-700.woff2', 'playfair-display-700.woff2', 'Rubik-OFL.txt', 'Playfair-Display-OFL.txt']) {
assert.ok(fs.existsSync(path.join(root, 'public/assets/campaign/fonts', name)));
}
});
test('inactive lead presentation has no native submission surface or live integration runtime', () => {
for (const page of pages) {
const $ = html(page);
assert.equal($('form').length, 0);
assert.equal($('#lead-form').attr('data-forms-mode'), 'off');
assert.equal($('#lead-form [name]:not([disabled])').length, 0);
assert.equal($('#lead-form button').attr('type'), 'button');
assert.ok($('#lead-form button').is('[disabled]'));
assert.match($('#campaign-form-status').text(), /not enabled/);
assert.ok($('#campaign-form-status a[href^="tel:"]').length);
assert.equal($('iframe').length, 0);
assert.equal($('script[src]').length, 0);
assert.doesNotMatch($('script').text(), /gtag|googletagmanager|sessionStorage|fetch\(|utm_|gclid|postMessage/);
}
});
test('campaign links resolve locally and fragments identify exactly one section', () => {
for (const page of pages) {
const $ = html(page);
$('a[href^="#"]').each((_, el) => {
const id = $(el).attr('href').slice(1);
assert.equal($('[id]').filter((_, e) => $(e).attr('id') === id).length, 1);
});
$('a[href^="/"]').each((_, el) => {
const href = $(el).attr('href');
assert.ok(fs.existsSync(path.join(root, 'dist', href, 'index.html')), href);
});
}
assert.deepEqual(paymentPaths.map(p => p.href).sort(), pages.filter(p => p.intent !== 'general').map(p => p.slug).sort());
});
test('campaign styles do not load on the existing main site', () => {
const campaignCss = html(pages[0])('link[rel="stylesheet"]').map((_, e) => e.attribs.href).get();
assert.ok(campaignCss.length);
for (const route of ['', 'es', 'library']) {
const $ = load(read(`dist/${route ? route + '/' : ''}index.html`));
const mainCss = $('link[rel="stylesheet"]').map((_, e) => e.attribs.href).get();
assert.ok(campaignCss.every(css => !mainCss.includes(css)));
}
});
test('early-intervention preserves its own publication identity and source sections', () => {
const $ = load(read('dist/lp/early-intervention/index.html'));
assert.equal($('h1').text(), 'Early Autism Intervention Makes a Difference');
assert.equal($('h1').length, 1);
assert.equal($('main').length, 1);
assert.equal($('link[rel="canonical"]').attr('href'), 'https://www.azinstitute4autism.com/lp/early-intervention');
assert.equal($('.qr-landing section').length, 7);
assert.equal($('.qr-benefit-card').length, 3);
assert.equal($('.qr-process-step').length, 3);
assert.equal($('form, input[type="hidden"], script[src]').length, 0);
assert.equal($('#consultation input').length, 3);
assert.equal($('#consultation input:disabled').length, 3);
assert.equal($('#consultation button').attr('type'), 'button');
assert.ok($('#consultation-unavailable a[href="tel:+14806877099"]').length);
assert.equal($('img[src^="https:"]').length, 0);
assert.ok($('.early-header').length);
assert.equal($('.mobile-cta').length, 0);
});
+67
View File
@@ -0,0 +1,67 @@
/** Guarded loopback-only campaign verification. Optional source build for visual comparison.
* PLAYWRIGHT_MODULE=/path/to/playwright/index.mjs node tools/campaign-browser.mjs
* CAMPAIGN_REFERENCE_DIST=/tmp/reference/dist CAMPAIGN_BASELINE_DIST=/tmp/baseline ...
* Screenshots and evidence remain outside the repository. No submission tests.
*/
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import http from 'node:http';
import { createHash } from 'node:crypto';
import { fileURLToPath, pathToFileURL } from 'node:url';
const root = fileURLToPath(new URL('../', import.meta.url));
const { chromium } = await import(process.env.PLAYWRIGHT_MODULE ? pathToFileURL(process.env.PLAYWRIGHT_MODULE).href : 'playwright');
const output = await fs.mkdtemp('/tmp/aia-early-browser-');
const evidence = { date: new Date().toISOString(), output, status: 'pending', error: '', browser: '', checks: [], blocked: [], screenshots: [], errors: [] };
const servers = [];
let browser;
const mime = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.svg': 'image/svg+xml', '.woff2': 'font/woff2', '.webp': 'image/webp', '.png': 'image/png', '.json': 'application/json' };
async function serve(directory) {
const base=path.resolve(directory);
const server=http.createServer(async (req,res)=>{
try {
assert.ok(['GET','HEAD'].includes(req.method));
let file=path.resolve(base,'.'+decodeURIComponent(new URL(req.url,'http://localhost').pathname));
assert.ok(file.startsWith(base+'/') || file===base);
if((await fs.stat(file)).isDirectory()) file=path.join(file,'index.html');
res.setHeader('Content-Type',mime[path.extname(file)] || 'application/octet-stream');res.end(await fs.readFile(file));
} catch {res.statusCode=404;res.end('Not found');}
});
await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));servers.push(server);
return `http://127.0.0.1:${server.address().port}`;
}
try {
const origin=await serve(path.join(root,'dist'));
const reference=process.env.EARLY_REFERENCE_DIR ? await serve(process.env.EARLY_REFERENCE_DIR) : null;
const allowed=new Set([origin,reference].filter(Boolean));
browser=await chromium.launch({headless:true,executablePath:process.env.CHROMIUM_EXECUTABLE});
evidence.browser=browser.version();
for (const javaScriptEnabled of [true,false]) {
const ctx=await browser.newContext({javaScriptEnabled,serviceWorkers:'block',deviceScaleFactor:1});
await ctx.routeWebSocket('**/*',s=>s.close());
await ctx.route('**/*',r=>{const u=new URL(r.request().url());if(!allowed.has(u.origin)||!['GET','HEAD'].includes(r.request().method())){evidence.blocked.push({origin:u.origin,path:u.pathname,method:r.request().method()});return r.abort();}return r.continue();});
const page=await ctx.newPage();page.on('pageerror',e=>evidence.errors.push(e.message));
for(const width of javaScriptEnabled?[320,390,768,1440]:[390,1440]) {
await page.setViewportSize({width,height:900});
let source;
async function design(){return page.evaluate(()=>({headings:[...document.querySelectorAll('.qr-landing h1,.qr-landing h2,.qr-landing h3')].map(e=>e.textContent),heroWidth:document.querySelector('.qr-hero').getBoundingClientRect().width,font:getComputedStyle(document.querySelector('h1')).font,images:[...document.images].map(i=>({alt:i.alt,loaded:i.complete&&i.naturalWidth>0})),overflow:document.documentElement.scrollWidth>innerWidth}));}
async function visit(url){await page.goto(url,{waitUntil:'networkidle'});await page.evaluate(async()=>{await document.fonts.ready;document.querySelectorAll('img').forEach(i=>i.loading='eager');await Promise.all([...document.images].map(i=>i.decode().catch(()=>{})));});}
async function shot(label){const file=`${label}-${width}-${javaScriptEnabled?'js':'nojs'}.png`;const data=await page.screenshot({path:path.join(output,file),fullPage:true});evidence.screenshots.push({file,sha256:createHash('sha256').update(data).digest('hex')});}
if(reference&&javaScriptEnabled){await visit(reference+'/');source=await design();await shot('source');}
await visit(origin+'/lp/early-intervention');const actual=await design();
assert.equal(actual.overflow,false);assert.ok(actual.images.every(i=>i.loaded));
if(source){assert.deepEqual(actual.headings,source.headings);assert.equal(actual.heroWidth,source.heroWidth);assert.equal(actual.font,source.font);}
assert.equal(await page.locator('form').count(),0);
assert.equal(await page.locator('#consultation input:enabled').count(),0);
assert.equal(await page.locator('meta[name="robots"]').getAttribute('content'),'noindex,nofollow');
await page.locator('.qr-hero-actions a[href="#consultation"]').click();
assert.equal(new URL(page.url()).hash,'#consultation');
await page.evaluate(()=>scrollTo(0,0));await shot('import');
evidence.checks.push({width,javaScriptEnabled,sourceCompared:!!source,status:'passed',...actual});
}
await ctx.close();
}
assert.deepEqual(evidence.blocked,[]);assert.deepEqual(evidence.errors,[]);evidence.status='passed';
} catch(error){evidence.status='failed';evidence.error=String(error);process.exitCode=1;}
finally {if(browser)await browser.close();for(const s of servers)await new Promise(r=>s.close(r));await fs.writeFile(path.join(output,'evidence.json'),JSON.stringify(evidence,null,2));console.log(output,evidence.status,evidence.error);}
+12 -3
View File
@@ -661,7 +661,9 @@ test('/search is a noindex static Library-only compatibility route in both build
const publication = createLibrarySearchPublication(site);
for (const mode of ['staging', 'indexing']) {
const { html, sitemap } = artifacts.get(mode);
assert.equal(html.size, routeManifest.filter((item) => item.eligible).length + 1);
// /search is permanent; /lp is a staging-only reviewer utility.
assert.equal(html.size, routeManifest.filter((item) => item.eligible).length + (mode === 'staging' ? 2 : 1));
assert.equal(sitemap.includes('<loc>https://www.azinstitute4autism.com/lp</loc>'), false);
const $ = load(html.get('/search'));
assert.equal($('link[rel="canonical"]').attr('href'), `${site}/search`);
assert.equal($('meta[name="robots"]').attr('content'), robotsFor(publication, mode === 'indexing'));
@@ -709,7 +711,14 @@ test('nine author archives contain all and only matching eligible posts in stabl
'/library/author/rula-diab/page/6'
]);
const html = artifacts.get('staging').html;
assert.equal(html.size, 122);
assert.equal(html.size, 128); // Includes the staging-only /lp reviewer index.
assert.ok(html.has('/lp'));
const reviewIndex = load(html.get('/lp'));
assert.deepEqual(reviewIndex('nav a').toArray().map(el => reviewIndex(el).attr('href')), [
'/lp/aba-therapy', '/lp/aba-insurance', '/lp/aba-ahcccs', '/lp/aba-private-pay', '/lp/early-intervention'
]);
assert.equal(reviewIndex('meta[name="robots"]').attr('content'), 'noindex,nofollow');
assert.equal(artifacts.get('indexing').html.has('/lp'), false);
for (const [locale, sizes] of Object.entries(expected)) {
const author = createAuthorResolver(authors)(locale, 'rula-diab');
const links = [];
@@ -781,7 +790,7 @@ test('every card and post byline uses its resolved locale author without nested
test('author publication descriptors and HTML obey noindex, self-canonical, sitemap and translation exclusions in both modes', () => {
for (const mode of ['staging', 'indexing']) {
const { html, sitemap } = artifacts.get(mode);
assert.equal((sitemap.match(/<url>/g) ?? []).length, mode === 'indexing' ? 108 : 0);
assert.equal((sitemap.match(/<url>/g) ?? []).length, mode === 'indexing' ? 109 : 0);
let hreflang = 0, pages = 0;
for (const source of html.values()) {
const $ = load(source), count = $('link[hreflang]').length;
+9 -1
View File
@@ -354,7 +354,9 @@ after(async () => {
for (const mode of ['staging', 'indexing']) {
test(`${mode} build: only eligible local routes are generated, including home/Library`, () => {
assert.deepEqual([...artifacts.get(mode).html.keys()].sort(), generatedPublications(fixtureManifest).map((page) => page.route).sort());
const expected = generatedPublications(fixtureManifest).map((page) => page.route);
if (mode === 'staging') expected.push('/lp');
assert.deepEqual([...artifacts.get(mode).html.keys()].sort(), expected.sort());
assert.ok(!artifacts.get(mode).html.has('/ar'));
assert.ok(artifacts.get(mode).html.has('/schedule-consultation'));
assert.ok(artifacts.get(mode).html.has('/search'));
@@ -394,6 +396,12 @@ function assertRenderedTranslations({ html }, manifest, allowIndexing) {
const pages = new Map(publications.map((page) => [page.route, page]));
for (const [route, content] of html) {
const page = pages.get(route);
if (route === '/lp' && !allowIndexing) {
const $ = load(content);
assert.equal($('meta[name="robots"]').attr('content'), 'noindex,nofollow');
assert.equal($('link[hreflang], .lang-switcher').length, 0);
continue;
}
assert.ok(page, `Unknown generated route: ${route}`);
const $ = load(content);
const links = $('head link[rel="alternate"][hreflang]').map((_, tag) => ({
+3 -1
View File
@@ -278,7 +278,8 @@ for (const mode of ['staging', 'indexing']) {
const { html, sitemap } = artifacts.get(mode);
assert.deepEqual([...html.keys()].sort(), [
...routeManifest.filter((item) => item.eligible).map((item) => item.route),
createLibrarySearchPublication(site).route
createLibrarySearchPublication(site).route,
...(mode === 'staging' ? ['/lp'] : [])
].sort());
assert.equal(sitemap, renderSitemap(routeManifest, mode === 'indexing'));
for (const publication of manifest.filter((item) => item.eligible)) {
@@ -307,6 +308,7 @@ for (const mode of ['staging', 'indexing']) {
assert.ok(!sitemap.includes(`${site}${route}<`));
}
assert.ok(!html.has('/ar'));
assert.equal(html.has('/lp'), mode === 'staging');
});
}