Ctrl K
ComunidadGuías…

scripts/marketing-screenshots/mocks.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { Page, Route } from '@playwright/test'
import * as fx from './fixtures'

// Route mocks for everything the local stack cannot serve with realistic data:
// analytics-backed edge endpoints (stats, notifications, devices, keys,
// webhooks) and the team/audit RPCs whose seed rows would expose test users.

const ORG_ID = '046a36ac-e03c-4590-9257-bd6c9dba9ee8'
const PASSWORD_POLICY = { enabled: true, min_length: 12, require_uppercase: true, require_number: true, require_special: true }

function json(route: Route, body: unknown, headers: Record<string, string> = {}) {
  return route.fulfill({
    status: 200,
    contentType: 'application/json',
    headers: { 'access-control-allow-origin': '*', ...headers },
    body: typeof body === 'string' ? body : JSON.stringify(body),
  })
}

function restPath(name: string) {
  return (url: URL) => url.pathname.endsWith(`/rest/v1/${name}`)
}

function edgeResponse(path: string, url: URL, body: any): unknown {
  const days = Number(body.days ?? 7)
  if (path === '/private/sso/check-enforcement')
    return { allowed: true }
  if (path === '/private/sso/check-domain')
    return { has_sso: false }
  // Production builds fetch remote config; an empty object keeps the local defaults.
  if (path === '/private/config')
    return {}
  if (path === '/private/plans')
    return []
  if (path.startsWith('/statistics/org/'))
    return { global: [], byApp: [] }
  if (path === '/private/events')
    return { status: 'ok' }
  if (path === '/private/native_observe_stats')
    return body.view === 'plugins' ? fx.pluginAdoption() : fx.nativeObserve(days, body.version_group ?? 'version')
  if (path === '/private/stats/insights')
    return fx.insights(days, body.versionName)
  if (path === '/private/stats')
    return body.devicesId?.length ? fx.deployments(body.devicesId[0]) : fx.logs(body.rangeEnd)
  if (path === '/private/devices')
    return fx.devices(body)
  if (path === '/private/release_live')
    return fx.releaseLive()
  if (path === '/private/bundle_install_stats')
    return fx.bundleInstallStats(days)
  if (path === '/private/update_delivery_stats')
    return fx.deliveryStats(days)
  if (path === '/notifications/providers')
    return fx.notifProviders()
  if (path === '/notifications/campaigns')
    return fx.notifCampaigns()
  if (path === '/notifications/stats')
    return fx.notifStats(url.searchParams.get('campaign_id'))
  if (path === '/notifications/settings')
    return fx.notifSettings
  if (path === '/apikey')
    return fx.apiKeys()
  if (path === '/webhooks')
    return fx.webhooks()
  if (path === '/webhooks/deliveries')
    return fx.webhookDeliveries(url.searchParams.get('webhookId') ?? 'wh-1')
  if (path.startsWith('/private/sso/providers'))
    return fx.ssoProviders()
  if (path === '/private/sso/sp-metadata')
    return fx.ssoMetadata
  if (path.startsWith('/private/role_bindings/'))
    return []
  const usage = path.match(/\/statistics\/app\/[^/]+\/(bundle_usage|native_usage)/)
  if (usage)
    return fx.usage(usage[1] as 'bundle_usage' | 'native_usage', url.searchParams.get('from')!, url.searchParams.get('to')!)
  return undefined
}

