File size: 5,766 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | import { describe, expect, it } from "vitest";
import { addOsc8Hyperlinks, extractUrls, wrapOsc8 } from "./osc8-hyperlinks.js";
describe("wrapOsc8", () => {
it("wraps text with OSC 8 open and close sequences", () => {
const result = wrapOsc8("https://example.com", "click here");
expect(result).toBe("\x1b]8;;https://example.com\x07click here\x1b]8;;\x07");
});
it("handles empty text", () => {
const result = wrapOsc8("https://example.com", "");
expect(result).toBe("\x1b]8;;https://example.com\x07\x1b]8;;\x07");
});
});
describe("extractUrls", () => {
it("extracts bare URLs", () => {
const urls = extractUrls("Check out https://example.com for more info");
expect(urls).toEqual(["https://example.com"]);
});
it("extracts multiple bare URLs", () => {
const urls = extractUrls("Visit https://foo.com and http://bar.com");
expect(urls).toContain("https://foo.com");
expect(urls).toContain("http://bar.com");
expect(urls).toHaveLength(2);
});
it("extracts markdown link hrefs", () => {
const urls = extractUrls("[Click here](https://example.com/path)");
expect(urls).toEqual(["https://example.com/path"]);
});
it("extracts markdown links with angle brackets and title text", () => {
const urls = extractUrls('[Click here](<https://example.com/path> "Example Title")');
expect(urls).toEqual(["https://example.com/path"]);
});
it("extracts both bare URLs and markdown links", () => {
const md = "See [docs](https://docs.example.com) and https://api.example.com";
const urls = extractUrls(md);
expect(urls).toContain("https://docs.example.com");
expect(urls).toContain("https://api.example.com");
expect(urls).toHaveLength(2);
});
it("deduplicates URLs", () => {
const md = "Visit https://example.com and [link](https://example.com)";
const urls = extractUrls(md);
expect(urls).toEqual(["https://example.com"]);
});
it("returns empty array for text without URLs", () => {
expect(extractUrls("No links here")).toEqual([]);
});
it("handles URLs with query params and fragments", () => {
const urls = extractUrls("https://example.com/path?q=1&r=2#section");
expect(urls).toEqual(["https://example.com/path?q=1&r=2#section"]);
});
});
describe("addOsc8Hyperlinks", () => {
it("returns lines unchanged when no URLs", () => {
const lines = ["Hello world", "No links here"];
expect(addOsc8Hyperlinks(lines, [])).toEqual(lines);
});
it("wraps a single-line URL with OSC 8", () => {
const url = "https://example.com";
const lines = [`Visit ${url} for info`];
const result = addOsc8Hyperlinks(lines, [url]);
expect(result[0]).toContain(`\x1b]8;;${url}\x07`);
expect(result[0]).toContain(`\x1b]8;;\x07`);
// The URL text should be between open and close
expect(result[0]).toBe(`Visit \x1b]8;;${url}\x07${url}\x1b]8;;\x07 for info`);
});
it("wraps a URL broken across two lines", () => {
const fullUrl = "https://example.com/very/long/path/to/resource";
const lines = ["https://example.com/very/long/pa", "th/to/resource"];
const result = addOsc8Hyperlinks(lines, [fullUrl]);
// Line 1: fragment should be wrapped with the full URL
expect(result[0]).toContain(`\x1b]8;;${fullUrl}\x07`);
// Line 2: continuation should also be wrapped
expect(result[1]).toContain(`\x1b]8;;${fullUrl}\x07`);
});
it("handles URL with ANSI styling codes", () => {
const url = "https://example.com";
// Simulate styled text: green URL
const styledLine = `\x1b[32m${url}\x1b[0m`;
const result = addOsc8Hyperlinks([styledLine], [url]);
// Should preserve ANSI codes and add OSC 8 around the visible URL
expect(result[0]).toContain("\x1b[32m");
expect(result[0]).toContain("\x1b[0m");
expect(result[0]).toContain(`\x1b]8;;${url}\x07`);
expect(result[0]).toContain(`\x1b]8;;\x07`);
});
it("handles named link rendered as text (url)", () => {
const url = "https://github.com/org/repo";
// pi-tui renders [text](url) as "text (url)"
const line = `Click here (${url})`;
const result = addOsc8Hyperlinks([line], [url]);
// The URL part should be wrapped with OSC 8
expect(result[0]).toContain(`\x1b]8;;${url}\x07`);
});
it("handles multiple URLs on the same line", () => {
const url1 = "https://foo.com";
const url2 = "https://bar.com";
const line = `${url1} and ${url2}`;
const result = addOsc8Hyperlinks([line], [url1, url2]);
expect(result[0]).toContain(`\x1b]8;;${url1}\x07`);
expect(result[0]).toContain(`\x1b]8;;${url2}\x07`);
});
it("does not modify lines without URL text", () => {
const url = "https://example.com";
const lines = ["Just some text", "No URLs here"];
const result = addOsc8Hyperlinks(lines, [url]);
expect(result).toEqual(lines);
});
it("prefers the longest known URL when a fragment matches multiple prefixes", () => {
const short = "https://example.com/api/v2/users";
const long = "https://example.com/api/v2/users/list";
const fragment = "https://example.com/api/v2/u";
const result = addOsc8Hyperlinks([fragment], [short, long]);
expect(result[0]).toContain(`\x1b]8;;${long}\x07${fragment}\x1b]8;;\x07`);
});
it("handles URL split across three lines", () => {
const fullUrl = "https://example.com/a/very/long/path/that/keeps/going/and/going";
const lines = ["https://example.com/a/very/lon", "g/path/that/keeps/going/and/g", "oing"];
const result = addOsc8Hyperlinks(lines, [fullUrl]);
// All three lines should have OSC 8 wrapping
for (const line of result) {
expect(line).toContain(`\x1b]8;;${fullUrl}\x07`);
expect(line).toContain(`\x1b]8;;\x07`);
}
});
});
|