fix(image-pipeline): make preview histogram opt-in
This commit is contained in:
@@ -111,6 +111,7 @@ export default function AdjustmentPreview({
|
|||||||
sourceUrl,
|
sourceUrl,
|
||||||
steps,
|
steps,
|
||||||
nodeWidth,
|
nodeWidth,
|
||||||
|
includeHistogram: true,
|
||||||
// Die Vorschau muss in-Node gut lesbar bleiben, aber nicht in voller
|
// Die Vorschau muss in-Node gut lesbar bleiben, aber nicht in voller
|
||||||
// Display-Auflösung rechnen.
|
// Display-Auflösung rechnen.
|
||||||
previewScale: 0.5,
|
previewScale: 0.5,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export default function CompareSurface({
|
|||||||
sourceUrl: previewSourceUrl,
|
sourceUrl: previewSourceUrl,
|
||||||
steps: previewSteps,
|
steps: previewSteps,
|
||||||
nodeWidth,
|
nodeWidth,
|
||||||
|
includeHistogram: false,
|
||||||
// Compare-Nodes zeigen nur eine kompakte Live-Ansicht; kleinere Kacheln
|
// Compare-Nodes zeigen nur eine kompakte Live-Ansicht; kleinere Kacheln
|
||||||
// halten lange Workflows spürbar reaktionsfreudiger.
|
// halten lange Workflows spürbar reaktionsfreudiger.
|
||||||
previewScale: 0.5,
|
previewScale: 0.5,
|
||||||
|
|||||||
@@ -614,6 +614,7 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
|
|||||||
sourceUrl: isFullscreenOpen && sourceUrl ? sourceUrl : null,
|
sourceUrl: isFullscreenOpen && sourceUrl ? sourceUrl : null,
|
||||||
steps,
|
steps,
|
||||||
nodeWidth: fullscreenPreviewWidth,
|
nodeWidth: fullscreenPreviewWidth,
|
||||||
|
includeHistogram: false,
|
||||||
previewScale: 0.85,
|
previewScale: 0.85,
|
||||||
maxPreviewWidth: 1920,
|
maxPreviewWidth: 1920,
|
||||||
maxDevicePixelRatio: 1.5,
|
maxDevicePixelRatio: 1.5,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type UsePipelinePreviewOptions = {
|
|||||||
sourceUrl: string | null;
|
sourceUrl: string | null;
|
||||||
steps: readonly PipelineStep[];
|
steps: readonly PipelineStep[];
|
||||||
nodeWidth: number;
|
nodeWidth: number;
|
||||||
|
includeHistogram?: boolean;
|
||||||
previewScale?: number;
|
previewScale?: number;
|
||||||
maxPreviewWidth?: number;
|
maxPreviewWidth?: number;
|
||||||
maxDevicePixelRatio?: number;
|
maxDevicePixelRatio?: number;
|
||||||
@@ -125,6 +126,7 @@ export function usePipelinePreview(options: UsePipelinePreviewOptions): {
|
|||||||
sourceUrl,
|
sourceUrl,
|
||||||
steps: stableRenderInputRef.current?.steps ?? [],
|
steps: stableRenderInputRef.current?.steps ?? [],
|
||||||
previewWidth,
|
previewWidth,
|
||||||
|
includeHistogram: options.includeHistogram,
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
})
|
})
|
||||||
.then((result: PreviewRenderResult) => {
|
.then((result: PreviewRenderResult) => {
|
||||||
@@ -163,7 +165,7 @@ export function usePipelinePreview(options: UsePipelinePreviewOptions): {
|
|||||||
window.clearTimeout(timer);
|
window.clearTimeout(timer);
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
};
|
};
|
||||||
}, [pipelineHash, previewWidth]);
|
}, [options.includeHistogram, pipelineHash, previewWidth]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canvasRef,
|
canvasRef,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type PreviewWorkerPayload = {
|
|||||||
sourceUrl: string;
|
sourceUrl: string;
|
||||||
steps: readonly PipelineStep[];
|
steps: readonly PipelineStep[];
|
||||||
previewWidth: number;
|
previewWidth: number;
|
||||||
|
includeHistogram?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WorkerRequestMessage =
|
type WorkerRequestMessage =
|
||||||
@@ -93,6 +94,7 @@ async function handlePreviewRequest(requestId: number, payload: PreviewWorkerPay
|
|||||||
sourceUrl: payload.sourceUrl,
|
sourceUrl: payload.sourceUrl,
|
||||||
steps: payload.steps,
|
steps: payload.steps,
|
||||||
previewWidth: payload.previewWidth,
|
previewWidth: payload.previewWidth,
|
||||||
|
includeHistogram: payload.includeHistogram,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { PipelineStep } from "@/lib/image-pipeline/contracts";
|
import type { PipelineStep } from "@/lib/image-pipeline/contracts";
|
||||||
import { computeHistogram, type HistogramData } from "@/lib/image-pipeline/histogram";
|
import { computeHistogram, emptyHistogram, type HistogramData } from "@/lib/image-pipeline/histogram";
|
||||||
import { applyPipelineStep } from "@/lib/image-pipeline/render-core";
|
import { applyPipelineStep } from "@/lib/image-pipeline/render-core";
|
||||||
import { loadSourceBitmap } from "@/lib/image-pipeline/source-loader";
|
import { loadSourceBitmap } from "@/lib/image-pipeline/source-loader";
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ export async function renderPreview(options: {
|
|||||||
sourceUrl: string;
|
sourceUrl: string;
|
||||||
steps: readonly PipelineStep[];
|
steps: readonly PipelineStep[];
|
||||||
previewWidth: number;
|
previewWidth: number;
|
||||||
|
includeHistogram?: boolean;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}): Promise<PreviewRenderResult> {
|
}): Promise<PreviewRenderResult> {
|
||||||
const bitmap = await loadSourceBitmap(options.sourceUrl, {
|
const bitmap = await loadSourceBitmap(options.sourceUrl, {
|
||||||
@@ -82,7 +83,9 @@ export async function renderPreview(options: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const histogram = computeHistogram(imageData.data);
|
const histogram = options.includeHistogram === false
|
||||||
|
? emptyHistogram()
|
||||||
|
: computeHistogram(imageData.data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width,
|
width,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type PreviewWorkerPayload = {
|
|||||||
sourceUrl: string;
|
sourceUrl: string;
|
||||||
steps: readonly PipelineStep[];
|
steps: readonly PipelineStep[];
|
||||||
previewWidth: number;
|
previewWidth: number;
|
||||||
|
includeHistogram?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WorkerRequestMessage =
|
type WorkerRequestMessage =
|
||||||
@@ -261,6 +262,7 @@ export async function renderPreviewWithWorkerFallback(options: {
|
|||||||
sourceUrl: string;
|
sourceUrl: string;
|
||||||
steps: readonly PipelineStep[];
|
steps: readonly PipelineStep[];
|
||||||
previewWidth: number;
|
previewWidth: number;
|
||||||
|
includeHistogram?: boolean;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}): Promise<PreviewRenderResult> {
|
}): Promise<PreviewRenderResult> {
|
||||||
try {
|
try {
|
||||||
@@ -270,6 +272,7 @@ export async function renderPreviewWithWorkerFallback(options: {
|
|||||||
sourceUrl: options.sourceUrl,
|
sourceUrl: options.sourceUrl,
|
||||||
steps: options.steps,
|
steps: options.steps,
|
||||||
previewWidth: options.previewWidth,
|
previewWidth: options.previewWidth,
|
||||||
|
includeHistogram: options.includeHistogram,
|
||||||
},
|
},
|
||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,30 +18,46 @@ vi.mock("@/lib/image-pipeline/worker-client", () => ({
|
|||||||
|
|
||||||
import { usePipelinePreview } from "@/hooks/use-pipeline-preview";
|
import { usePipelinePreview } from "@/hooks/use-pipeline-preview";
|
||||||
|
|
||||||
|
const previewHarnessState = {
|
||||||
|
latestHistogram: emptyHistogram(),
|
||||||
|
latestError: null as string | null,
|
||||||
|
latestIsRendering: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let container: HTMLDivElement | null = null;
|
||||||
|
let root: Root | null = null;
|
||||||
|
|
||||||
function PreviewHarness({
|
function PreviewHarness({
|
||||||
sourceUrl,
|
sourceUrl,
|
||||||
steps,
|
steps,
|
||||||
|
includeHistogram,
|
||||||
}: {
|
}: {
|
||||||
sourceUrl: string | null;
|
sourceUrl: string | null;
|
||||||
steps: PipelineStep[];
|
steps: PipelineStep[];
|
||||||
|
includeHistogram?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { canvasRef } = usePipelinePreview({
|
const { canvasRef, histogram, error, isRendering } = usePipelinePreview({
|
||||||
sourceUrl,
|
sourceUrl,
|
||||||
steps,
|
steps,
|
||||||
nodeWidth: 320,
|
nodeWidth: 320,
|
||||||
|
includeHistogram,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
previewHarnessState.latestHistogram = histogram;
|
||||||
|
previewHarnessState.latestError = error;
|
||||||
|
previewHarnessState.latestIsRendering = isRendering;
|
||||||
|
|
||||||
return createElement("canvas", { ref: canvasRef });
|
return createElement("canvas", { ref: canvasRef });
|
||||||
}
|
}
|
||||||
|
|
||||||
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
|
|
||||||
describe("usePipelinePreview", () => {
|
describe("usePipelinePreview", () => {
|
||||||
let container: HTMLDivElement | null = null;
|
|
||||||
let root: Root | null = null;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
|
previewHarnessState.latestHistogram = emptyHistogram();
|
||||||
|
previewHarnessState.latestError = null;
|
||||||
|
previewHarnessState.latestIsRendering = false;
|
||||||
workerClientMocks.renderPreviewWithWorkerFallback.mockReset();
|
workerClientMocks.renderPreviewWithWorkerFallback.mockReset();
|
||||||
workerClientMocks.renderPreviewWithWorkerFallback.mockResolvedValue({
|
workerClientMocks.renderPreviewWithWorkerFallback.mockResolvedValue({
|
||||||
width: 120,
|
width: 120,
|
||||||
@@ -119,4 +135,280 @@ describe("usePipelinePreview", () => {
|
|||||||
|
|
||||||
expect(workerClientMocks.renderPreviewWithWorkerFallback).toHaveBeenCalledTimes(1);
|
expect(workerClientMocks.renderPreviewWithWorkerFallback).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders image output when histogram work is disabled", async () => {
|
||||||
|
const putImageData = vi.fn();
|
||||||
|
vi.spyOn(HTMLCanvasElement.prototype, "getContext").mockReturnValue({
|
||||||
|
putImageData,
|
||||||
|
} as unknown as CanvasRenderingContext2D);
|
||||||
|
|
||||||
|
workerClientMocks.renderPreviewWithWorkerFallback.mockResolvedValueOnce({
|
||||||
|
width: 64,
|
||||||
|
height: 32,
|
||||||
|
imageData: { data: new Uint8ClampedArray(64 * 32 * 4) },
|
||||||
|
histogram: emptyHistogram(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root?.render(
|
||||||
|
createElement(PreviewHarness, {
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
steps: [],
|
||||||
|
includeHistogram: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
vi.advanceTimersByTime(16);
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(workerClientMocks.renderPreviewWithWorkerFallback).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
includeHistogram: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(putImageData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(previewHarnessState.latestHistogram).toEqual(emptyHistogram());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps histogram data available when explicitly requested", async () => {
|
||||||
|
const histogram = {
|
||||||
|
red: Array.from({ length: 256 }, (_, index) => index),
|
||||||
|
green: Array.from({ length: 256 }, (_, index) => index + 1),
|
||||||
|
blue: Array.from({ length: 256 }, (_, index) => index + 2),
|
||||||
|
rgb: Array.from({ length: 256 }, (_, index) => index + 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
workerClientMocks.renderPreviewWithWorkerFallback.mockResolvedValueOnce({
|
||||||
|
width: 64,
|
||||||
|
height: 32,
|
||||||
|
imageData: { data: new Uint8ClampedArray(64 * 32 * 4) },
|
||||||
|
histogram,
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root?.render(
|
||||||
|
createElement(PreviewHarness, {
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
steps: [],
|
||||||
|
includeHistogram: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
vi.advanceTimersByTime(16);
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(workerClientMocks.renderPreviewWithWorkerFallback).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
includeHistogram: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(previewHarnessState.latestHistogram).toEqual(histogram);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("preview histogram call sites", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
container = document.createElement("div");
|
||||||
|
document.body.appendChild(container);
|
||||||
|
root = createRoot(container);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
if (root) {
|
||||||
|
await act(async () => {
|
||||||
|
root?.unmount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
container?.remove();
|
||||||
|
root = null;
|
||||||
|
container = null;
|
||||||
|
vi.resetModules();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps histogram enabled for AdjustmentPreview", async () => {
|
||||||
|
const hookSpy = vi.fn(() => ({
|
||||||
|
canvasRef: { current: null },
|
||||||
|
histogram: emptyHistogram(),
|
||||||
|
isRendering: false,
|
||||||
|
hasSource: true,
|
||||||
|
previewAspectRatio: 1,
|
||||||
|
error: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock("@/hooks/use-pipeline-preview", () => ({
|
||||||
|
usePipelinePreview: hookSpy,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/canvas/canvas-graph-context", () => ({
|
||||||
|
useCanvasGraph: () => ({ nodes: [], edges: [] }),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/lib/canvas-render-preview", () => ({
|
||||||
|
collectPipelineFromGraph: () => [],
|
||||||
|
getSourceImageFromGraph: () => "https://cdn.example.com/source.png",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const module = await import("@/components/canvas/nodes/adjustment-preview");
|
||||||
|
const AdjustmentPreview = module.default;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root?.render(
|
||||||
|
createElement(AdjustmentPreview, {
|
||||||
|
nodeId: "light-1",
|
||||||
|
nodeWidth: 320,
|
||||||
|
currentType: "light-adjust",
|
||||||
|
currentParams: { brightness: 10 },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(hookSpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
includeHistogram: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requests previews without histogram work in CompareSurface and fullscreen RenderNode", async () => {
|
||||||
|
const hookSpy = vi.fn(() => ({
|
||||||
|
canvasRef: { current: null },
|
||||||
|
histogram: emptyHistogram(),
|
||||||
|
isRendering: false,
|
||||||
|
hasSource: true,
|
||||||
|
previewAspectRatio: 1,
|
||||||
|
error: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock("@/hooks/use-pipeline-preview", () => ({
|
||||||
|
usePipelinePreview: hookSpy,
|
||||||
|
}));
|
||||||
|
vi.doMock("@xyflow/react", () => ({
|
||||||
|
Handle: () => null,
|
||||||
|
Position: { Left: "left", Right: "right" },
|
||||||
|
}));
|
||||||
|
vi.doMock("convex/react", () => ({
|
||||||
|
useMutation: () => vi.fn(async () => undefined),
|
||||||
|
}));
|
||||||
|
vi.doMock("lucide-react", () => ({
|
||||||
|
AlertCircle: () => null,
|
||||||
|
ArrowDown: () => null,
|
||||||
|
CheckCircle2: () => null,
|
||||||
|
CloudUpload: () => null,
|
||||||
|
Loader2: () => null,
|
||||||
|
Maximize2: () => null,
|
||||||
|
X: () => null,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/canvas/nodes/base-node-wrapper", () => ({
|
||||||
|
default: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/canvas/nodes/adjustment-controls", () => ({
|
||||||
|
SliderRow: () => null,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/ui/select", () => ({
|
||||||
|
Select: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
SelectContent: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
SelectItem: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
SelectTrigger: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
SelectValue: () => null,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/canvas/canvas-sync-context", () => ({
|
||||||
|
useCanvasSync: () => ({
|
||||||
|
queueNodeDataUpdate: vi.fn(async () => undefined),
|
||||||
|
queueNodeResize: vi.fn(async () => undefined),
|
||||||
|
status: { isOffline: false },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/hooks/use-debounced-callback", () => ({
|
||||||
|
useDebouncedCallback: (callback: () => void) => callback,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/canvas/canvas-graph-context", () => ({
|
||||||
|
useCanvasGraph: () => ({ nodes: [], edges: [] }),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/lib/canvas-render-preview", () => ({
|
||||||
|
resolveRenderPreviewInputFromGraph: () => ({
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
steps: [],
|
||||||
|
}),
|
||||||
|
findSourceNodeFromGraph: () => null,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/lib/canvas-utils", () => ({
|
||||||
|
resolveMediaAspectRatio: () => null,
|
||||||
|
}));
|
||||||
|
vi.doMock("@/lib/image-formats", () => ({
|
||||||
|
parseAspectRatioString: () => ({ w: 1, h: 1 }),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/lib/image-pipeline/contracts", async () => {
|
||||||
|
const actual = await vi.importActual<typeof import("@/lib/image-pipeline/contracts")>(
|
||||||
|
"@/lib/image-pipeline/contracts",
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
hashPipeline: () => "pipeline-hash",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
vi.doMock("@/lib/image-pipeline/worker-client", () => ({
|
||||||
|
isPipelineAbortError: () => false,
|
||||||
|
renderFullWithWorkerFallback: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.doMock("@/components/ui/dialog", () => ({
|
||||||
|
Dialog: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
DialogContent: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
DialogTitle: ({ children }: { children: React.ReactNode }) => createElement("div", null, children),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const compareSurfaceModule = await import("@/components/canvas/nodes/compare-surface");
|
||||||
|
const CompareSurface = compareSurfaceModule.default;
|
||||||
|
const renderNodeModule = await import("@/components/canvas/nodes/render-node");
|
||||||
|
const RenderNode = renderNodeModule.default;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root?.render(
|
||||||
|
createElement("div", null,
|
||||||
|
createElement(CompareSurface, {
|
||||||
|
nodeWidth: 320,
|
||||||
|
previewInput: {
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
steps: [],
|
||||||
|
},
|
||||||
|
preferPreview: true,
|
||||||
|
}),
|
||||||
|
createElement(RenderNode, {
|
||||||
|
id: "render-1",
|
||||||
|
data: {},
|
||||||
|
selected: false,
|
||||||
|
dragging: false,
|
||||||
|
zIndex: 0,
|
||||||
|
isConnectable: true,
|
||||||
|
type: "render",
|
||||||
|
xPos: 0,
|
||||||
|
yPos: 0,
|
||||||
|
width: 320,
|
||||||
|
height: 300,
|
||||||
|
sourcePosition: undefined,
|
||||||
|
targetPosition: undefined,
|
||||||
|
positionAbsoluteX: 0,
|
||||||
|
positionAbsoluteY: 0,
|
||||||
|
} as never),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(hookSpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
includeHistogram: false,
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(hookSpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
includeHistogram: false,
|
||||||
|
sourceUrl: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user