feat(redis): add JSON value viewer

This commit is contained in:
t8y2
2026-05-22 10:58:30 +08:00
parent b23988161a
commit ed28040555
9 changed files with 577 additions and 71 deletions
@@ -0,0 +1,41 @@
import { strict as assert } from "node:assert";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import test from "node:test";
import { createRedisShikiJsonHighlighter } from "../../apps/desktop/src/lib/redisJsonHighlighter.ts";
const redisViewerSource = readFileSync(
join(process.cwd(), "apps/desktop/src/components/redis/RedisValueViewer.vue"),
"utf8",
);
const jsonTreeSource = readFileSync(join(process.cwd(), "apps/desktop/src/components/redis/RedisJsonTree.vue"), "utf8");
test("Redis string values expose JSON and raw content views", () => {
assert.match(redisViewerSource, /stringValueDetail/);
assert.match(redisViewerSource, /stringValueView === 'json'/);
assert.match(redisViewerSource, /RedisJsonTree/);
assert.match(redisViewerSource, /redis\.jsonView/);
assert.match(redisViewerSource, /redis\.rawContent/);
});
test("Redis JSON tree supports folding and word wrap", () => {
assert.match(jsonTreeSource, /collapsedPaths/);
assert.match(jsonTreeSource, /toggleCollapsed/);
assert.match(jsonTreeSource, /wordWrap/);
assert.match(jsonTreeSource, /ChevronRight/);
assert.match(jsonTreeSource, /ChevronDown/);
});
test("Redis JSON raw content uses Shiki highlighting safely", async () => {
assert.match(redisViewerSource, /createRedisShikiJsonHighlighter/);
assert.match(redisViewerSource, /:highlight-json="highlightRedisJson"/);
assert.match(redisViewerSource, /v-html="memberRawJsonHtml"/);
const highlight = await createRedisShikiJsonHighlighter({ appearance: () => "dark" });
const html = highlight('{"name":"<script>","active":true}');
assert.match(html, /style=/);
assert.match(html, /(?:&lt;|&#x3C;)script(?:&gt;|>)/);
assert.doesNotMatch(html, /<script>/);
assert.doesNotMatch(html, /<pre/);
});
@@ -8,12 +8,14 @@ import {
formatRedisStringValue,
getRedisMemberSelectionKey,
highlightRedisJsonDetail,
parseRedisJsonDetail,
} from "../../apps/desktop/src/lib/redisValuePresentation.ts";
test("formats JSON object strings for Redis member details", () => {
const detail = formatRedisMemberDetail('{"id":1,"name":"Ada","tags":["dbx","redis"]}');
assert.equal(detail.format, "json");
assert.equal(detail.rawText, '{"id":1,"name":"Ada","tags":["dbx","redis"]}');
assert.equal(detail.text, '{\n "id": 1,\n "name": "Ada",\n "tags": [\n "dbx",\n "redis"\n ]\n}');
});
@@ -29,6 +31,18 @@ test("formats JSON string values without changing plain strings", () => {
assert.equal(formatRedisStringValue("plain redis value"), "plain redis value");
});
test("parses Redis JSON details only for object and array containers", () => {
const objectDetail = parseRedisJsonDetail('{"id":1,"name":"Ada"}');
assert.equal(objectDetail?.rawText, '{"id":1,"name":"Ada"}');
assert.equal(objectDetail?.formattedText, '{\n "id": 1,\n "name": "Ada"\n}');
assert.deepEqual(objectDetail?.value, { id: 1, name: "Ada" });
assert.equal(parseRedisJsonDetail("[1,2]")?.formattedText, "[\n 1,\n 2\n]");
assert.equal(parseRedisJsonDetail('"plain json string"'), null);
assert.equal(parseRedisJsonDetail("123"), null);
assert.equal(parseRedisJsonDetail("plain redis value"), null);
});
test("formats Redis command results with JSON strings expanded", () => {
assert.equal(formatRedisCommandResult('{"balance":42,"unit":"USD"}'), '{\n "balance": 42,\n "unit": "USD"\n}');
assert.equal(formatRedisCommandResult(["a", 2]), '[\n "a",\n 2\n]');