- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { mergeWidgetCode, parseMixedWidgetCode, scopeWidgetCss } from "./widgetCode.ts";
|
|
|
|
describe("parseMixedWidgetCode", () => {
|
|
it("拆分混写的 style/script 段", () => {
|
|
const r = parseMixedWidgetCode(
|
|
`<div class="a">hi</div>\n<style>.a { color: red; }</style>\n<script>console.log(1);</script>`
|
|
);
|
|
assert.equal(r.html, `<div class="a">hi</div>`);
|
|
assert.equal(r.css, `.a { color: red; }`);
|
|
assert.equal(r.js, `console.log(1);`);
|
|
});
|
|
|
|
it("多段 style/script 按序拼接", () => {
|
|
const r = parseMixedWidgetCode(`<style>.a{}</style><style>.b{}</style><p>x</p><script>a();</script><script>b();</script>`);
|
|
assert.equal(r.css, ".a{}\n\n.b{}");
|
|
assert.equal(r.js, "a();\n\nb();");
|
|
assert.equal(r.html, "<p>x</p>");
|
|
});
|
|
|
|
it("纯 HTML 不误伤", () => {
|
|
const r = parseMixedWidgetCode(`<p>hello</p>`);
|
|
assert.deepEqual(r, { html: "<p>hello</p>", css: "", js: "" });
|
|
});
|
|
|
|
it("外链与非 JS type 的 script 留在 html", () => {
|
|
const code = `<script src="https://x/y.js"></script><script type="application/json">{"a":1}</script><script>z();</script>`;
|
|
const r = parseMixedWidgetCode(code);
|
|
assert.equal(r.js, "z();");
|
|
assert.equal(
|
|
r.html,
|
|
`<script src="https://x/y.js"></script><script type="application/json">{"a":1}</script>`
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("mergeWidgetCode", () => {
|
|
it("合并后再解析可还原三字段", () => {
|
|
const parts = { html: "<b>go</b>", css: "b { font-size: 18px; }", js: "x();" };
|
|
assert.deepEqual(parseMixedWidgetCode(mergeWidgetCode(parts)), parts);
|
|
});
|
|
|
|
it("空段不产生标签", () => {
|
|
assert.equal(mergeWidgetCode({ html: "hi", css: "", js: "" }), "hi");
|
|
});
|
|
});
|
|
|
|
describe("scopeWidgetCss", () => {
|
|
const p = ".j13-widget-7";
|
|
|
|
it("普通规则加前缀", () => {
|
|
assert.equal(scopeWidgetCss(".a { color: red; }", 7), `${p} .a { color: red; }`);
|
|
});
|
|
|
|
it("选择器列表逐个前缀,括号内逗号不分割", () => {
|
|
assert.equal(
|
|
scopeWidgetCss(".a, .b {}", 7),
|
|
`${p} .a, ${p} .b {}`
|
|
);
|
|
assert.equal(
|
|
scopeWidgetCss(':is(.a, .b) { color: red }', 7),
|
|
`${p} :is(.a, .b) { color: red }`
|
|
);
|
|
});
|
|
|
|
it("root/body 选择器替换为容器本身", () => {
|
|
assert.equal(scopeWidgetCss(":root { --x: 1; }", 7), `${p} { --x: 1; }`);
|
|
assert.equal(scopeWidgetCss("body { margin: 0 }", 7), `${p} { margin: 0 }`);
|
|
assert.equal(scopeWidgetCss("body .card {}", 7), `${p} .card {}`);
|
|
});
|
|
|
|
it("容器自身伪类紧贴前缀", () => {
|
|
assert.equal(scopeWidgetCss(":hover { opacity: .8 }", 7), `${p}:hover { opacity: .8 }`);
|
|
});
|
|
|
|
it("@media 内层前缀化,头部原样", () => {
|
|
assert.equal(
|
|
scopeWidgetCss("@media (min-width: 10px) { .a {} }", 7),
|
|
`@media (min-width: 10px) { ${p} .a {} }`
|
|
);
|
|
});
|
|
|
|
it("@keyframes 原样保留", () => {
|
|
const css = "@keyframes spin { from { opacity: 0 } to { opacity: 1 } }";
|
|
assert.equal(scopeWidgetCss(css, 7), css);
|
|
});
|
|
|
|
it("@import 语句原样保留", () => {
|
|
assert.equal(
|
|
scopeWidgetCss('@import url("a.css"); .a {}', 7),
|
|
'@import url("a.css"); .j13-widget-7 .a {}'
|
|
);
|
|
});
|
|
|
|
it("注释与字符串内的花括号不干扰配对", () => {
|
|
assert.equal(
|
|
scopeWidgetCss("/* { nope } */ .a::after { content: \"{\" }", 7),
|
|
`/* { nope } */ ${p} .a::after { content: "{" }`
|
|
);
|
|
});
|
|
});
|