← back to Dw Signup Fulfillment

verification/shopify-account-e2e.js

208 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 shopify = require('../lib/shopify');

const STOREFRONT = 'https://www.designerwallcoverings.com/pages/trade-only-benefits';
const SAMPLE_VARIANT_ID = 13960778743920;
const artifacts = path.join(__dirname, 'artifacts');
fs.mkdirSync(artifacts, { recursive: true });

function check(condition, message) {
  if (!condition) throw new Error(message);
}

function remoteGeorge(endpoint) {
  const source = `
    const config = require('./lib/config');
    const auth = 'Basic ' + Buffer.from(config.GEORGE_BASIC_AUTH).toString('base64');
    fetch('http://127.0.0.1:9850' + Buffer.from(process.argv[1], 'base64').toString(), {
      headers: { authorization: auth }
    }).then(async response => {
      const body = await response.text();
      if (!response.ok) throw new Error(response.status + ' ' + body);
      process.stdout.write(body);
    }).catch(error => { console.error(error.message); process.exit(1); });
  `;
  const encodedSource = Buffer.from(source).toString('base64');
  const encodedEndpoint = Buffer.from(endpoint).toString('base64');
  const command = `cd /root/Projects/dw-signup-fulfillment && node -e "eval(Buffer.from('${encodedSource}','base64').toString())" '${encodedEndpoint}'`;
  return JSON.parse(execFileSync('ssh', [
    '-o', 'BatchMode=yes',
    '-o', 'ConnectTimeout=12',
    'root@45.61.58.125',
    command,
  ], { encoding: 'utf8', timeout: 30000 }));
}

async function waitForLoginCode(email, notBefore) {
  const query = `to:${email} newer_than:1d`;
  const deadline = Date.now() + 120000;
  while (Date.now() < deadline) {
    const search = remoteGeorge(`/api/search?account=steve-office&maxResults=10&q=${encodeURIComponent(query)}`);
    for (const item of search.messages || []) {
      const message = remoteGeorge(`/api/messages/${encodeURIComponent(item.id)}?account=steve-office`);
      if (Number(message.internalDate || 0) < notBefore - 30000) continue;
      const haystack = `${message.subject || ''}\n${message.snippet || ''}\n${message.body || ''}`;
      const code = haystack.match(/(?:code|verification)[^0-9]{0,80}([0-9]{6})/i)?.[1]
        || haystack.match(/\b([0-9]{6})\b/)?.[1];
      if (code) return code;
    }
    await new Promise(resolve => setTimeout(resolve, 3000));
  }
  throw new Error('Shopify login code did not arrive in the controlled mailbox within 120 seconds');
}

async function waitForShopifyCustomer(email) {
  const deadline = Date.now() + 90000;
  while (Date.now() < deadline) {
    const customerId = await shopify.findCustomerByEmail(email);
    if (customerId) return customerId;
    await new Promise(resolve => setTimeout(resolve, 5000));
  }
  return null;
}

