返回 DeepSeek-Reasonix
code-block-copy-position.test.tsx
根目录 / desktop / frontend / src / __tests__ / code-block-copy-position.test.tsx
1 // Run: tsx src/__tests__/code-block-copy-position.test.tsx
2
3 import { readFileSync } from "node:fs";
4 import { fileURLToPath } from "node:url";
5 import { JSDOM } from "jsdom";
6 import { createElement } from "react";
7 import { renderToStaticMarkup } from "react-dom/server";
8 import HljsCode from "../components/editors/HljsCode";
9 import { LocaleProvider } from "../lib/i18n";
10
11 let passed = 0;
12 let failed = 0;
13
14 function ok(value: unknown, label: string) {
15 if (value) {
16 process.stdout.write(` PASS ${label}\n`);
17 passed += 1;
18 } else {
19 process.stdout.write(` FAIL ${label}\n`);
20 failed += 1;
21 }
22 }
23
24 function notOk(value: unknown, label: string) {
25 ok(!value, label);
26 }
27
28 function renderCodeBlock() {
29 return renderToStaticMarkup(
30 createElement(
31 LocaleProvider,
32 null,
33 createElement(HljsCode, {
34 value: "const veryLongLine = 'x'.repeat(240);",
35 language: "ts",
36 maxHeight: 120,
37 }),
38 ),
39 );
40 }
41
42 function selectorPresent(css: string, selector: string): boolean {
43 return css.includes(selector);
44 }
45
46 console.log("\ncode block copy position");
47
48 const markup = renderCodeBlock();
49 const dom = new JSDOM(markup);
50 const document = dom.window.document;
51 const wrap = document.querySelector(".code-block__wrap");
52 const pre = document.querySelector("pre.code.hljs");
53 const copy = document.querySelector(".code-block__copy");
54
55 ok(wrap, "HljsCode renders a positioned copy wrapper");
56 ok(pre, "HljsCode renders the highlighted pre");
57 ok(copy, "HljsCode renders the copy button");
58 ok(pre?.parentElement === wrap, "scrollable pre is inside the wrapper");
59 ok(copy?.parentElement === wrap, "copy button is a wrapper child");
60 notOk(pre?.contains(copy ?? null), "copy button is outside the scrollable pre");
61 ok(copy?.previousElementSibling === pre, "copy button follows pre as a sibling");
62
63 const stylesPath = fileURLToPath(new URL("../styles.css", import.meta.url));
64 const css = readFileSync(stylesPath, "utf8");
65
66 ok(selectorPresent(css, ".code-block__wrap:hover .code-block__copy"), "hover reveal targets the wrapper");
67 notOk(selectorPresent(css, ".code:hover .code-block__copy"), "hover reveal no longer depends on pre descendants");
68 ok(
69 selectorPresent(css, ".app--creation .msg--assistant .md .code:not([data-lang]) + .code-block__copy"),
70 "creation unlabelled code button styles target sibling copy buttons",
71 );
72 notOk(
73 selectorPresent(css, ".app--creation .msg--assistant .md .code:not([data-lang]) .code-block__copy"),
74 "creation unlabelled code styles do not target descendants inside pre",
75 );
76 ok(
77 selectorPresent(css, '.app--creation .msg--assistant .md .code[data-lang="bash"] + .code-block__copy'),
78 "creation shell code button styles target sibling copy buttons",
79 );
80 notOk(
81 selectorPresent(css, '.app--creation .msg--assistant .md .code[data-lang="bash"] .code-block__copy'),
82 "creation shell code styles do not target descendants inside pre",
83 );
84
85 console.log(`\n${passed} passed, ${failed} failed`);
86 if (failed > 0) process.exit(1);
87
87 lines Plain Text