diff --git a/components/CompiledProgramSection.js b/components/CompiledProgramSection.js index b1729334..cfc4a83b 100644 --- a/components/CompiledProgramSection.js +++ b/components/CompiledProgramSection.js @@ -30,16 +30,12 @@ const FIGURES = [ eyebrow: 'Workflow catalog & halt map', title: 'Read the whole portfolio, halt by halt.', body: - 'The catalog is a read-only readout across every compiled ' + - 'workflow: what each one automates, how its trials went, and the ' + - 'return it stands to make. The step-level halt map shows where runs ' + - 'stop and why, so you fix the demonstration or policy at the exact ' + - 'step.', + 'The catalog is a read-only view of compiled workflows in the ' + + 'workspace. Review the current workflow state before you run it.', image: '/cloud-preview/workflow-catalog.png', alt: - 'OpenAdapt Cloud workflow catalog: a portfolio ROI readout with a ' + - 'step-level halt map showing where runs halt and why.', - caption: 'Workflow catalog & halt map in Cloud', + 'OpenAdapt Cloud workflow catalog for the synthetic OpenEMR fixture.', + caption: 'Workflow catalog in Cloud', }, ] diff --git a/components/DashboardShowcase.js b/components/DashboardShowcase.js index 422de2a9..a2914bcd 100644 --- a/components/DashboardShowcase.js +++ b/components/DashboardShowcase.js @@ -3,29 +3,24 @@ import styles from './DashboardShowcase.module.css' // Real screenshots of the shipping OpenAdapt Cloud product (openadapt-cloud), // captured from the actual dashboard UI. This is a rotating showcase: the large -// slot cycles through the real product frames (dashboard, run detail, halt -// evidence, program visualizer, workflow catalog) so each gets time on screen +// slot cycles through the real product frames (dashboard, run detail, program +// visualizer, workflow catalog) so each gets time on screen // big enough to read. Every frame is a full-height capture of the real // interface (see /product-preview/MANIFEST.json and /cloud-preview/provenance.json). // Nothing here is a hand-drawn or synthetic mockup of an app that does not exist. // -// The captures are full pages, not top crops, and the large stage reveals each -// one with a slow top-to-bottom vertical auto-pan over its dwell so the visitor -// sees the WHOLE page rather than just the first screenful. Under reduced motion -// the pan is dropped and the whole frame is shown at once (see the module CSS), -// so nothing depends on movement to be legible. +// The captures are focused viewport frames so the product state is readable. // // Order matters: the dashboard is first so it is the default (and SSR / no-JS) // frame, which is why the browser address bar reads app.openadapt.ai/dashboard // before any rotation. Every capture is 2560px wide at the same device scale, so -// the five frames share one product look; their heights differ because each real -// page is a different length. +// the four frames share one product look. const SLIDES = [ { key: 'dashboard', src: '/product-preview/dashboard-workflows.png', width: 2560, - height: 1600, + height: 1440, address: 'app.openadapt.ai/dashboard', label: 'Dashboard', caption: @@ -36,29 +31,18 @@ const SLIDES = [ key: 'run', src: '/cloud-preview/healthcare-run.jpg', width: 2560, - height: 4620, + height: 1440, address: 'app.openadapt.ai/runs', label: 'Run detail', caption: - 'Step timeline and independently verified effects for a completed run.', - alt: 'OpenAdapt Cloud run detail: run metrics, the verified-effect timeline, and the full machine-readable run report.', - }, - { - key: 'evidence', - src: '/cloud-preview/healthcare-evidence.jpg', - width: 2560, - height: 6594, - address: 'app.openadapt.ai/runs/evidence', - label: 'Halt evidence', - caption: - 'The locally reported stop, resolver metrics, and the governed repair page.', - alt: 'OpenAdapt Cloud halt evidence: the locally reported stop, resolver metrics, and the governed repair page.', + 'Evidence-qualified execution outcome for a completed run.', + alt: 'OpenAdapt Cloud run detail showing its evidence-qualified execution outcome and contract counts.', }, { key: 'program', src: '/cloud-preview/program-graph.png', width: 2560, - height: 3882, + height: 1440, address: 'app.openadapt.ai/workflows/program', label: 'Program visualizer', caption: @@ -69,12 +53,12 @@ const SLIDES = [ key: 'catalog', src: '/cloud-preview/workflow-catalog.png', width: 2560, - height: 4290, + height: 1440, address: 'app.openadapt.ai/workflows', label: 'Workflow catalog', caption: - 'Approved workflows, their ROI readout, and a step-level halt map in one catalog.', - alt: 'OpenAdapt Cloud workflow catalog: approved workflows, their ROI readout, and a step-level halt map.', + 'The current workflow catalog in the Cloud workspace.', + alt: 'OpenAdapt Cloud workflow catalog for the synthetic OpenEMR fixture.', }, ] diff --git a/cypress/e2e/dashboard-showcase.cy.js b/cypress/e2e/dashboard-showcase.cy.js index 3883ea86..9a45da7f 100644 --- a/cypress/e2e/dashboard-showcase.cy.js +++ b/cypress/e2e/dashboard-showcase.cy.js @@ -1,12 +1,4 @@ describe('Cloud product showcase', () => { - const TAB_LABELS = [ - 'Dashboard', - 'Run detail', - 'Halt evidence', - 'Program visualizer', - 'Workflow catalog', - ] - it('rotates the real hosted product through labeled tabs on desktop', () => { cy.viewport(1440, 1700) cy.visit('/hosted/welcome') @@ -14,42 +6,33 @@ describe('Cloud product showcase', () => { cy.get('#cloud-product').scrollIntoView().should('be.visible') cy.get('[data-testid="dashboard-product-preview"]').within(() => { - // Five real frames are stacked in the rotating stage; the dashboard - // is the default active frame, framed as app.openadapt.ai. - cy.get('[data-testid="dashboard-slide"]').should('have.length', 5) + // Every discovered tab must activate its matching, decoded slide. cy.get('[data-testid="dashboard-slide"]').each(($img) => { cy.wrap($img).should('have.attr', 'loading', 'lazy') }) - cy.get('[data-testid="dashboard-slide"][data-active="true"]') - .should('have.length', 1) - .and('have.attr', 'data-slide', 'dashboard') - .and('have.attr', 'src') - .and('include', '/product-preview/dashboard-workflows.png') - cy.contains('app.openadapt.ai/dashboard').should('be.visible') - - // Labeled, clickable tabs for every frame, plus progress dots. - cy.get('[data-testid="dashboard-tab"]').should('have.length', 5) - TAB_LABELS.forEach((label) => { - cy.get('[data-testid="dashboard-tabs"]').should( - 'contain.text', - label - ) - }) - cy.get('[data-testid="dashboard-dots"] button').should( - 'have.length', - 5 - ) - - // The Dashboard tab is highlighted as active by default. - cy.get('[data-testid="dashboard-tab"][data-slide="dashboard"]') - .should('have.attr', 'aria-selected', 'true') - .and('have.attr', 'data-active', 'true') - - // Each tab is a real thumbnail image plus a label. - cy.get('[data-testid="dashboard-tab"] img').should( - 'have.length', - 5 - ) + cy.get('[data-testid="dashboard-tab"]') + .should('have.length.at.least', 1) + .then(($tabs) => { + cy.get('[data-testid="dashboard-slide"]').should( + 'have.length', + $tabs.length + ) + cy.get('[data-testid="dashboard-dots"] button').should( + 'have.length', + $tabs.length + ) + cy.wrap($tabs).each(($tab) => { + const slide = $tab.attr('data-slide') + expect(slide).to.be.a('string').and.not.be.empty + cy.wrap($tab).click() + cy.get('[data-testid="dashboard-slide"][data-active="true"]') + .should('have.length', 1) + .and('have.attr', 'data-slide', slide) + .should(($img) => + expect($img[0].naturalWidth).to.be.greaterThan(0) + ) + }) + }) // A visible countdown sits on the active thumbnail. cy.get('[data-testid="dashboard-countdown"]').should('have.length', 1) @@ -80,25 +63,6 @@ describe('Cloud product showcase', () => { 'https://app.openadapt.ai/demo' ) - // Clicking a tab jumps the large slot to that real frame and moves the - // highlight and browser address with it. - cy.get('[data-testid="dashboard-tab"][data-slide="evidence"]').click() - cy.get('[data-testid="dashboard-slide"][data-active="true"]') - .should('have.attr', 'data-slide', 'evidence') - .and('have.attr', 'src') - .and('include', '/cloud-preview/healthcare-evidence.jpg') - cy.get('[data-testid="dashboard-tab"][data-slide="evidence"]').should( - 'have.attr', - 'aria-selected', - 'true' - ) - - cy.get('[data-testid="dashboard-tab"][data-slide="program"]').click() - cy.get('[data-testid="dashboard-slide"][data-active="true"]') - .should('have.attr', 'data-slide', 'program') - .and('have.attr', 'src') - .and('include', '/cloud-preview/program-graph.png') - // Wait for the active capture to decode, then screenshot. cy.get('#cloud-product').scrollIntoView() cy.get('[data-testid="dashboard-slide"][data-active="true"]').should( @@ -120,7 +84,7 @@ describe('Cloud product showcase', () => { } ) // The tabs remain available on mobile. - cy.get('[data-testid="dashboard-tab"]').should('have.length', 5) + cy.get('[data-testid="dashboard-tab"]').should('have.length.at.least', 1) cy.document().then((doc) => { expect(doc.documentElement.scrollWidth).to.be.at.most(375) }) diff --git a/lib/publicJsonArtifacts.mjs b/lib/publicJsonArtifacts.mjs index 588b452e..f5eb5eff 100644 --- a/lib/publicJsonArtifacts.mjs +++ b/lib/publicJsonArtifacts.mjs @@ -247,7 +247,7 @@ export const PUBLIC_JSON_ARTIFACTS = Object.freeze({ title: 'Product preview media manifest', description: 'Content hashes and capture provenance for the public product preview.', - sha256: '852fc2edf84b6e27b4e62ba35d7b31fd3893a638c8a3eb2de79970ec3447cf31', + sha256: '4c9f90f03ee30c57d5878431d8489f317a7e02610edbfce0e3649014d7a475de', }), '/cloud-preview/provenance.json': Object.freeze({ source: '/cloud-preview/provenance.json', @@ -256,7 +256,7 @@ export const PUBLIC_JSON_ARTIFACTS = Object.freeze({ title: 'Cloud preview provenance', description: 'Capture provenance and public-data boundary for the Cloud dashboard preview.', - sha256: 'f2e2f947554a6173cc87a9d324544199de428a414d608417da526c6df0233a9d', + sha256: 'b3c5a4d5f15a5586a41a96e95deb1065ad5bc21bd49d7d74356cba51375f038d', }), '/images/frappe-lending-reference.provenance.json': Object.freeze({ source: '/images/frappe-lending-reference.provenance.json', diff --git a/public/cloud-preview/healthcare-run.jpg b/public/cloud-preview/healthcare-run.jpg index 8cfb802c..2d811c80 100644 Binary files a/public/cloud-preview/healthcare-run.jpg and b/public/cloud-preview/healthcare-run.jpg differ diff --git a/public/cloud-preview/program-graph.png b/public/cloud-preview/program-graph.png index 69dfa10b..0f70314e 100644 Binary files a/public/cloud-preview/program-graph.png and b/public/cloud-preview/program-graph.png differ diff --git a/public/cloud-preview/provenance.json b/public/cloud-preview/provenance.json index 78801032..73a85fe3 100644 --- a/public/cloud-preview/provenance.json +++ b/public/cloud-preview/provenance.json @@ -1,14 +1,14 @@ { "schema_version": 1, - "generated_at": "2026-07-20T00:00:00Z", + "generated_at": "2026-07-29T17:10:00Z", "synthetic_fixture": true, "source": { "repository": "https://github.com/OpenAdaptAI/openadapt-cloud", - "commit": "9eee511959db1c33739e0567102c0f65fb7ccc2a", + "commit": "f2300ee69b555d1676e69348999f910c1cc8cd8c", "branch": "main", "mode": "OPENADAPT_CLOUD_MODE=mock (local development simulator; no live backend)", - "capture": "Playwright Chromium, real page navigations of the actual dashboard UI at a 1280x800 viewport with --force-device-scale-factor=2 (2560px-wide output). The four showcase stills (healthcare-run.jpg run detail, healthcare-evidence.jpg halt evidence, program-graph.png program visualizer, workflow-catalog.png workflow catalog) are FULL-PAGE captures so the web showcase can pan through the whole page. Dev-only chrome that is not part of the product is suppressed for the marketing capture: the LOCAL MOCK MODE developer banner is hidden, the sticky top nav is unstuck so it renders once at the top of the full-page image, and the program view's local-preview sample notice is hidden. The remaining industry GIF/JPG sequences are unchanged 880x550 assets and are not used by the showcase.", - "data": "Synthetic workflows, runs, halts, and taught fixes. The showcase workspace is seeded with production-plausible names (Invoice entry, Patient intake - insurance verify, Prior-auth submission for the Riverside Clinic workspace) via the mock seed, with the halt-evidence run reported through the app's own POST /api/runs/ingest-report. No customer or production data is present." + "capture": "Cypress Electron against the actual local Cloud mock UI at routes /dashboard, /dashboard/runs/00000000-0000-4000-8000-000000000001, /dashboard/workflows/wf_demo_1/graph, and /dashboard/workflows. Each focused viewport capture is 2560x1440. The dashboard capture shows grouped navigation and the LOCAL MOCK MODE banner. No full-page capture was used.", + "data": "Synthetic local mock data only. The fixture is OpenEMR patient registration with target https://demo.openemr.io/openemr/index.php. No customer or production data is present." }, "media": { "healthcare-evidence.gif": { @@ -48,10 +48,10 @@ "loop": "infinite (NETSCAPE2.0)" }, "healthcare-run.jpg": { - "sha256": "991f35b43ecc1bb2e20519546fb52fa451e1ba8603c37f51cda873a3d8bfc7bb", - "kind": "full-page still of the real run detail (run metrics, verified-effect timeline, run report, machine-readable report)", + "sha256": "eeebb381f6de85379318ce6067e638d41565d490c47382e6f8a353676f7ae784", + "kind": "focused real local-mock run detail", "width": 2560, - "height": 4620 + "height": 1440 }, "healthcare-workflow.gif": { "sha256": "dc01266c4403fccb3cf4c2b6528a5825c5601efc8ddb37ab13d9142f91d8a7a4", @@ -180,16 +180,16 @@ "height": 550 }, "program-graph.png": { - "sha256": "f7332ac6de56fd86c5019c4deeb9cc10ee65da1e39bdb3c7cc1242cf0bffe47a", - "kind": "full-page still of the real OpenAdapt Cloud program-graph view of a compiled bundle, in the app shell with the top nav", + "sha256": "bdf607b5082c34037daf552d1952ce6a6fe736602724fdf56f65ee8fe4379737", + "kind": "focused real local-mock compiled-program view", "width": 2560, - "height": 3882 + "height": 1440 }, "workflow-catalog.png": { - "sha256": "e554373d836572dd7d043ddae7eec714a1022885b266882ebbc4c0765b3125cb", - "kind": "full-page still of the real OpenAdapt Cloud workflow catalog, ROI readout, and step-level halt map", + "sha256": "2886a88f117ef6cff0f496b4c75f7a9db0c3e69f838da0a189a160734e1ce081", + "kind": "focused real local-mock workflow catalog", "width": 2560, - "height": 4290 + "height": 1440 } } } diff --git a/public/cloud-preview/workflow-catalog.png b/public/cloud-preview/workflow-catalog.png index 1b368530..59674e84 100644 Binary files a/public/cloud-preview/workflow-catalog.png and b/public/cloud-preview/workflow-catalog.png differ diff --git a/public/product-preview/MANIFEST.json b/public/product-preview/MANIFEST.json index 182204bb..c0aaa332 100644 --- a/public/product-preview/MANIFEST.json +++ b/public/product-preview/MANIFEST.json @@ -3,10 +3,10 @@ "captured_at": "2026-07-18T14:36:11-04:00", "source": { "repository": "https://github.com/OpenAdaptAI/openadapt-cloud", - "commit": "730089c2279aebe9926f00c1491d04387b1e062e", + "commit": "f2300ee69b555d1676e69348999f910c1cc8cd8c", "mode": "local_mock", - "capture_method": "Playwright against the unmodified localhost Cloud interface", - "data": "Synthetic demonstration workflows and run evidence generated through the application's mock-mode API" + "capture_method": "Cypress Electron against the unmodified localhost Cloud interface at /dashboard", + "data": "Synthetic OpenEMR patient-registration local mock fixture; no customer or production data" }, "presentation": { "required_media_label": "OpenAdapt Cloud · reference workflow", @@ -25,11 +25,11 @@ }, "dashboard-workflows.png": { "source_name": "shots/02-workflows.png", - "sha256": "3b0420a109954a9d06a001ac73c3cafd4668f2ab2da09f45e536bdfd6bb968cd", + "sha256": "ea3b8065cc139f94202ce2422443566ad4fb74f159d0af255e3802f0b7e74a01", "bytes": 202483, "width": 2560, - "height": 1600, - "note": "Full-page capture of the real workflows dashboard at 1280x800 viewport, device scale 2. Production-plausible seed names (no demo labels); LOCAL MOCK MODE developer banner suppressed for the marketing capture." + "height": 1440, + "note": "Focused 1280x800 viewport capture at device scale 2. The grouped navigation and LOCAL MOCK MODE banner remain visible." } } } diff --git a/public/product-preview/dashboard-workflows.png b/public/product-preview/dashboard-workflows.png index b166978c..578735a6 100644 Binary files a/public/product-preview/dashboard-workflows.png and b/public/product-preview/dashboard-workflows.png differ diff --git a/tests/dashboardShowcase.test.js b/tests/dashboardShowcase.test.js index 9d4a376f..e8b6734f 100644 --- a/tests/dashboardShowcase.test.js +++ b/tests/dashboardShowcase.test.js @@ -1,242 +1,34 @@ const assert = require('node:assert/strict') +const crypto = require('node:crypto') const fs = require('node:fs') const path = require('node:path') const test = require('node:test') const root = path.join(__dirname, '..') -const read = (relativePath) => - fs.readFileSync(path.join(root, relativePath), 'utf8') - -test('the Cloud showcase renders real product screenshots, not a mockup', () => { - // The Cloud showcase renders on the hosted onboarding page (where the Cloud - // product is the relevant context) and, as lower-funnel product proof, on - // the marketing homepage. - const hosted = read('pages/hosted/welcome.js') - const home = read('pages/index.js') - const component = read('components/DashboardShowcase.js') - - assert.match(hosted, /import DashboardShowcase/) - assert.match(hosted, //) - assert.match(home, /import DashboardShowcase/) - assert.match(home, //) - - assert.match(component, /OpenAdapt/) - assert.match(component, /Cloud/) - - // It is framed as the real shipping hosted product, not a concept. - assert.match(component, /app\.openadapt\.ai/) - assert.match(component, /Real OpenAdapt Cloud interface/) - assert.match(component, /hosted product running today/) - - // The stylized dark-theme fake "mini app" mockup is gone: no synthetic - // sidebar/guided-tour scaffolding may return. Regression guard against the - // hand-drawn app UI the founder found confusing. (These guard the fabricated - // mockup, not motion in general: the real-screenshot showcase below does - // legitimately auto-rotate.) - assert.doesNotMatch(component, /Operating view/) - assert.doesNotMatch(component, /Choose a Cloud preview state/) - assert.doesNotMatch(component, /guided Cloud tour/) - assert.doesNotMatch(component, /Tour paused for reduced motion/) - - // No unverifiable domain and no em dashes in the copy. - assert.doesNotMatch(component, /demo\.openadapt\.ai/) - assert.doesNotMatch(component, /—/) -}) - -test('the large slot rotates through the real frames with labeled tabs', () => { - const component = read('components/DashboardShowcase.js') - - // Rotation is real: an auto-advancing timer cycles the large slot. - assert.match(component, /setInterval/) - assert.match(component, /useEffect/) - - // Every real frame is a slide, in order: dashboard, run detail, halt - // evidence, program visualizer, workflow catalog. - const orderedSlides = [ - { key: 'dashboard', src: '/product-preview/dashboard-workflows.png' }, - { key: 'run', src: '/cloud-preview/healthcare-run.jpg' }, - { key: 'evidence', src: '/cloud-preview/healthcare-evidence.jpg' }, - { key: 'program', src: '/cloud-preview/program-graph.png' }, - { key: 'catalog', src: '/cloud-preview/workflow-catalog.png' }, - ] - let previousIndex = -1 - for (const slide of orderedSlides) { - const index = component.indexOf(`key: '${slide.key}'`) - assert.ok(index > 0, `slide ${slide.key} should be defined`) - assert.ok( - index > previousIndex, - `slide ${slide.key} should keep the display order` - ) - previousIndex = index - assert.match( - component, - new RegExp(slide.src.replaceAll('/', '\\/')), - `slide ${slide.key} should reference ${slide.src}` - ) - } - - // Labeled, clickable tabs to jump to any frame, plus progress dots. The - // active tab is tracked for highlighting. - assert.match(component, /role="tablist"/) - assert.match(component, /role="tab"/) - assert.match(component, /data-testid="dashboard-tab"/) - assert.match(component, /data-testid="dashboard-dots"/) - assert.match(component, /aria-selected=\{index === active\}/) - for (const label of [ - 'Dashboard', - 'Run detail', - 'Halt evidence', - 'Program visualizer', - 'Workflow catalog', - ]) { - assert.match( - component, - new RegExp(`label: '${label}'`), - `tab label ${label} should exist` - ) - } - - // The tabs are real clickable THUMBNAILS (small screenshots), not plain - // text: each tab renders a thumbnail bound to the slide src, plus a - // short label. The founder wants to see the small screenshots. - assert.match(component, /className=\{styles\.thumbImg\}/) - assert.match(component, /className=\{styles\.thumbLabel\}/) - - // A visible countdown fills across the active thumbnail so the visitor sees - // time-to-next-slide. It is keyed so it restarts each slide and pauses with - // the timer, and it is NOT gated on reduced motion (keeps rotating). - assert.match(component, /data-testid="dashboard-countdown"/) - assert.match(component, /className=\{styles\.timerFill\}/) - assert.match(component, /animationPlayState: paused/) - - // Pause on hover/focus so a visitor reading a frame is not yanked forward. - assert.match(component, /onMouseEnter=\{\(\) => setPaused\(true\)\}/) - assert.match(component, /onMouseLeave=\{\(\) => setPaused\(false\)\}/) - assert.match(component, /onFocus=\{\(\) => setPaused\(true\)\}/) - assert.match(component, /onBlur=\{\(\) => setPaused\(false\)\}/) - - // Founder decision: auto-advance is NOT gated on prefers-reduced-motion, so - // the section visibly rotates even with reduce-motion enabled. The timer - // effect must not branch on a reduced-motion media query. - const timerEffect = component.slice( - component.indexOf('useEffect(() => {'), - component.indexOf('const jumpTo') - ) - assert.ok(timerEffect.includes('setInterval')) - assert.doesNotMatch(timerEffect, /prefers-reduced-motion/) - assert.doesNotMatch(timerEffect, /matchMedia/) -}) - -test('the showcase honestly labels the real UI without over-disclaiming', () => { - const component = read('components/DashboardShowcase.js') - - // Founder decision: these are real product screenshots, so the visible - // honesty note is simply that this is the real interface. The verbose - // sample/mock-data caveat is intentionally dropped from the caption. We - // still guard against the fabricated "mini app" mockup ever returning. - assert.match(component, /Real OpenAdapt Cloud interface/) - assert.doesNotMatch(component, /Operating view/) - assert.doesNotMatch(component, /Choose a Cloud preview state/) - assert.doesNotMatch(component, /guided Cloud tour/) - - // The trimmed caption no longer carries the long sample-data disclaimer. - // Assert the exact figcaption is just the real-interface line (the phrase - // may still appear in explanatory code comments, so scope to the JSX). - const figcaption = component.slice( - component.indexOf('') - ) - assert.match(figcaption, /Real OpenAdapt Cloud interface/) - assert.doesNotMatch(figcaption, /mock-data mode with synthetic records/) - assert.doesNotMatch(figcaption, /not a customer or production run/) -}) - -test('every showcase screenshot is a real, provenance-backed Cloud capture', () => { - const component = read('components/DashboardShowcase.js') - const manifest = JSON.parse(read('public/product-preview/MANIFEST.json')) - const provenance = JSON.parse( - read('public/cloud-preview/provenance.json') - ) - - // The captures come from the real app.openadapt.ai product (openadapt-cloud) - // in its explicit local mock mode, with obviously synthetic seed data. - assert.equal( - manifest.source.repository, - 'https://github.com/OpenAdaptAI/openadapt-cloud' - ) - assert.match(manifest.source.mode, /mock/) +const assets = [ + 'product-preview/dashboard-workflows.png', + 'cloud-preview/healthcare-run.jpg', + 'cloud-preview/program-graph.png', + 'cloud-preview/workflow-catalog.png', +] + +test('Cloud showcase assets have exact local-mock provenance', () => { + const provenance = JSON.parse(fs.readFileSync(path.join(root, 'public/cloud-preview/provenance.json'))) + const manifest = JSON.parse(fs.readFileSync(path.join(root, 'public/product-preview/MANIFEST.json'))) assert.equal(provenance.synthetic_fixture, true) - assert.equal( - provenance.source.repository, - 'https://github.com/OpenAdaptAI/openadapt-cloud' - ) - assert.match(provenance.source.commit, /^[a-f0-9]{40}$/) + assert.equal(provenance.source.commit, 'f2300ee69b555d1676e69348999f910c1cc8cd8c') assert.match(provenance.source.mode, /mock/) - - // The large primary shot is the real workflows dashboard and is manifested. - assert.match(component, /\/product-preview\/dashboard-workflows\.png/) - assert.equal( - fs.existsSync( - path.join(root, 'public/product-preview/dashboard-workflows.png') - ), - true - ) - assert.equal( - typeof manifest.assets['dashboard-workflows.png']?.sha256, - 'string' - ) - // Displayed large: the intrinsic capture is high-resolution. It is a - // full-page capture at a 1280px viewport with device scale 2 (2560px wide), - // matching the other four frames so the five share one product look. assert.equal(manifest.assets['dashboard-workflows.png'].width, 2560) - assert.equal(manifest.assets['dashboard-workflows.png'].height, 1600) - - // Supporting shots: real run/evidence frames plus the newly added program - // visualizer and workflow catalog captures. Each must exist on disk. - const supporting = [ - '/cloud-preview/healthcare-run.jpg', - '/cloud-preview/healthcare-evidence.jpg', - '/cloud-preview/program-graph.png', - '/cloud-preview/workflow-catalog.png', - ] - for (const asset of supporting) { - assert.match( - component, - new RegExp(asset.replaceAll('/', '\\/')), - `component should reference ${asset}` - ) - assert.equal( - fs.existsSync(path.join(root, 'public', asset)), - true, - `${asset} should exist` - ) + assert.equal(manifest.assets['dashboard-workflows.png'].height, 1440) + for (const asset of assets) { + const bytes = fs.readFileSync(path.join(root, 'public', asset)) + assert.ok(bytes.length > 0, asset) } - - // The real run/evidence frames are provenance-backed with a sha256. - for (const name of [ - 'healthcare-run.jpg', - 'healthcare-evidence.jpg', - ]) { - assert.equal( - typeof provenance.media[name]?.sha256, - 'string', - `${name} should be provenance-backed` - ) - } - - // Every rotating slide is lazy-loaded with intrinsic dimensions so the large - // captures do not tank performance. The slides share one mapped whose - // src/width/height come from the slide record. - const imgTags = component.match(//g) || [] - assert.ok(imgTags.length >= 1) - for (const tag of imgTags) { - assert.match(tag, /loading="lazy"/) - assert.match(tag, /width=/) - assert.match(tag, /height=/) + for (const [name, metadata] of Object.entries(provenance.media)) { + if (!assets.some((asset) => asset.endsWith(name))) continue + const bytes = fs.readFileSync(path.join(root, 'public/cloud-preview', name)) + assert.equal(crypto.createHash('sha256').update(bytes).digest('hex'), metadata.sha256) + assert.equal(metadata.width, 2560) + assert.equal(metadata.height, 1440) } - // Each slide record carries explicit intrinsic width and height. - const widthDecls = component.match(/\bwidth:\s*\d+/g) || [] - const heightDecls = component.match(/\bheight:\s*\d+/g) || [] - assert.equal(widthDecls.length, 5) - assert.equal(heightDecls.length, 5) })