async function main() {
  const runId = new Date().toISOString().replace(/[:.]/g, '-');
  const mode = process.env.DW_ACCOUNT_E2E_MODE === 'designer' ? 'designer' : 'retail';
  const email = process.env.DW_ACCOUNT_E2E_EMAIL
    || (mode === 'designer'
      ? 'steve+dw-e2e-designer@designerwallcoverings.com'
      : 'steve+dw-e2e-shopify@designerwallcoverings.com');
  const evidence = {
    intent: 'Prove a new controlled customer can enter Shopify email-code authentication and return to DW authenticated',
    environment: 'production controlled canary',
    timestamp: new Date().toISOString(),
    syntheticAccount: email,
    accountMode: mode,
    checks: [],
    artifacts: [],
  };
  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();

  try {
    await page.goto(`${STOREFRONT}?account_e2e=${encodeURIComponent(runId)}`, { waitUntil: 'load', timeout: 45000 });
    await page.locator('[data-dw-returning]').first().click();
    const authHref = await page.locator('#dwReturningDialog a.dw-ta-button').getAttribute('href');
    check(authHref && /customer_authentication\/redirect/.test(authHref), 'DW secure sign-in handoff is missing');
    pass('DW handoff', 'Returning-client action opens the branded reassurance dialog and supplies Shopify secure authentication');

    const sentAt = Date.now();
    await page.goto(authHref, { waitUntil: 'domcontentloaded', timeout: 45000 });
    const emailInput = page.locator('input[type="email"], input[autocomplete="email"]').first();
    await emailInput.waitFor({ state: 'visible', timeout: 30000 });
    await emailInput.fill(email);
    const beforeCode = path.join(artifacts, `shopify-account-email-${runId}.png`);
    await page.screenshot({ path: beforeCode, fullPage: true });
    evidence.artifacts.push(beforeCode);

    const continueButton = page.getByRole('button', { name: /continue|submit|sign in/i }).last();
    await continueButton.click();
    pass('account entry', 'A unique DW-owned synthetic email was accepted by Shopify customer authentication');

    const code = await waitForLoginCode(email, sentAt);
    const codeInputs = page.locator('input[inputmode="numeric"], input[autocomplete="one-time-code"]');
    await codeInputs.first().waitFor({ state: 'visible', timeout: 30000 });
    const count = await codeInputs.count();
    if (count === 1) {
      await codeInputs.first().fill(code);
    } else {
      for (let i = 0; i < Math.min(count, code.length); i += 1) await codeInputs.nth(i).fill(code[i]);
    }
    const verifyButton = page.getByRole('button', { name: /continue|submit|verify|sign in/i });
    if (await verifyButton.count()) await verifyButton.last().click();
    await page.waitForURL(url => !url.hostname.endsWith('shopify.com') || /account/.test(url.pathname), { timeout: 45000 });
    await page.waitForLoadState('domcontentloaded');
    pass('email-code verification', 'The Shopify one-time code arrived in the controlled DW mailbox and was accepted');

    const finalUrl = page.url();
    check(
      /shopify\.com\/1541177456\/account/.test(finalUrl) || /designerwallcoverings\.com/.test(finalUrl),
      `authentication did not reach the store's authenticated account surface: ${finalUrl}`
    );
    const accountText = await page.locator('body').innerText();
    check(/account|orders|profile|sign out/i.test(accountText), 'authenticated account controls were not visible');
    const afterAuth = path.join(artifacts, `shopify-account-authenticated-${runId}.png`);
    await page.screenshot({ path: afterAuth, fullPage: true });
    evidence.artifacts.push(afterAuth);
    pass('authenticated account', `Shopify opened the store's authenticated customer-account surface at ${new URL(finalUrl).origin}`);

    if (mode === 'designer') {
      const customerId = await waitForShopifyCustomer(email);
      check(customerId, 'controlled designer customer could not be resolved through Shopify Admin API');
      const tagged = await shopify.addTags(customerId, ['trade_approved']);
      check(tagged?.ok && !tagged?.dryRun, 'controlled designer account was not tagged trade_approved in production');
      evidence.syntheticCustomerId = String(customerId);
      pass('designer approval', 'The DW-owned synthetic customer received the exact authoritative trade_approved entitlement tag');
    }

    await page.goto(STOREFRONT, { waitUntil: 'load', timeout: 45000 });
    const accountLinks = page.locator('a[href*="/account"], a[href*="customer_authentication"]');
    check(await accountLinks.count() > 0, 'DW storefront no longer exposes an account route after authentication');
    pass('storefront return', 'The authenticated browser returned to DW and retained an accessible customer-account route');

    const cartResult = await page.evaluate(async variantId => {
      await fetch('/cart/clear.js', { method: 'POST', headers: { accept: 'application/json' } });
      const response = await fetch('/cart/add.js', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({ items: [{ id: variantId, quantity: 4 }] }),
      });
      return { status: response.status, body: await response.json() };
    }, SAMPLE_VARIANT_ID);
    check(cartResult.status === 200, `sample cart add returned ${cartResult.status}`);
    check(cartResult.body?.items?.[0]?.quantity === 4, 'sample cart did not retain quantity four');
    pass('sample cart', mode === 'designer'
      ? 'Four $4.25 sample units were added to the controlled approved-designer cart to test unlimited sample entitlement'
      : 'Four $4.25 sample units were added to the controlled retail cart to test the three-lifetime limit');

    await page.goto('https://www.designerwallcoverings.com/checkout', { waitUntil: 'domcontentloaded', timeout: 60000 });
    await page.waitForTimeout(8000);
    const checkoutText = (await page.locator('body').innerText()).replace(/\s+/g, ' ');
    const checkoutShot = path.join(artifacts, `shopify-account-checkout-${runId}.png`);
    await page.screenshot({ path: checkoutShot, fullPage: true });
    evidence.artifacts.push(checkoutShot);
    check(/free samples|discount/i.test(checkoutText), 'free-sample discount was not visible at checkout');
    if (mode === 'designer') {
      check(/\$17\.00/.test(checkoutText), 'approved designer checkout did not show all four samples discounted by $17.00');
      check(/(?:USD\s*)?\$0\.00/.test(checkoutText), 'approved designer checkout total was not $0.00 before shipping');
      pass('designer checkout boundary', 'At production checkout, an approved designer receives all four sample units free ($17.00 off); no order was submitted');
    } else {
      check(/\$12\.75/.test(checkoutText), 'checkout did not show the expected three-sample $12.75 discount');
      check(/\$4\.25/.test(checkoutText), 'checkout did not retain one paid $4.25 sample after the three free units');
      pass('retail checkout boundary', 'At production checkout, four samples receive exactly $12.75 off and retain one paid $4.25 unit; no order was submitted');
    }

    await page.goto('https://www.designerwallcoverings.com/cart/clear', { waitUntil: 'domcontentloaded', timeout: 30000 });

    evidence.verdict = 'PASS';
    evidence.retainedState = mode === 'designer'
      ? 'Synthetic DW-owned account retained with trade_approved for repeatable designer checkout validation; no real customer record was changed.'
      : 'Synthetic DW-owned retail account retained for repeatable checkout/sample validation; no real customer record was changed.';
    const report = path.join(__dirname, `shopify-account-${mode}-e2e.json`);
    fs.writeFileSync(report, `${JSON.stringify(evidence, null, 2)}\n`);
    console.log(JSON.stringify({ ok: true, email, checks: evidence.checks.length, finalUrl, report }, null, 2));
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(`SHOPIFY ACCOUNT E2E FAILED: ${error.stack || error.message}`);
  process.exit(1);
});