|
2 | 2 | import { render } from "lit"; |
3 | 3 | import { describe, expect, it, vi } from "vitest"; |
4 | 4 | import { i18n } from "../i18n/index.ts"; |
5 | | -import { decodeCodeBlockCopyPayload } from "./chat/code-block-copy-payload.ts"; |
| 5 | +import { |
| 6 | + blockArtCodeBlockCopyPayloadEncoding, |
| 7 | + decodeCodeBlockCopyPayload, |
| 8 | +} from "./chat/code-block-copy-payload.ts"; |
6 | 9 | import { |
7 | 10 | md, |
8 | 11 | toSanitizedMarkdownHtml, |
@@ -379,6 +382,7 @@ describe("toSanitizedMarkdownHtml", () => { |
379 | 382 |
|
380 | 383 | expect(fragment.querySelector(".code-block-lang")?.textContent).toBe("ts"); |
381 | 384 | expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "")).toBe("console.log(1)"); |
| 385 | + expect(copy?.dataset.codeEncoding).toBeUndefined(); |
382 | 386 | expect(code?.classList.contains("language-ts")).toBe(true); |
383 | 387 | expect(code?.textContent).toBe("console.log(1)\n"); |
384 | 388 | }); |
|
458 | 462 | expect(fragment.querySelector(".code-block-lang")?.textContent).toBe("js"); |
459 | 463 | expect(copy?.dataset.code).toBe(source.trimEnd()); |
460 | 464 | expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "")).toBe(source.trimEnd()); |
| 465 | + expect(copy?.dataset.codeEncoding).toBeUndefined(); |
461 | 466 | expect(code?.textContent).toBe(source); |
462 | 467 | expect(code?.querySelector(".hljs-keyword")?.textContent).toBe("const"); |
463 | 468 | expect(code?.querySelector(".hljs-string")?.textContent).toBe('"yes"'); |
464 | 469 | }); |
465 | 470 |
|
| 471 | + it("keeps ordinary code blocks raw when they start with the block-art prefix", () => { |
| 472 | + const source = 'openclaw:block-art-code:"literal"\n'; |
| 473 | + const html = toSanitizedMarkdownHtml(`\`\`\`txt\n${source}\`\`\``); |
| 474 | + const fragment = htmlFragment(html); |
| 475 | + const copy = fragment.querySelector<HTMLButtonElement>(".code-block-copy"); |
| 476 | + |
| 477 | + expect(copy?.dataset.code).toBe(source.trimEnd()); |
| 478 | + expect(copy?.dataset.codeEncoding).toBeUndefined(); |
| 479 | + expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "", copy?.dataset.codeEncoding)).toBe( |
| 480 | + source.trimEnd(), |
| 481 | + ); |
| 482 | + }); |
| 483 | + |
466 | 484 | it("keeps boundary spaces in encoded copy payloads after sanitization", () => { |
467 | 485 | const source = " ▀▀▀▀ \n ▄▄▄▄ "; |
468 | 486 | const html = toSanitizedMarkdownHtml(`\`\`\`\n${source}\n\`\`\``); |
|
471 | 489 |
|
472 | 490 | expect(copy?.dataset.code).not.toMatch(/^\s|\s$/); |
473 | 491 | expect(copy?.dataset.code).toContain("openclaw:block-art-code:"); |
474 | | - expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "")).toBe(source); |
| 492 | + expect(copy?.dataset.codeEncoding).toBe(blockArtCodeBlockCopyPayloadEncoding); |
| 493 | + expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "", copy?.dataset.codeEncoding)).toBe( |
| 494 | + source, |
| 495 | + ); |
475 | 496 | expect(fragment.querySelector("pre code")?.textContent).toBe(`${source}\n`); |
476 | 497 | }); |
477 | 498 |
|
@@ -811,7 +832,10 @@ describe("toStreamingMarkdownHtml", () => { |
811 | 832 | expect(code?.textContent).toContain(`showing first 140000`); |
812 | 833 | expect(code?.textContent?.length).toBeLessThan(blockArt.length); |
813 | 834 | expect(copy?.dataset.code).toContain("openclaw:block-art-code:"); |
814 | | - expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "")).toBe(code?.textContent); |
| 835 | + expect(copy?.dataset.codeEncoding).toBe(blockArtCodeBlockCopyPayloadEncoding); |
| 836 | + expect(decodeCodeBlockCopyPayload(copy?.dataset.code ?? "", copy?.dataset.codeEncoding)).toBe( |
| 837 | + code?.textContent, |
| 838 | + ); |
815 | 839 | }); |
816 | 840 |
|
817 | 841 | it("renders completed block prefixes as markdown and keeps the open tail plain", () => { |
|
0 commit comments