Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clients/static-site/src/config-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ let screenshotSchema = z.object({
fullPage: z.boolean().default(true),
omitBackground: z.boolean().default(false),
timeout: z.number().int().positive().default(45_000), // 45 seconds
requestTimeout: z.number().int().positive().optional(),
});

/**
Expand Down
8 changes: 7 additions & 1 deletion clients/static-site/src/screenshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,15 @@ let DEFAULT_SCREENSHOT_TIMEOUT = 45_000;
* @param {boolean} [options.fullPage=true] - Capture full page
* @param {boolean} [options.omitBackground=false] - Omit background (transparent)
* @param {number} [options.timeout=45000] - Screenshot timeout in ms
* @param {number} [options.requestTimeout] - Vizzly request timeout in ms
* @returns {Promise<Buffer>} Screenshot buffer
*/
export async function captureScreenshot(page, options = {}) {
let {
fullPage = true,
omitBackground = false,
timeout = DEFAULT_SCREENSHOT_TIMEOUT,
requestTimeout: _requestTimeout,
} = options;

// Playwright has built-in timeout support
Expand Down Expand Up @@ -105,6 +107,10 @@ export async function captureAndSendScreenshot(
let properties = generateScreenshotProperties(viewport);
properties.url = page.url();
let screenshot = await captureScreenshot(page, screenshotOptions);
let requestTimeout =
screenshotOptions.requestTimeout ||
screenshotOptions.timeout ||
DEFAULT_SCREENSHOT_TIMEOUT;

await vizzlyScreenshot(name, screenshot, { properties });
await vizzlyScreenshot(name, screenshot, { properties, requestTimeout });
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"test": "node --experimental-test-coverage --test --test-concurrency=1 --test-reporter=spec $(find tests -name '*.test.js')",
"test:watch": "node --test --test-reporter=spec --watch $(find tests -name '*.test.js')",
"test:reporter": "playwright test --config=tests/reporter/playwright.config.js",
"test:reporter:visual": "node bin/vizzly.js run \"pnpm run test:reporter\"",
"test:reporter:visual": "node bin/vizzly.js tdd run \"pnpm run test:reporter\" --no-open",
"test:swift:e2e": "pnpm run build && node clients/swift/scripts/run-e2e.js",
"test:tui": "node --test --test-reporter=spec tests/tui/*.test.js",
"test:tui:docker": "./tests/tui/run-tui-tests.sh",
Expand All @@ -92,6 +92,7 @@
"registry": "https://registry.npmjs.org/"
},
"dependencies": {
"@vizzly-testing/bear-den": "0.1.2",
"@vizzly-testing/honeydiff": "^0.10.3",
"ansis": "^4.2.0",
"commander": "^14.0.0",
Expand Down
27 changes: 27 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ minimumReleaseAge: 10080
minimumReleaseAgeStrict: true
minimumReleaseAgeIgnoreMissingTime: false
minimumReleaseAgeExclude:
- '@vizzly-testing/bear-den@0.1.2'
- '@vizzly-testing/honeydiff@0.10.3'
onlyBuiltDependencies:
- canvas
Expand Down
4 changes: 2 additions & 2 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ import { saveUserPath } from './utils/global-config.js';
import * as output from './utils/output.js';
import { getPackageVersion } from './utils/package-info.js';

// Custom help formatting with Observatory design system
// Custom help formatting with BearDen design system
const formatHelp = (cmd, helper) => {
let c = colors;
let lines = [];
Expand Down Expand Up @@ -591,7 +591,7 @@ tddCmd
if (reportResult.success) {
const reportUrl = getReportFileUrl(reportResult.reportPath);
output.print(
` ${colors.brand.textTertiary('→')} Report: ${colors.blue(reportUrl)}`
` ${colors.brand.textTertiary('→')} Report: ${colors.info(reportUrl)}`
);
output.blank();

Expand Down
3 changes: 2 additions & 1 deletion src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ function createSimpleClient(serverUrl) {
let isFilePath = typeof imageBuffer === 'string';
let image = isFilePath ? imageBuffer : imageBuffer.toString('base64');
let type = isFilePath ? 'file-path' : 'base64';
let requestTimeout = options.requestTimeout || DEFAULT_TIMEOUT_MS;

let properties = createScreenshotProperties(options);

Expand All @@ -212,7 +213,7 @@ function createSimpleClient(serverUrl) {
type,
properties,
},
DEFAULT_TIMEOUT_MS
requestTimeout
);
let httpMs = Date.now() - httpStart;

Expand Down
8 changes: 4 additions & 4 deletions src/commands/doctor.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,15 @@ export async function doctorCommand(
for (let item of contextItems) {
if (item.type === 'success') {
output.printErr(
` ${colors.green('✓')} ${colors.gray(item.label)} ${colors.white(item.value)}`
` ${colors.success('✓')} ${colors.brand.textMuted(item.label)} ${colors.white(item.value)}`
);
} else if (item.type === 'warning') {
output.printErr(
` ${colors.yellow('!')} ${colors.gray(item.label)} ${colors.yellow(item.value)}`
` ${colors.yellow('!')} ${colors.brand.textMuted(item.label)} ${colors.yellow(item.value)}`
);
} else {
output.printErr(
` ${colors.dim('○')} ${colors.gray(item.label)} ${colors.dim(item.value)}`
` ${colors.dim('○')} ${colors.brand.textMuted(item.label)} ${colors.dim(item.value)}`
);
}
}
Expand All @@ -260,7 +260,7 @@ export async function doctorCommand(
output.printErr('');
output.printErr(` ${colors.dim('─'.repeat(52))}`);
output.printErr(
` ${colors.dim('Docs')} ${colors.cyan(colors.underline('docs.vizzly.dev'))} ${colors.dim('GitHub')} ${colors.cyan(colors.underline('github.com/vizzly-testing/cli'))}`
` ${colors.dim('Docs')} ${colors.info(colors.underline('docs.vizzly.dev'))} ${colors.dim('GitHub')} ${colors.info(colors.underline('github.com/vizzly-testing/cli'))}`
);

// Emit structured data in verbose mode (in addition to visual output)
Expand Down
4 changes: 2 additions & 2 deletions src/commands/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ export async function previewCommand(
if (result.reusedBlobs > 0) {
let savedBytes = result.totalBytes - result.newBytes;
output.print(
` ${colors.brand.textTertiary('Deduped')} ${colors.green(result.reusedBlobs)} files (saved ${formatBytes(savedBytes)})`
` ${colors.brand.textTertiary('Deduped')} ${colors.success(result.reusedBlobs)} files (saved ${formatBytes(savedBytes)})`
);
}

Expand All @@ -680,7 +680,7 @@ export async function previewCommand(

output.blank();
output.print(
` ${colors.brand.textTertiary('Preview')} ${colors.cyan(colors.underline(result.previewUrl))}`
` ${colors.brand.textTertiary('Preview')} ${colors.info(colors.underline(result.previewUrl))}`
);

// Open in browser if requested
Expand Down
2 changes: 1 addition & 1 deletion src/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ export async function runCommand(

if (displayUrl) {
output.print(
` ${colors.brand.textTertiary('Results')} ${colors.cyan(colors.underline(displayUrl))}`
` ${colors.brand.textTertiary('Results')} ${colors.info(colors.underline(displayUrl))}`
);
} else {
output.print(
Expand Down
10 changes: 6 additions & 4 deletions src/reporter/src/components/app-router.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { EmptyState, Spinner } from '@vizzly-testing/bear-den';
import { Route, Switch, useLocation } from 'wouter';
import { useReportData } from '../hooks/queries/use-tdd-queries.js';
import { EmptyState, Spinner } from './design-system/index.js';
import { Layout } from './layout/index.js';
import BuildsView from './views/builds-view.jsx';
import ComparisonDetailView from './views/comparison-detail-view.jsx';
Expand All @@ -13,8 +13,10 @@ import WaitingForScreenshots from './waiting-for-screenshots.jsx';
function LoadingState() {
return (
<div className="flex flex-col items-center justify-center py-32">
<Spinner size="lg" className="text-amber-400 mb-4" />
<p className="text-slate-400 text-sm">Loading report data...</p>
<Spinner size="lg" className="text-[var(--accent-brand)] mb-4" />
<p className="text-[var(--text-tertiary)] text-sm">
Loading report data...
</p>
</div>
);
}
Expand All @@ -31,7 +33,7 @@ function ErrorState({ error, onRetry }) {
<button
type="button"
onClick={onRetry}
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-slate-900 font-medium rounded-lg transition-colors"
className="px-4 py-2 bg-[var(--accent-brand)] hover:bg-[var(--accent-brand-hover)] text-[var(--vz-bg)] font-medium rounded-lg transition-colors"
>
Try Again
</button>
Expand Down
4 changes: 2 additions & 2 deletions src/reporter/src/components/code-block.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ export default function CodeBlock({ code, language = 'javascript' }) {
let highlightedCode = renderTokenizedCode(code, isShell ? 'shell' : language);

return (
<pre className="bg-slate-950 rounded-lg p-4 overflow-x-auto text-sm">
<code className="font-mono text-slate-300 block whitespace-pre">
<pre className="bg-[var(--vz-surface)] rounded-lg p-4 overflow-x-auto text-sm">
<code className="font-mono text-[var(--text-secondary)] block whitespace-pre">
{highlightedCode}
</code>
</pre>
Expand Down
4 changes: 2 additions & 2 deletions src/reporter/src/components/comparison/comparison-actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function ComparisonActions({
{/* Accept button - full width on mobile */}
<button
type="button"
className="flex-1 md:flex-none inline-flex items-center justify-center gap-1.5 bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 disabled:bg-slate-600 text-white text-sm font-medium px-4 py-3 md:py-2 rounded-lg md:rounded-md transition-colors disabled:cursor-not-allowed disabled:opacity-50 touch-manipulation"
className="flex-1 md:flex-none inline-flex items-center justify-center gap-1.5 bg-[var(--accent-success-muted)] hover:bg-[color-mix(in_srgb,var(--accent-success)_22%,transparent)] active:bg-[color-mix(in_srgb,var(--accent-success)_28%,transparent)] disabled:bg-[var(--vz-raised)] text-[var(--accent-success)] text-sm font-medium px-4 py-3 md:py-2 rounded-lg md:rounded-md transition-colors disabled:cursor-not-allowed disabled:opacity-85 touch-manipulation"
onClick={onAccept}
disabled={disabled}
title="Accept these changes as the new baseline"
Expand All @@ -21,7 +21,7 @@ export default function ComparisonActions({
{/* Reject button - full width on mobile */}
<button
type="button"
className="flex-1 md:flex-none inline-flex items-center justify-center gap-1.5 bg-slate-700 hover:bg-slate-600 active:bg-slate-500 disabled:bg-slate-600 text-slate-300 hover:text-white text-sm font-medium px-4 py-3 md:py-2 rounded-lg md:rounded-md transition-colors disabled:cursor-not-allowed disabled:opacity-50 touch-manipulation"
className="flex-1 md:flex-none inline-flex items-center justify-center gap-1.5 bg-[var(--accent-danger-muted)] hover:bg-[color-mix(in_srgb,var(--accent-danger)_22%,transparent)] active:bg-[color-mix(in_srgb,var(--accent-danger)_28%,transparent)] disabled:bg-[var(--vz-raised)] text-[var(--accent-danger)] hover:text-[var(--text-primary)] text-sm font-medium px-4 py-3 md:py-2 rounded-lg md:rounded-md transition-colors disabled:cursor-not-allowed disabled:opacity-85 touch-manipulation"
onClick={onReject}
disabled={disabled}
title="Keep the current baseline (reject changes)"
Expand Down
30 changes: 15 additions & 15 deletions src/reporter/src/components/comparison/comparison-viewer.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import { useCallback, useMemo, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';
import {
OnionSkinMode,
OverlayMode,
ToggleView,
} from '../design-system/index.js';
} from '@vizzly-testing/bear-den/review';
import { useCallback, useMemo, useState } from 'react';
import { VIEW_MODES } from '../../utils/constants.js';
import { withImageVersion } from '../../utils/image-url.js';

/**
* Comparison Viewer for inline card display
* Simpler than ScreenshotDisplay - no zoom, just renders comparison modes
*/
export default function ComparisonViewer({ comparison, viewMode }) {
const [showDiffOverlay, setShowDiffOverlay] = useState(true);
const [onionSkinPosition, setOnionSkinPosition] = useState(50);
const [imageErrors, setImageErrors] = useState(new Set());
let [showDiffOverlay, setShowDiffOverlay] = useState(true);
let [onionSkinPosition, setOnionSkinPosition] = useState(50);
let [imageErrors, setImageErrors] = useState(new Set());

const handleImageError = useCallback(imageKey => {
let handleImageError = useCallback(imageKey => {
setImageErrors(prev => new Set([...prev, imageKey]));
}, []);

const handleImageLoad = useCallback(() => {
let handleImageLoad = useCallback(() => {
// No-op for now, could track load states if needed
}, []);

const handleDiffToggle = useCallback(() => {
let handleDiffToggle = useCallback(() => {
setShowDiffOverlay(prev => !prev);
}, []);

// Create a screenshot-like object for the comparison modes
const screenshot = useMemo(
let screenshot = useMemo(
() => ({
id: comparison.id || comparison.signature || 'unknown',
name: comparison.name || comparison.originalName || 'Screenshot',
Expand All @@ -38,7 +38,7 @@ export default function ComparisonViewer({ comparison, viewMode }) {
);

// Build image URLs once per comparison update.
const imageUrls = useMemo(
let imageUrls = useMemo(
() => ({
current: withImageVersion(comparison.current, comparison.timestamp),
baseline: withImageVersion(comparison.baseline, comparison.timestamp),
Expand All @@ -57,7 +57,7 @@ export default function ComparisonViewer({ comparison, viewMode }) {
return (
<div className="comparison-viewer new-baseline">
<div className="text-center py-8">
<p className="text-slate-400 text-sm mb-4">
<p className="text-[var(--text-tertiary)] text-sm mb-4">
First screenshot - creating new baseline
</p>
<img
Expand Down Expand Up @@ -114,7 +114,7 @@ export default function ComparisonViewer({ comparison, viewMode }) {
{viewMode === VIEW_MODES.SIDE_BY_SIDE && (
<div className="flex gap-4 max-w-full overflow-auto">
<div className="flex-1 min-w-0">
<div className="text-xs text-gray-400 mb-2 text-center">
<div className="text-xs text-[var(--text-tertiary)] mb-2 text-center">
Baseline
</div>
<img
Expand All @@ -124,7 +124,7 @@ export default function ComparisonViewer({ comparison, viewMode }) {
/>
</div>
<div className="flex-1 min-w-0">
<div className="text-xs text-gray-400 mb-2 text-center">
<div className="text-xs text-[var(--text-tertiary)] mb-2 text-center">
Current
</div>
<img
Expand Down
Loading