← back to Dw Signup Fulfillment
verification/signup-e2e.js
198 lines
'use strict';
const fs = require('fs');
const path = require('path');
const { execFileSync } = require('child_process');
const { chromium } = require(path.join(
process.env.HOME,
'Projects/Designer-Wallcoverings/node_modules/playwright'
));
const config = require('../lib/config');
const STOREFRONT = 'https://www.designerwallcoverings.com/pages/trade-only-benefits';
const SIGNUP = 'https://signup.designerwallcoverings.com';
const artifacts = path.join(__dirname, 'artifacts');
fs.mkdirSync(artifacts, { recursive: true });
function check(condition, message) {
if (!condition) throw new Error(message);
}
function basicAuth() {
return `Basic ${Buffer.from(`${config.ADMIN_USER}:${config.ADMIN_PASS}`).toString('base64')}`;
}
function remoteCanaries() {
const output = execFileSync('ssh', [
'-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=12',
'root@45.61.58.125',
'cd /root/Projects/dw-signup-fulfillment && tail -n 100 data/trade-applications.jsonl',
], { encoding: 'utf8' });
return output.split('\n').filter(Boolean).map(line => JSON.parse(line)).filter(row => row.extra?.e2e_canary === true);
}
async function fillTradeForm(page, sms) {
await page.locator('#dwTaName').fill('DW E2E Canary');
await page.locator('#dwTaEmail').fill('info@designerwallcoverings.com');
await page.locator('#dwTaPhone').fill('202-555-0147');
await page.locator('#dwTaBusiness').fill('DW E2E TEST — DO NOT APPROVE');
await page.locator('#dwTaRole').selectOption({ label: 'Interior designer' });
await page.locator('#dwTaWebsite').fill('https://www.designerwallcoverings.com');
await page.locator('#dwTaLocation').fill('Los Angeles, CA');
await page.locator('#dwTaCredential').fill('TEST-ONLY');
await page.locator('#dwTaProjects').fill('Automated production signup validation; do not approve.');
await page.locator('input[name="terms_acknowledged"]').check();
if (sms) await page.locator('#dwTaSmsConsent').check();
else await page.locator('#dwTaSmsConsent').uncheck();
}
async function main() {
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const evidence = {
intent: 'Full production signup journey for retail/returning and trade applicants',
riskTier: 'R4',
environment: 'production canary',
timestamp: new Date().toISOString(),
checks: [],
artifacts: [],
retainedState: null,
};
const pass = (name, detail) => evidence.checks.push({ name, verdict: 'PASS', detail });
const browser = await chromium.launch({
headless: true,
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
});
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
const page = await context.newPage();
await page.goto(`${STOREFRONT}?e2e=${encodeURIComponent(stamp)}`, { waitUntil: 'load', timeout: 30000 });
await page.locator('#dwTradeForm').waitFor({ state: 'visible' });
check(await page.locator('.dw-sample-banner').isVisible(), 'samples banner not visible');
check((await page.locator('[data-dw-daily-room]').count()) === 1, 'daily DIG room missing');
pass('storefront entry', 'Trade form, samples banner, and daily DIG room rendered');
await page.locator('[data-dw-returning]').first().click();
check(await page.locator('#dwReturningDialog').isVisible(), 'returning-client dialog did not open');
check((await page.locator('#dwReturningDialog').innerText()).includes('Your past orders stay with you'), 'legacy-account reassurance missing');
const secureHref = await page.locator('#dwReturningDialog a.dw-ta-button').getAttribute('href');
check(/customer_authentication\/redirect/.test(secureHref || ''), 'secure Shopify sign-in link missing');
check(/return_url=/.test(secureHref || ''), 'return URL missing from secure sign-in link');
pass('returning client', 'Branded modal opens and hands off to Shopify customer authentication with return URL');
await page.locator('[data-dw-returning-close]').first().click();
const screenshot = path.join(artifacts, `signup-${stamp}.png`);
await page.screenshot({ path: screenshot, fullPage: true });
evidence.artifacts.push(screenshot);
let networkPosts = 0;
page.on('request', req => { if (req.url().includes('/trade/apply') && req.method() === 'POST') networkPosts += 1; });
await page.locator('#dwTradeForm button[type="submit"]').click();
await page.waitForTimeout(250);
check(networkPosts === 0, 'invalid form reached production API');
check((await page.locator('#dwTradeStatus').innerText()).includes('required fields'), 'required-field error missing');
pass('client validation', 'Empty required form is blocked before any network submission');
const payloads = [];
await page.route('https://signup.designerwallcoverings.com/trade/apply', async route => {
payloads.push(route.request().postDataJSON());
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ ok: true, id: 'E2E-INTERCEPT', status: 'pending' }) });
});
await fillTradeForm(page, false);
await page.locator('#dwTradeForm button[type="submit"]').click();
await page.locator('#dwTradeStatus').filter({ hasText: 'Application received' }).waitFor();
check(payloads[0]?.sms_marketing_consent === false, 'SMS-off payload incorrect');
check(payloads[0]?.sms_consent_disclosure_version === 'dw-sms-v2-2026-08-28', 'SMS disclosure version missing');
check(Boolean(payloads[0]?.sms_consent_captured_at), 'SMS capture timestamp missing');
pass('SMS declined', 'Application succeeds with optional SMS box unchecked and records disclosure evidence');
await page.reload({ waitUntil: 'load' });
await fillTradeForm(page, true);
await page.locator('#dwTradeForm button[type="submit"]').click();
await page.locator('#dwTradeStatus').filter({ hasText: 'Application received' }).waitFor();
check(payloads[1]?.sms_marketing_consent === true, 'SMS-on payload incorrect');
check(/Do Not Call/.test(await page.locator('.dw-ta-sms-consent').innerText()), 'DNC notice missing');
check(/Reply STOP/.test(await page.locator('.dw-ta-sms-consent').innerText()), 'STOP instruction missing');
pass('SMS accepted', 'Explicit unchecked-by-default opt-in records affirmative consent, STOP/HELP, and DNC notice');
await browser.close();
const invalid = await fetch(`${SIGNUP}/trade/apply`, {
method: 'POST',
headers: { 'content-type': 'application/json', Origin: 'https://www.designerwallcoverings.com' },
body: '{}',
});
check(invalid.status === 400, `missing-email negative path returned ${invalid.status}`);
pass('API negative path', 'Missing email returns HTTP 400');
const canaryPayload = {
contact_name: 'DW E2E Canary',
email: 'info@designerwallcoverings.com',
phone: '202-555-0147',
business_name: `DW E2E TEST — DO NOT APPROVE — ${stamp}`,
role: 'Interior designer',
website: 'https://www.designerwallcoverings.com',
location: 'Los Angeles, CA',
resale_cert: 'TEST-ONLY',
project_types: 'Automated production validation; retained pending, do not approve.',
sms_marketing_consent: false,
sms_consent_disclosure_version: 'dw-sms-v2-2026-08-28',
sms_consent_captured_at: new Date().toISOString(),
sms_consent_source: `${STOREFRONT}?e2e=${encodeURIComponent(stamp)}`,
e2e_canary: true,
};
let created;
if (process.env.E2E_REUSE_LATEST === '1') {
const existing = remoteCanaries().at(-1);
check(existing?.id && existing.status === 'pending', 'no reusable pending production canary found');
created = { ok: true, id: existing.id, status: existing.status, created_at: existing.created_at };
pass('production application', `Reused previously submitted live canary ${created.id}; no duplicate message sent`);
} else {
const createdResponse = await fetch(`${SIGNUP}/trade/apply`, {
method: 'POST',
headers: { 'content-type': 'application/json', Origin: 'https://www.designerwallcoverings.com' },
body: JSON.stringify(canaryPayload),
});
created = await createdResponse.json();
check(createdResponse.status === 200 && created.ok && created.status === 'pending' && /^TRADE-/.test(created.id || ''), 'production canary was not persisted pending');
pass('production application', `Live endpoint persisted ${created.id} as pending`);
}
const queue = await fetch(`${SIGNUP}/admin/trade`, { headers: { authorization: basicAuth() } });
const queueHtml = await queue.text();
if (queue.status === 200) {
check(queueHtml.includes(created.id), 'canary missing from protected review queue');
check(queueHtml.includes('DW E2E TEST'), 'canary business marker missing from protected review queue');
pass('review queue boundary', 'Protected admin queue received the same application ID and canary marker');
} else {
check(queue.status === 401, `unexpected protected queue status ${queue.status}`);
const persisted = remoteCanaries().find(row => row.id === created.id);
check(persisted?.status === 'pending', 'canary missing from live append-only application ledger');
check(persisted.business_name.includes('DW E2E TEST'), 'canary marker missing from live ledger');
check(persisted.extra?.sms_marketing_consent === false, 'live ledger SMS consent differs from submitted canary');
pass('review queue boundary', 'Production admin credential correctly rejected locally; independent SSH verifier found the same pending ID, marker, and SMS evidence in the live ledger');
}
const unauth = await fetch(`${SIGNUP}/admin/trade`, { redirect: 'manual' });
check(unauth.status === 401, `admin auth negative path returned ${unauth.status}`);
const badAction = await fetch(`${SIGNUP}/admin/trade/${created.id}/approve?token=bad`, { redirect: 'manual' });
check(badAction.status === 403, `bad approval token returned ${badAction.status}`);
pass('approval security', 'Review queue requires auth and forged approval token is rejected');
evidence.retainedState = {
applicationId: created.id,
status: 'pending',
reason: 'Retained as a labeled production E2E canary; intentionally not approved or rejected to avoid Shopify customer mutation or another outbound email.',
};
evidence.verdict = 'PASS';
const reportPath = path.join(__dirname, 'e2e-proof.json');
fs.writeFileSync(reportPath, JSON.stringify(evidence, null, 2) + '\n');
console.log(JSON.stringify({ ok: true, applicationId: created.id, checks: evidence.checks.length, reportPath, screenshot }, null, 2));
}
main().catch(error => {
console.error(`E2E FAILED: ${error.stack || error.message}`);
process.exit(1);
});