Verify trusted-peer proxy help in the shipped Docs viewer

Exercise the real guide, safe copied Nginx block and keyboard links on desktop Chromium and narrow phone WebKit. This renders guidance only; it is not native proxy, TLS or SSO acceptance.

Contract-Neutral: Browser test harness only; no frontend runtime, authentication or proxy contract change.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-03 19:35:12 +01:00
parent cbbed83add
commit d36dba5dff
@@ -0,0 +1,120 @@
// Real shipped Docs/router/CSS, with no backend, proxy or real credential.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { createHash } = require('node:crypto');
const { chromium, webkit } = require('playwright');
(async () => {
const workspace = process.cwd();
const root = path.join(workspace, 'frontend-modern');
const output = path.join(workspace, 'tmp', 'reverse-proxy-help');
fs.mkdirSync(output, { recursive: true });
const binding = JSON.parse(fs.readFileSync(path.join(output, 'binding.json'), 'utf8'));
assert.match(binding.sourceSha, /^[0-9a-f]{40}$/);
for (const [file, digest] of Object.entries(binding.contentSha256)) {
assert.equal(
createHash('sha256').update(fs.readFileSync(path.join(workspace, file))).digest('hex'),
digest,
file,
);
}
process.chdir(root);
const { createServer } = await import(path.join(root, 'node_modules/vite/dist/node/index.js'));
const server = await createServer({
root,
configFile: path.join(root, 'vite.config.ts'),
cacheDir: path.join(root, 'node_modules', '.vite-reverse-proxy-help'),
server: { host: '127.0.0.1', port: 5268, strictPort: true },
optimizeDeps: { noDiscovery: true, include: [] },
});
const observations = [];
let browser;
const inViewport = (id) => {
const heading = document.getElementById(id);
return heading && heading.getBoundingClientRect().top >= 0 &&
heading.getBoundingClientRect().top < innerHeight;
};
try {
await server.listen();
for (const [engine, width, height, tone] of [
['chromium', 1280, 1000, 'light'], ['webkit', 390, 844, 'dark'],
]) {
browser = await { chromium, webkit }[engine].launch(engine === 'chromium'
? { headless: true, channel: 'chromium', args: ['--no-sandbox'] }
: { headless: true });
const page = await browser.newPage({
viewport: { width, height },
...(engine === 'webkit' ? { isMobile: true, hasTouch: true } : {}),
});
const errors = [];
page.on('pageerror', (error) => errors.push(error.message));
page.on('console', (message) => {
if (message.type() === 'error') errors.push(message.text());
});
await page.goto('http://127.0.0.1:5268/browser-tests/docs-fragment-navigation.html');
if (tone === 'dark') await page.evaluate(() => document.documentElement.classList.add('dark'));
await page.getByRole('link', { name: '← All documentation' }).click();
const entry = page.locator('a[data-doc-link][href="/docs/REVERSE_PROXY"]').first();
await entry.focus();
await page.keyboard.press('Enter');
const heading = page.locator('#before-configuring-the-proxy');
await heading.waitFor();
await heading.scrollIntoViewIfNeeded();
let text = await page.locator('article').innerText();
for (const phrase of [
'configured immediate peer', 'PULSE_TRUSTED_PROXY_CIDRS=127.0.0.1/32',
'Wildcard ranges', 'rejected at startup', 'not an authentication bypass',
'Adding the header alone is not sufficient',
]) assert.ok(text.includes(phrase), phrase);
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1));
await page.screenshot({ path: path.join(output, `${engine}-trust.png`) });
const nginx = page.locator('pre').filter({ hasText: 'proxy_set_header X-Forwarded-For $remote_addr;' });
await nginx.scrollIntoViewIfNeeded();
const copied = await nginx.innerText();
assert.ok(copied.includes('proxy_set_header X-Forwarded-Proto $scheme;'));
assert.ok(copied.includes('proxy_set_header Forwarded "";'));
assert.ok(!copied.includes('$proxy_add_x_forwarded_for'));
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1));
await page.screenshot({ path: path.join(output, `${engine}-nginx.png`) });
const configuration = page.getByRole('link', { name: 'configuration overrides', exact: true });
await configuration.focus();
await page.keyboard.press('Enter');
await page.waitForFunction(inViewport, 'common-overrides-environment-variables');
text = await page.locator('article').innerText();
assert.ok(text.includes('forwarded client IP, scheme, host and port'));
const back = page.getByRole('link', { name: 'Reverse Proxy', exact: true });
await back.focus();
await page.keyboard.press('Enter');
await page.waitForFunction(inViewport, 'before-configuring-the-proxy');
assert.ok(page.url().endsWith('/docs/REVERSE_PROXY#before-configuring-the-proxy'));
const authentication = page.getByRole('link', { name: 'proxy authentication guide', exact: true });
await authentication.focus();
await page.keyboard.press('Enter');
await page.waitForFunction(inViewport, '-header-trust-boundary');
assert.ok(page.url().endsWith('/docs/PROXY_AUTH#-header-trust-boundary'));
assert.ok((await page.locator('article').innerText()).includes('never append to them'));
assert.deepEqual(errors, []);
observations.push({
engine, width, height, tone, browserVersion: browser.version(),
trustWarningRendered: true, safeNginxBlockRendered: true,
keyboardConfigurationRoundTrip: true, keyboardAuthenticationBoundary: true,
noPageOverflow: true, errors,
});
await browser.close();
browser = undefined;
}
const result = {
result: 'passed', playwrightVersion: require('playwright/package.json').version,
sourceSha: binding.sourceSha, verifiedAt: new Date().toISOString(),
contentSha256: binding.contentSha256, dependencySnapshot: binding.dependencySnapshot,
scope: 'Production Docs rendering/navigation; not native proxy configuration, TLS or SSO acceptance',
observations,
};
fs.writeFileSync(path.join(output, 'result.json'), JSON.stringify(result, null, 2) + '\n');
console.log(JSON.stringify(result));
} finally {
if (browser) await browser.close();
await server.close();
}
})().catch((error) => { console.error(error); process.exitCode = 1; });