feat(landing): integrate campaign and early intervention pages
This commit is contained in:
63 files changed
+4230
-50
No files matched your search
@@ -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));}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);}
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => ({
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user