feat(forms): gate Form A production embedding
This commit is contained in:
17 files changed
+284
-47
No files matched your search
@@ -19,9 +19,11 @@ import { setTimeout as delay } from 'node:timers/promises';
|
||||
const root = fileURLToPath(new URL('../', import.meta.url));
|
||||
const moduleName = process.env.PLAYWRIGHT_MODULE ? pathToFileURL(path.resolve(process.env.PLAYWRIGHT_MODULE)).href : 'playwright';
|
||||
const mode = process.env.FORM_A_IFRAME_BROWSER_MODE ?? 'off';
|
||||
if (!['off', 'mock', 'isolated-test'].includes(mode)) throw new Error(`Unsupported Form A browser mode: ${mode}`);
|
||||
if (!['off', 'mock', 'isolated-test', 'production'].includes(mode)) throw new Error(`Unsupported Form A browser mode: ${mode}`);
|
||||
|
||||
const expectedIframe = mode === 'isolated-test';
|
||||
const expectedIframe = mode === 'isolated-test' || mode === 'production';
|
||||
const expectedFormMode = mode === 'production' ? 'live' : 'isolated-test';
|
||||
const expectedEnvironment = mode === 'production' ? 'production' : 'isolated-test';
|
||||
const routes = [
|
||||
{ route: '/contact', locale: 'en', context: 'contact', screenshot: 'contact' },
|
||||
{ route: '/es/contact', locale: 'es', context: 'contact', screenshot: 'es-contact' },
|
||||
@@ -91,7 +93,8 @@ try {
|
||||
assert.equal(await iframe.getAttribute('title'), locale === 'es' ? 'Formulario de consultas generales de AIA' : 'AIA general enquiries form', route);
|
||||
assert.equal(await iframe.getAttribute('referrerpolicy'), 'no-referrer', route);
|
||||
assert.equal(await iframe.getAttribute('sandbox'), 'allow-forms allow-scripts allow-same-origin', route);
|
||||
assert.equal(await page.locator('[data-jotform-form-a][data-form-mode="isolated-test"]').count(), 1, route);
|
||||
assert.equal(await page.locator(`[data-jotform-form-a][data-form-mode="${expectedFormMode}"]`).count(), 1, route);
|
||||
assert.equal(await page.locator(`[data-jotform-form-a][data-form-environment="${expectedEnvironment}"]`).count(), 1, route);
|
||||
assert.equal(await page.locator(`[data-jotform-form-a][data-form-context="${formContext}"]`).count(), 1, route);
|
||||
const frameBox = await iframe.boundingBox();
|
||||
const expectedFrameHeight = formContext === 'contact'
|
||||
@@ -152,6 +155,7 @@ try {
|
||||
const response = await page.goto(origin + route, { waitUntil: 'domcontentloaded', timeout: 15000 });
|
||||
assert.equal(response?.status(), 200, `${route} without JavaScript`);
|
||||
assert.equal(await page.locator('[data-jotform-form-a] iframe').count(), 1, `${route} without JavaScript`);
|
||||
assert.equal(await page.locator(`[data-jotform-form-a][data-form-mode="${expectedFormMode}"]`).count(), 1, `${route} without JavaScript`);
|
||||
const iframe = page.locator('[data-jotform-form-a] iframe');
|
||||
assert.equal(await iframe.evaluate((element) => element.innerHTML.trim()), '', `${route} without JavaScript should not place fallback content inside the iframe`);
|
||||
const noscriptFallback = page.locator('[data-jotform-form-a] .jotform-iframe__fallback--noscript');
|
||||
|
||||
@@ -15,6 +15,7 @@ const isolated = (overrides = {}) => ({
|
||||
rawMode: 'isolated-test',
|
||||
liveApproved: 'false',
|
||||
isolatedTestApproved: 'true',
|
||||
productionApproved: 'false',
|
||||
environmentRole: 'isolated-test',
|
||||
allowIndexing: 'false',
|
||||
route: '/contact',
|
||||
@@ -22,6 +23,18 @@ const isolated = (overrides = {}) => ({
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const production = (overrides = {}) => ({
|
||||
rawMode: 'live',
|
||||
liveApproved: 'true',
|
||||
isolatedTestApproved: 'false',
|
||||
productionApproved: 'true',
|
||||
environmentRole: 'production',
|
||||
allowIndexing: 'true',
|
||||
route: '/contact',
|
||||
locale: 'en',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
test('Form A iframe uses only fixed, approved route parameters', () => {
|
||||
assert.equal(formAIframeOrigin, 'https://form.jotform.com');
|
||||
assert.equal(formAIframeId, '262946078314059');
|
||||
@@ -54,14 +67,15 @@ test('Form A iframe uses only fixed, approved route parameters', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('Form A iframe requires every isolated-test gate and refuses live mode', () => {
|
||||
test('Form A iframe requires every isolated-test or production approval gate', () => {
|
||||
const enabled = resolveFormAIframe(isolated());
|
||||
assert.equal(enabled.enabled, true);
|
||||
assert.equal(enabled.reason, 'enabled');
|
||||
assert.equal(enabled.title, 'AIA general enquiries form');
|
||||
assert.equal(resolveFormAIframe(isolated({ rawMode: undefined })).reason, 'mode-not-isolated-test');
|
||||
assert.equal(resolveFormAIframe(isolated({ rawMode: 'mock' })).reason, 'mode-not-isolated-test');
|
||||
assert.equal(resolveFormAIframe(isolated({ rawMode: 'live', liveApproved: 'true' })).reason, 'mode-not-isolated-test');
|
||||
assert.equal(enabled.mode, 'isolated-test');
|
||||
assert.equal(enabled.environmentRole, 'isolated-test');
|
||||
assert.equal(resolveFormAIframe(isolated({ rawMode: undefined })).reason, 'mode-not-authorized');
|
||||
assert.equal(resolveFormAIframe(isolated({ rawMode: 'mock' })).reason, 'mode-not-authorized');
|
||||
assert.equal(resolveFormAIframe(isolated({ liveApproved: undefined })).reason, 'live-approval-not-disabled');
|
||||
assert.equal(resolveFormAIframe(isolated({ liveApproved: 'true' })).reason, 'live-approval-not-disabled');
|
||||
assert.equal(resolveFormAIframe(isolated({ isolatedTestApproved: 'false' })).reason, 'isolated-test-not-approved');
|
||||
@@ -73,6 +87,18 @@ test('Form A iframe requires every isolated-test gate and refuses live mode', ()
|
||||
const campaign = resolveFormAIframe(isolated({ route: '/lp/aba-therapy' }));
|
||||
assert.equal(campaign.url, 'https://form.jotform.com/262946078314059?formContext=landing&language=en');
|
||||
assert.equal(campaign.context, 'landing');
|
||||
const live = resolveFormAIframe(production());
|
||||
assert.equal(live.enabled, true);
|
||||
assert.equal(live.mode, 'live');
|
||||
assert.equal(live.environmentRole, 'production');
|
||||
assert.equal(live.url, 'https://form.jotform.com/262946078314059?formContext=contact&language=en');
|
||||
assert.equal(resolveFormAIframe(production({ productionApproved: undefined })).reason, 'production-approval-not-granted');
|
||||
assert.equal(resolveFormAIframe(production({ productionApproved: 'false' })).reason, 'production-approval-not-granted');
|
||||
assert.equal(resolveFormAIframe(production({ environmentRole: 'isolated-test' })).reason, 'invalid-environment-role');
|
||||
assert.equal(resolveFormAIframe(production({ liveApproved: 'false' })).reason, 'mode-not-authorized');
|
||||
assert.equal(resolveFormAIframe(production({ rawMode: 'off' })).reason, 'mode-not-authorized');
|
||||
assert.equal(resolveFormAIframe(production({ route: '/referrals' })).reason, 'route-not-authorized');
|
||||
assert.equal(resolveFormAIframe(production({ route: '/es/contact', locale: 'es' })).url, 'https://form.jotform.com/262946078314059?formContext=contact&language=es');
|
||||
assert.equal(formModeForRuntime(resolveFormMode('isolated-test', undefined)), 'off');
|
||||
assert.equal(formModeForRuntime(resolveFormMode('live', 'true')), 'off');
|
||||
});
|
||||
@@ -86,8 +112,10 @@ test('Only the seven allowlisted placements can render the adapter, with no help
|
||||
assert.match(contact, /resolveFormAIframe/);
|
||||
assert.match(contact, /route = lang === 'es' \? '\/es\/contact' : '\/contact'/);
|
||||
assert.match(contact, /JotformIframe/);
|
||||
assert.match(contact, /PUBLIC_AIA_FORM_A_PRODUCTION_APPROVED/);
|
||||
assert.match(campaign, /resolveFormAIframe/);
|
||||
assert.match(campaign, /JotformIframe/);
|
||||
assert.match(campaign, /PUBLIC_AIA_FORM_A_PRODUCTION_APPROVED/);
|
||||
assert.match(campaign, /variant="embedded"/);
|
||||
assert.match(campaign, /\/lp\/aba-therapy/);
|
||||
assert.match(campaign, /const iframeDescription = 'Use this form to request a conversation\. An AIA team member can explain the next steps for your family\.'/);
|
||||
@@ -96,8 +124,11 @@ test('Only the seven allowlisted placements can render the adapter, with no help
|
||||
assert.match(earlyIntervention, /resolveFormAIframe/);
|
||||
assert.match(earlyIntervention, /route: '\/lp\/early-intervention'/);
|
||||
assert.match(earlyIntervention, /JotformIframe/);
|
||||
assert.match(earlyIntervention, /PUBLIC_AIA_FORM_A_PRODUCTION_APPROVED/);
|
||||
assert.match(adapter, /referrerpolicy="no-referrer"/);
|
||||
assert.match(adapter, /sandbox="allow-forms allow-scripts allow-same-origin"/);
|
||||
assert.match(adapter, /data-form-mode=\{config\.mode\}/);
|
||||
assert.match(adapter, /data-form-environment=\{config\.environmentRole\}/);
|
||||
assert.match(adapter, /data-jotform-iframe-frame/);
|
||||
assert.match(adapter, /data-jotform-iframe-fallback hidden aria-live="polite" role="status"/);
|
||||
assert.match(adapter, /<noscript>/);
|
||||
|
||||
@@ -36,18 +36,41 @@ function hasTerminalSlash(url) {
|
||||
return pathname !== '/' && pathname.endsWith('/');
|
||||
}
|
||||
|
||||
function unsafeSignals(html) {
|
||||
const formAProductionUrls = new Map([
|
||||
['/contact', 'https://form.jotform.com/262946078314059?formContext=contact&language=en'],
|
||||
['/es/contact', 'https://form.jotform.com/262946078314059?formContext=contact&language=es'],
|
||||
['/lp/aba-therapy', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['/lp/aba-insurance', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['/lp/aba-ahcccs', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['/lp/aba-private-pay', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['/lp/early-intervention', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
]);
|
||||
|
||||
function iframeSources(html) {
|
||||
// Astro correctly escapes query separators in serialized HTML attributes.
|
||||
// Normalize that representation before comparing the fixed allowlisted URL.
|
||||
return [...html.matchAll(/<iframe\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi)].map((match) => match[1].replaceAll('&', '&'));
|
||||
}
|
||||
|
||||
function formAAdapters(html) {
|
||||
return [...html.matchAll(/<section\b[^>]*\bdata-jotform-form-a\b[^>]*>/gi)].map((match) => match[0]);
|
||||
}
|
||||
|
||||
function unsafeSignals(html, allowApprovedFormA = false) {
|
||||
const signals = [];
|
||||
if (/<iframe\b[^>]*\bsrc=["'][^"']*jotform/i.test(html)) signals.push('active Jotform iframe');
|
||||
if (/262946078314059/i.test(html)) signals.push('isolated-test Form A identifier');
|
||||
if (!allowApprovedFormA && /<iframe\b[^>]*\bsrc=["'][^"']*jotform/i.test(html)) signals.push('active Jotform iframe');
|
||||
if (!allowApprovedFormA && /262946078314059/i.test(html)) signals.push('isolated-test Form A identifier');
|
||||
if (/<script\b[^>]*\bsrc=["'][^"']*(?:jotform|googletagmanager|google-analytics|doubleclick)/i.test(html)) signals.push('active vendor tracking/form script');
|
||||
if (/data-(?:form|forms)-mode=["']live["']/i.test(html)) signals.push('live form mode');
|
||||
if (!allowApprovedFormA && /data-(?:form|forms)-mode=["']live["']/i.test(html)) signals.push('live form mode');
|
||||
if (/api\.azinstitute4autism\.com/i.test(html)) signals.push('production API origin');
|
||||
if (/AIza[0-9A-Za-z_-]{20,}|-----BEGIN (?:RSA|OPENSSH|EC) PRIVATE KEY-----|(?:api[_-]?key|secret|password)\s*[:=]/i.test(html)) signals.push('credential-like content');
|
||||
return signals;
|
||||
}
|
||||
|
||||
export async function verifyArtifact(root, mode = 'staging') {
|
||||
const formAEnabled = mode === 'isolated-test' || mode === 'production';
|
||||
const production = mode === 'production';
|
||||
const stagingPolicy = mode === 'staging' || mode === 'isolated-test';
|
||||
const htmlFiles = (await fg('**/*.html', { cwd: root, absolute: true })).sort();
|
||||
const routes = [];
|
||||
const canonicalMap = new Map();
|
||||
@@ -56,6 +79,7 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
let hreflangLinks = 0;
|
||||
let activeForms = 0;
|
||||
let secretSignals = [];
|
||||
let formAIframes = 0;
|
||||
|
||||
for (const file of htmlFiles) {
|
||||
const html = await readText(file);
|
||||
@@ -66,12 +90,22 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
const actions = formActions(html);
|
||||
const nonSearchActions = actions.filter((action) => action && action !== '/search');
|
||||
const trailingSlashUrls = rootRelativeUrls(html).filter(hasTerminalSlash);
|
||||
const signals = unsafeSignals(html);
|
||||
const expectedFormAUrl = formAEnabled ? formAProductionUrls.get(route) : undefined;
|
||||
const signals = unsafeSignals(html, formAEnabled);
|
||||
const jotformFrames = iframeSources(html).filter((src) => {
|
||||
try {
|
||||
return new URL(src).origin === 'https://form.jotform.com';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const adapters = formAAdapters(html);
|
||||
if (!title) errors.push(`${route}: missing title`);
|
||||
if (!canonical) errors.push(`${route}: missing canonical`);
|
||||
if (!robots) errors.push(`${route}: missing robots directive`);
|
||||
if (trailingSlashUrls.length) errors.push(`${route}: root-relative URL(s) end in a slash: ${trailingSlashUrls.join(', ')}`);
|
||||
if (mode === 'staging' && robots !== 'noindex,nofollow') errors.push(`${route}: staging robots is ${robots ?? 'missing'}`);
|
||||
if (stagingPolicy && robots !== 'noindex,nofollow') errors.push(`${route}: staging robots is ${robots ?? 'missing'}`);
|
||||
if (production && robots === 'noindex,nofollow') errors.push(`${route}: production artifact is noindex`);
|
||||
if (canonical) {
|
||||
const prior = canonicalMap.get(canonical);
|
||||
if (prior) errors.push(`${route}: duplicate canonical ${canonical} (also ${prior})`);
|
||||
@@ -79,6 +113,19 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
}
|
||||
hreflangLinks += hrefValues(html, 'alternate').filter((href) => href).length;
|
||||
if (signals.length) secretSignals.push({ route, signals });
|
||||
if (formAEnabled) {
|
||||
if (expectedFormAUrl) {
|
||||
if (jotformFrames.length !== 1 || jotformFrames[0] !== expectedFormAUrl) errors.push(`${route}: missing or invalid approved Form A iframe URL`);
|
||||
const expectedMode = production ? 'live' : 'isolated-test';
|
||||
const expectedEnvironment = production ? 'production' : 'isolated-test';
|
||||
if (adapters.length !== 1 || !adapters[0].includes(`data-form-mode="${expectedMode}"`) || !adapters[0].includes(`data-form-environment="${expectedEnvironment}"`)) {
|
||||
errors.push(`${route}: Form A adapter lacks the approved ${expectedMode} ${expectedEnvironment} gate`);
|
||||
}
|
||||
formAIframes += jotformFrames.length;
|
||||
} else if (jotformFrames.length || adapters.length || /262946078314059/i.test(html)) {
|
||||
errors.push(`${route}: unauthorized Form A iframe or identifier in production artifact`);
|
||||
}
|
||||
}
|
||||
if (nonSearchActions.length) activeForms += nonSearchActions.length;
|
||||
routes.push({ route, title, robots, canonical, nativeForms: actions.length, nonSearchActions: nonSearchActions.length });
|
||||
}
|
||||
@@ -87,7 +134,7 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
const sitemap = await fs.readFile(sitemapFile, 'utf8').catch(() => '');
|
||||
const sitemapRoutes = [...sitemap.matchAll(/<loc>([^<]+)<\/loc>/gi)].map((match) => match[1]);
|
||||
const reviewIndex = htmlFiles.some((file) => routeFromHtml(file, root) === '/lp');
|
||||
if (mode === 'staging') {
|
||||
if (stagingPolicy) {
|
||||
if (sitemapRoutes.length) errors.push(`staging sitemap contains ${sitemapRoutes.length} URLs`);
|
||||
if (hreflangLinks) errors.push(`staging emits ${hreflangLinks} hreflang links`);
|
||||
if (!reviewIndex) errors.push('staging /lp review index is missing');
|
||||
@@ -112,7 +159,14 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
digest.update(createHash('sha256').update(await fs.readFile(file)).digest('hex'));
|
||||
digest.update('\n');
|
||||
}
|
||||
if (formAResourceFiles.length) errors.push(`isolated-test Form A identifier appears in artifact: ${formAResourceFiles.join(', ')}`);
|
||||
if (formAEnabled) {
|
||||
const expectedFormAFiles = new Set([...formAProductionUrls.keys()].map((route) => route === '/' ? 'index.html' : `${route.slice(1)}/index.html`));
|
||||
const unexpectedFormAFiles = formAResourceFiles.filter((file) => !expectedFormAFiles.has(file));
|
||||
if (unexpectedFormAFiles.length) errors.push(`Form A identifier appears outside approved production routes: ${unexpectedFormAFiles.join(', ')}`);
|
||||
if (formAIframes !== formAProductionUrls.size) errors.push(`production artifact has ${formAIframes} approved Form A iframe(s), expected ${formAProductionUrls.size}`);
|
||||
} else if (formAResourceFiles.length) {
|
||||
errors.push(`isolated-test Form A identifier appears in artifact: ${formAResourceFiles.join(', ')}`);
|
||||
}
|
||||
return {
|
||||
mode,
|
||||
htmlRoutes: htmlFiles.length,
|
||||
@@ -120,6 +174,7 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
hreflangLinks,
|
||||
reviewIndex,
|
||||
activeNativeFormActions: activeForms,
|
||||
formAIframes,
|
||||
artifactFiles: files.length,
|
||||
artifactSha256: digest.digest('hex'),
|
||||
routes,
|
||||
@@ -132,7 +187,7 @@ export async function verifyArtifact(root, mode = 'staging') {
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)) {
|
||||
const mode = process.argv.find((arg) => arg.startsWith('--mode='))?.slice('--mode='.length) ?? 'staging';
|
||||
const root = process.argv.find((arg) => arg.startsWith('--root='))?.slice('--root='.length) ?? 'dist';
|
||||
if (!['staging', 'indexing'].includes(mode)) throw new Error(`Unsupported mode: ${mode}`);
|
||||
if (!['staging', 'indexing', 'isolated-test', 'production'].includes(mode)) throw new Error(`Unsupported mode: ${mode}`);
|
||||
const result = await verifyArtifact(path.resolve(root), mode);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.passed) process.exitCode = 1;
|
||||
|
||||
@@ -7,15 +7,31 @@ import { verifyArtifact } from './phase10-artifact-verify.mjs';
|
||||
|
||||
async function fixture(mode = 'staging') {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'aia-phase10-artifact-'));
|
||||
const productionFrames = [
|
||||
['contact/index.html', 'https://form.jotform.com/262946078314059?formContext=contact&language=en'],
|
||||
['es/contact/index.html', 'https://form.jotform.com/262946078314059?formContext=contact&language=es'],
|
||||
['lp/aba-therapy/index.html', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['lp/aba-insurance/index.html', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['lp/aba-ahcccs/index.html', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['lp/aba-private-pay/index.html', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
['lp/early-intervention/index.html', 'https://form.jotform.com/262946078314059?formContext=landing&language=en'],
|
||||
];
|
||||
const pages = mode === 'staging'
|
||||
? [['index.html', 'noindex,nofollow'], ['lp/index.html', 'noindex,nofollow']]
|
||||
: [['index.html', 'index,follow']];
|
||||
for (const [file, robots] of pages) {
|
||||
? [['index.html', 'noindex,nofollow', ''] , ['lp/index.html', 'noindex,nofollow', '']]
|
||||
: mode === 'isolated-test'
|
||||
? [
|
||||
...productionFrames.map(([file, frame]) => [file, 'noindex,nofollow', `<section data-jotform-form-a data-form-mode="isolated-test" data-form-environment="isolated-test"><iframe src="${frame}"></iframe></section>`]),
|
||||
['lp/index.html', 'noindex,nofollow', ''],
|
||||
]
|
||||
: mode === 'production'
|
||||
? productionFrames.map(([file, frame]) => [file, 'index,follow', `<section data-jotform-form-a data-form-mode="live" data-form-environment="production"><iframe src="${frame}"></iframe></section>`])
|
||||
: [['index.html', 'index,follow', '']];
|
||||
for (const [file, robots, body] of pages) {
|
||||
const target = path.join(root, file);
|
||||
await fs.mkdir(path.dirname(target), { recursive: true });
|
||||
await fs.writeFile(target, `<!doctype html><html><head><title>${file}</title><meta name="robots" content="${robots}"><link rel="canonical" href="https://www.azinstitute4autism.com/${file}"></head><body><main><h1>${file}</h1></main></body></html>`);
|
||||
await fs.writeFile(target, `<!doctype html><html><head><title>${file}</title><meta name="robots" content="${robots}"><link rel="canonical" href="https://www.azinstitute4autism.com/${file}"></head><body><main><h1>${file}</h1>${body}</main></body></html>`);
|
||||
}
|
||||
await fs.writeFile(path.join(root, 'sitemap.xml'), mode === 'staging' ? '<urlset></urlset>' : '<urlset><url><loc>https://www.azinstitute4autism.com/</loc></url></urlset>');
|
||||
await fs.writeFile(path.join(root, 'sitemap.xml'), ['staging', 'isolated-test'].includes(mode) ? '<urlset></urlset>' : '<urlset><url><loc>https://www.azinstitute4autism.com/</loc></url></urlset>');
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -33,6 +49,31 @@ test('indexing artifact requires sitemap and excludes review index', async () =>
|
||||
assert.equal(result.reviewIndex, false);
|
||||
});
|
||||
|
||||
test('isolated-test artifact permits exactly the seven approved Form A iframe placements under staging safeguards', async () => {
|
||||
const result = await verifyArtifact(await fixture('isolated-test'), 'isolated-test');
|
||||
assert.equal(result.passed, true);
|
||||
assert.equal(result.formAIframes, 7);
|
||||
assert.equal(result.sitemapUrls, 0);
|
||||
assert.equal(result.reviewIndex, true);
|
||||
});
|
||||
|
||||
test('production artifact permits exactly the seven approved Form A iframe placements', async () => {
|
||||
const result = await verifyArtifact(await fixture('production'), 'production');
|
||||
assert.equal(result.passed, true);
|
||||
assert.equal(result.formAIframes, 7);
|
||||
assert.equal(result.reviewIndex, false);
|
||||
});
|
||||
|
||||
test('production artifact rejects an invalid Form A URL', async () => {
|
||||
const root = await fixture('production');
|
||||
const contact = path.join(root, 'contact/index.html');
|
||||
const html = await fs.readFile(contact, 'utf8');
|
||||
await fs.writeFile(contact, html.replace('formContext=contact&language=en', 'formContext=landing&language=en'));
|
||||
const result = await verifyArtifact(root, 'production');
|
||||
assert.equal(result.passed, false);
|
||||
assert.ok(result.errors.some((error) => error.includes('invalid approved Form A iframe URL')));
|
||||
});
|
||||
|
||||
test('staging artifact rejects active native form actions', async () => {
|
||||
const root = await fixture('staging');
|
||||
await fs.appendFile(path.join(root, 'index.html'), '<form action="https://example.invalid/submit"></form>');
|
||||
|
||||
Reference in new issue
Block a user