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
2 changes: 1 addition & 1 deletion packages/core/src/chat/ui/panel.generated.ts

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions packages/core/src/chat/ui/panel.jsdom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,27 @@ describe("panel.jsdom: streaming", () => {
});
});

describe("panel.jsdom: feed images", () => {
it("uses HTTP(S) URLs for list covers and reader heroes, and omits unsupported images", () => {
const p = boot();
const image = "https://example.com/cover.png";
p.$("#agentsBtn")!.click();
p.host({ type: "blogFeed", posts: [
{ id: NOTE_ID, author: WALLET, timestamp: 1725400000000, title: "Cover", image },
{ id: "unsupported", author: WALLET, timestamp: 1725400000001, title: "No cover", image: "ipfs://cover" },
] });
const rows = p.$$("#feedList .fd-row");
expect(rows[0].querySelector(".fd-cover img")!.getAttribute("src")).toBe(image);
expect(rows[1].querySelector(".fd-cover")).toBeNull();
rows[0].click();
expect(p.$("#agFeedPost .fdp-hero img")!.getAttribute("src")).toBe(image);
p.$("#agFeedPost .fdp-bar .bk")!.click();
p.$$("#feedList .fd-row")[1].click();
expect(p.$("#agFeedPost .fdp-hero")).toBeNull();
expect(p.errors).toEqual([]);
});
});

describe("panel.jsdom: feed quote cards", () => {
const QUOTED = noteId(OTHER_WALLET, 1725400000001, "q9z2ab");
const DEAD = noteId(OTHER_WALLET, 1725400000002, "dead01");
Expand Down
25 changes: 9 additions & 16 deletions packages/core/src/chat/ui/panel/format.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,15 @@ describe("panel/format: explorerTxUrl follows S.rpcNetwork", () => {
});
});

describe("panel/format: feedImgUrl, pinned as it shipped", () => {
// The intended /^https?:\/\//i lost its backslashes inside the old template literal, so the
// shipped line is `return v && /^https?:/` with the rest of the line a comment. This pins what
// the panel does today, not what the comment above the function intends; changing which covers
// render is a follow-up, not part of the port.
it("passes falsy input through unchanged", () => {
expect(feedImgUrl("")).toBe("");
expect(feedImgUrl(null)).toBe(null);
expect(feedImgUrl(undefined)).toBe(undefined);
});
it("returns the bare /^https?:/ RegExp for any truthy input, http or not", () => {
for (const v of ["https://x/a.png", "http://x", "ipfs://x", W]) {
const out = feedImgUrl(v);
expect(out).toBeInstanceOf(RegExp);
expect(out.source).toBe("^https?:");
expect(out.flags).toBe("");
describe("panel/format: feedImgUrl", () => {
it("returns the original HTTP(S) image URL", () => {
for (const url of ["https://example.com/cover.png", "http://example.com/a.jpg?size=2", "HTTPS://example.com/cover.png"]) {
expect(feedImgUrl(url)).toBe(url);
}
});
it("omits missing images and values the panel cannot resolve", () => {
for (const value of ["", null, undefined, "ipfs://x", "git://example.com/x", "data:image/png;base64,AA==", "javascript:alert(1)", "//example.com/x", "/cover.png", "https:/example.com/x", W]) {
expect(feedImgUrl(value)).toBeNull();
}
});
});
Expand Down
8 changes: 2 additions & 6 deletions packages/core/src/chat/ui/panel/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,8 @@ export function fdAgo(ms) {
}
// http(s) images only: the panel has no gateway media resolver, so on-chain
// address / tx-id images (rare) fall back to no cover rather than a broken img.
// Shipped as written: inside the old template literal the intended /^https?:\/\//i lost its
// backslashes (a template's `\/` is just `/`), so this line is the regex /^https?:/ followed
// by a line comment, and every truthy input returns that RegExp. Kept verbatim under the
// port's no-behavior-change rule; the `any` only names that shape (the fix is a follow-up).
export function feedImgUrl(v): any {
return v && /^https?:///i.test(v) ? v : null;
export function feedImgUrl(v: string | null | undefined): string | null {
return v && /^https?:\/\//i.test(v) ? v : null;
}
export function agShort(w) { return w.slice(0, 6) + '...' + w.slice(-4); }
export function pad2(n) { return n < 10 ? '0' + n : String(n); }
Expand Down