export async function installMocks(page: Page) {
  await page.route('**/functions/v1/**', async (route) => {
    const request = route.request()
    if (request.method() === 'OPTIONS') {
      return route.fulfill({
        status: 204,
        headers: { 'access-control-allow-origin': '*', 'access-control-allow-headers': '*', 'access-control-allow-methods': '*' },
      })
    }
    const url = new URL(request.url())
    const path = url.pathname.replace(/^.*\/functions\/v1/, '')
    let body: any = {}
    try {
      body = request.postDataJSON() ?? {}
    }
    catch {}
    const response = edgeResponse(path, url, body)
    if (response === undefined)
      console.warn(`[marketing-screenshots] unmocked ${request.method()} ${path}`)
    return json(route, response ?? {})
  })

  await page.route(restPath('compatibility_events'), (route) => {
    const rows = fx.compatibilityEvents().filter(row => !route.request().url().includes('resolved_at=is.null') || !row.resolved_at)
    return json(route, route.request().method() === 'HEAD' ? '' : rows, { 'Content-Range': `0-${rows.length - 1}/${rows.length}` })
  })
  await page.route(restPath('audit_logs'), (route) => {
    const rows = fx.auditLogs()
    return json(route, route.request().method() === 'HEAD' ? '' : rows, { 'Content-Range': `0-${rows.length - 1}/${rows.length}` })
  })
  await page.route(restPath('rpc/get_org_members_rbac'), route => json(route, fx.orgMembersRbac()))
  await page.route(restPath('rpc/get_org_members'), route => json(route, fx.orgMembers()))
  await page.route(restPath('rpc/check_org_members_2fa_enabled'), route => json(route, fx.members2fa()))
  await page.route(restPath('rpc/check_org_members_password_policy'), route => json(route, fx.people.map(p => ({ user_id: p.uid, password_policy_compliant: true, first_name: '', last_name: '' }))))
  await page.route(restPath('rpc/get_app_access_rbac'), route => json(route, fx.appAccess()))
  // The live panel falls back to this query for rollout state; the mocked release payload already carries it.
  await page.route(url => restPath('channels')(url) && url.searchParams.get('select')?.startsWith('rollout_enabled') === true, route => json(route, []))
  // Hide the "deploy the latest bundle" banner: the demo intentionally keeps production behind beta.
  await page.route(url => restPath('channels')(url) && url.searchParams.get('select')?.startsWith('id,name,ios') === true, route => json(route, []))
  await page.route('https://registry.npmjs.org/**', route => json(route, { 'dist-tags': { 'latest': '8.42.3', 'lts-v7': '7.51.23', 'lts-v6': '6.51.23', 'lts-v5': '5.51.23' } }))

  // Present a hardened org (2FA, password policy) without gating the seed login.
  const patchJson = (match: (url: URL) => boolean, patch: (body: any) => any) => page.route(match, async (route) => {
    const res = await route.fetch()
    let body: any
    try {
      body = await res.json()
    }
    catch {
      return route.fulfill({ response: res })
    }
    return route.fulfill({ response: res, body: JSON.stringify(patch(body)) })
  })
  const hardenOrg = (org: any) => ({ ...org, enforcing_2fa: true, enforce_hashed_api_keys: true, password_policy_config: PASSWORD_POLICY })
  await patchJson(restPath('rpc/get_orgs_v7'), body => Array.isArray(body)
    ? body.map((org: any) => org.gid === ORG_ID ? { ...hardenOrg(org), '2fa_has_access': true, 'password_has_access': true } : org)
    : body)
  await patchJson(restPath('orgs'), body => Array.isArray(body) ? body.map(hardenOrg) : hardenOrg(body))
}

const IDENTITY_MAP: Record<string, string> = {
  'test@capgo.app': 'demo@capgo.app',
  'test2@capgo.app': 'maya@example.com',
  'admin@capgo.app': 'jordan@example.com',
  'test Capgo': 'Demo User',
}

/** Replace seed-account identities left in the rendered page with neutral demo ones. */
export async function maskIdentities(page: Page) {
  await page.evaluate((map) => {
    const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT)
    for (let node = walker.nextNode(); node; node = walker.nextNode()) {
      for (const [from, to] of Object.entries(map)) {
        if (node.nodeValue?.includes(from))
          node.nodeValue = node.nodeValue.replaceAll(from, to)
      }
    }
  }, IDENTITY_MAP)
}