Extract parse/renderHtmlEmbedHeight and test: '300'->300, absent->null,
'abc'->null (pins the NaN guard), '120px'->120; render 120->data-height, null/0->{}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
encodeHtmlEmbedSource,
|
||||
decodeHtmlEmbedSource,
|
||||
parseHtmlEmbedHeight,
|
||||
renderHtmlEmbedHeight,
|
||||
} from "./html-embed";
|
||||
|
||||
// Unit coverage for the base64 codec used by the htmlEmbed node's
|
||||
@@ -118,6 +120,45 @@ describe("html-embed codec — encode failure fallback", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("html-embed height — parseHtmlEmbedHeight (data-height -> px | null)", () => {
|
||||
it('parses a numeric string ("300" -> 300)', () => {
|
||||
expect(parseHtmlEmbedHeight("300")).toBe(300);
|
||||
});
|
||||
|
||||
it("parses an absent value (null -> null = auto-resize)", () => {
|
||||
expect(parseHtmlEmbedHeight(null)).toBeNull();
|
||||
expect(parseHtmlEmbedHeight("")).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a non-numeric value ("abc" -> null) — pins the NaN guard (BUG-2)', () => {
|
||||
// Without Number.isFinite this would be NaN (typeof "number"), disabling
|
||||
// auto-resize and yielding an unclamped iframe height downstream.
|
||||
expect(parseHtmlEmbedHeight("abc")).toBeNull();
|
||||
});
|
||||
|
||||
it('parses a trailing-unit value ("120px" -> 120) via parseInt', () => {
|
||||
expect(parseHtmlEmbedHeight("120px")).toBe(120);
|
||||
});
|
||||
});
|
||||
|
||||
describe("html-embed height — renderHtmlEmbedHeight (px -> data-height | {})", () => {
|
||||
it("renders a fixed height (120 -> { data-height: '120' })", () => {
|
||||
expect(renderHtmlEmbedHeight(120)).toEqual({ "data-height": "120" });
|
||||
});
|
||||
|
||||
it("renders auto-resize as no attribute (null -> {})", () => {
|
||||
expect(renderHtmlEmbedHeight(null)).toEqual({});
|
||||
});
|
||||
|
||||
it("renders 0 as no attribute (0 is auto -> {})", () => {
|
||||
expect(renderHtmlEmbedHeight(0)).toEqual({});
|
||||
});
|
||||
|
||||
it("renders undefined as no attribute (absent -> {})", () => {
|
||||
expect(renderHtmlEmbedHeight(undefined)).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("html-embed codec — decode of malformed input (browser branch)", () => {
|
||||
it("returns '' for input atob rejects (catch branch)", () => {
|
||||
// atob throws on characters outside the base64 alphabet; the codec catches
|
||||
|
||||
@@ -69,6 +69,30 @@ export function decodeHtmlEmbedSource(encoded: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the `data-height` attribute value into a fixed iframe height in px.
|
||||
*
|
||||
* Returns null (auto-resize) when the value is absent, empty, or non-numeric.
|
||||
* A non-numeric `data-height` (e.g. a crafted/corrupted import) must NOT become
|
||||
* NaN: NaN is typeof "number" and would disable auto-resize and yield an
|
||||
* unclamped iframe height downstream. The Number.isFinite guard pins that fix.
|
||||
*/
|
||||
export function parseHtmlEmbedHeight(value: string | null): number | null {
|
||||
if (!value) return null;
|
||||
const n = parseInt(value, 10);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a fixed height back to a `data-height` attribute. A null/0/absent
|
||||
* height means auto-resize, so no attribute is emitted.
|
||||
*/
|
||||
export function renderHtmlEmbedHeight(
|
||||
height: number | null | undefined,
|
||||
): { "data-height": string } | Record<string, never> {
|
||||
return height ? { "data-height": String(height) } : {};
|
||||
}
|
||||
|
||||
export const HtmlEmbed = Node.create<HtmlEmbedOptions>({
|
||||
name: "htmlEmbed",
|
||||
inline: false,
|
||||
@@ -103,17 +127,9 @@ export const HtmlEmbed = Node.create<HtmlEmbedOptions>({
|
||||
// Fixed iframe height in px. null/absent => auto-resize on the client.
|
||||
height: {
|
||||
default: null,
|
||||
parseHTML: (el) => {
|
||||
const v = el.getAttribute("data-height");
|
||||
if (!v) return null;
|
||||
const n = parseInt(v, 10);
|
||||
// A non-numeric data-height (e.g. crafted/corrupted import) must not
|
||||
// become NaN: NaN is typeof "number" and would disable auto-resize and
|
||||
// yield an unclamped iframe height downstream. Treat it as auto (null).
|
||||
return Number.isFinite(n) ? n : null;
|
||||
},
|
||||
parseHTML: (el) => parseHtmlEmbedHeight(el.getAttribute("data-height")),
|
||||
renderHTML: (attrs: HtmlEmbedAttributes) =>
|
||||
attrs.height ? { "data-height": String(attrs.height) } : {},
|
||||
renderHtmlEmbedHeight(attrs.height),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user