chore: import upstream snapshot with attribution
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Tests for the code block language helpers.
|
||||
*
|
||||
* Covers id/alias lookup, normalization of free-form input, and human-readable
|
||||
* label fallback. These are the helpers backing the editor's language picker
|
||||
* and the round-trip between user input and the stored `language` attribute.
|
||||
*/
|
||||
|
||||
import { i18n } from "@lingui/core";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import {
|
||||
CODE_BLOCK_LANGUAGES,
|
||||
findLanguage,
|
||||
languageLabelDescriptor,
|
||||
normalizeLanguage,
|
||||
} from "../../src/components/editor/codeBlockLanguages";
|
||||
|
||||
function resolveLabel(
|
||||
label: ReturnType<typeof languageLabelDescriptor> | undefined,
|
||||
): string | undefined {
|
||||
if (!label) return undefined;
|
||||
return typeof label === "string" ? label : i18n._(label);
|
||||
}
|
||||
|
||||
describe("findLanguage", () => {
|
||||
it("returns the canonical entry for a known id", () => {
|
||||
const ts = findLanguage("typescript");
|
||||
expect(ts?.id).toBe("typescript");
|
||||
expect(resolveLabel(ts?.label)).toBe("TypeScript");
|
||||
});
|
||||
|
||||
it("resolves aliases to the canonical entry", () => {
|
||||
expect(findLanguage("ts")?.id).toBe("typescript");
|
||||
expect(findLanguage("js")?.id).toBe("javascript");
|
||||
expect(findLanguage("sh")?.id).toBe("bash");
|
||||
expect(findLanguage("c++")?.id).toBe("cpp");
|
||||
expect(findLanguage("c#")?.id).toBe("csharp");
|
||||
expect(findLanguage("md")?.id).toBe("markdown");
|
||||
expect(findLanguage("yml")?.id).toBe("yaml");
|
||||
});
|
||||
|
||||
it("is case-insensitive", () => {
|
||||
expect(findLanguage("TypeScript")?.id).toBe("typescript");
|
||||
expect(findLanguage("HTML")?.id).toBe("html");
|
||||
expect(findLanguage(" Python ")?.id).toBe("python");
|
||||
});
|
||||
|
||||
it("returns null for unknown or empty input", () => {
|
||||
expect(findLanguage("brainfuck")).toBeNull();
|
||||
expect(findLanguage("")).toBeNull();
|
||||
expect(findLanguage(null)).toBeNull();
|
||||
expect(findLanguage(undefined)).toBeNull();
|
||||
expect(findLanguage(" ")).toBeNull();
|
||||
});
|
||||
|
||||
it("has no duplicate ids or aliases in the curated list", () => {
|
||||
const seen = new Set<string>();
|
||||
for (const lang of CODE_BLOCK_LANGUAGES) {
|
||||
expect(seen.has(lang.id), `duplicate id: ${lang.id}`).toBe(false);
|
||||
seen.add(lang.id);
|
||||
for (const alias of lang.aliases ?? []) {
|
||||
expect(seen.has(alias), `duplicate alias: ${alias}`).toBe(false);
|
||||
seen.add(alias);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeLanguage", () => {
|
||||
it("returns the canonical id for a known language or alias", () => {
|
||||
expect(normalizeLanguage("ts")).toBe("typescript");
|
||||
expect(normalizeLanguage("TypeScript")).toBe("typescript");
|
||||
expect(normalizeLanguage("c++")).toBe("cpp");
|
||||
});
|
||||
|
||||
it("lowercases and trims unknown input so class names stay stable", () => {
|
||||
expect(normalizeLanguage(" Brainfuck ")).toBe("brainfuck");
|
||||
expect(normalizeLanguage("Erlang")).toBe("erlang");
|
||||
});
|
||||
|
||||
it("collapses internal whitespace so the value remains a single class token", () => {
|
||||
// Frontend renders `<pre class="language-${id}">`; whitespace would
|
||||
// split that into two classes (`language-objective c`), so unknown
|
||||
// inputs with spaces are joined with a single hyphen.
|
||||
expect(normalizeLanguage("Objective C")).toBe("objective-c");
|
||||
expect(normalizeLanguage("My Lang")).toBe("my-lang");
|
||||
expect(normalizeLanguage("Pure\tScript")).toBe("pure-script");
|
||||
expect(normalizeLanguage("a\nb")).toBe("a-b");
|
||||
});
|
||||
|
||||
it("strips other characters that would break a CSS class token", () => {
|
||||
// Dots, slashes, and other punctuation get collapsed to `-`.
|
||||
expect(normalizeLanguage("foo.bar")).toBe("foo-bar");
|
||||
expect(normalizeLanguage("a/b")).toBe("a-b");
|
||||
expect(normalizeLanguage("plain!text")).toBe("plain-text");
|
||||
});
|
||||
|
||||
it("preserves hyphens and underscores in unknown input", () => {
|
||||
expect(normalizeLanguage("my-lang")).toBe("my-lang");
|
||||
expect(normalizeLanguage("my_lang")).toBe("my_lang");
|
||||
});
|
||||
|
||||
it("trims leading and trailing hyphens introduced by sanitization", () => {
|
||||
expect(normalizeLanguage("@swift")).toBe("swift");
|
||||
expect(normalizeLanguage("rust!")).toBe("rust");
|
||||
expect(normalizeLanguage("---x---")).toBe("x");
|
||||
});
|
||||
|
||||
it("returns undefined for input that sanitizes to an empty string", () => {
|
||||
expect(normalizeLanguage("!!!")).toBeUndefined();
|
||||
expect(normalizeLanguage("@@@")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined for empty input", () => {
|
||||
expect(normalizeLanguage("")).toBeUndefined();
|
||||
expect(normalizeLanguage(null)).toBeUndefined();
|
||||
expect(normalizeLanguage(undefined)).toBeUndefined();
|
||||
expect(normalizeLanguage(" ")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("languageLabelDescriptor", () => {
|
||||
it("returns the curated label for known languages", () => {
|
||||
expect(resolveLabel(languageLabelDescriptor("typescript"))).toBe("TypeScript");
|
||||
expect(resolveLabel(languageLabelDescriptor("ts"))).toBe("TypeScript");
|
||||
expect(resolveLabel(languageLabelDescriptor("cpp"))).toBe("C++");
|
||||
});
|
||||
|
||||
it("falls back to the raw id for unknown languages", () => {
|
||||
expect(resolveLabel(languageLabelDescriptor("brainfuck"))).toBe("brainfuck");
|
||||
});
|
||||
|
||||
it("returns a friendly default for empty input", () => {
|
||||
expect(resolveLabel(languageLabelDescriptor(null))).toBe("Plain text");
|
||||
expect(resolveLabel(languageLabelDescriptor(undefined))).toBe("Plain text");
|
||||
expect(resolveLabel(languageLabelDescriptor(""))).toBe("Plain text");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user