80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { scopeCss } from "../scripts/scope-css.mjs";
|
|
|
|
const one = (css: string) => scopeCss(css).replace(/\s+/gu, " ").trim();
|
|
|
|
describe("scoping the built stylesheet", () => {
|
|
it("prefixes a utility with the view root, keeping the root itself in reach", () => {
|
|
expect(one(".flex{display:flex!important}")).toBe(
|
|
":is(.beaver-root, .beaver-root *).flex { display: flex !important; }"
|
|
);
|
|
});
|
|
|
|
it("outranks the same utility from another plugin's Tailwind", () => {
|
|
// Class specificity: theirs is (0,1,0); ours must be (0,2,0).
|
|
const out = one(".hidden{display:none!important}");
|
|
expect(out.startsWith(":is(.beaver-root, .beaver-root *).hidden")).toBe(
|
|
true
|
|
);
|
|
});
|
|
|
|
it("keeps pseudo-elements at the end where they are valid", () => {
|
|
expect(one(".x::placeholder{color:red}")).toBe(
|
|
":is(.beaver-root, .beaver-root *).x::placeholder { color: red; }"
|
|
);
|
|
expect(one(".y::first-letter{t:1}")).toContain(".y:first-letter");
|
|
});
|
|
|
|
it("scopes rules nested in container queries and layers", () => {
|
|
const out = one(
|
|
"@layer utilities{@container (min-width:30rem){.a{display:inline-block!important}}}"
|
|
);
|
|
// lightningcss rewrites the query into range syntax; Obsidian's Chromium
|
|
// has understood that inside @container since container queries landed.
|
|
expect(out).toContain("@container (width >= 30rem)");
|
|
expect(out).toContain(":is(.beaver-root, .beaver-root *).a");
|
|
});
|
|
|
|
it("drops a leading universal selector instead of producing `:is()*`", () => {
|
|
const out = one("*,:before,::backdrop{--tw-a:initial}");
|
|
expect(out).toContain(":is(.beaver-root, .beaver-root *),");
|
|
expect(out).toContain(":is(.beaver-root, .beaver-root *):before");
|
|
expect(out).not.toContain("*)*");
|
|
});
|
|
|
|
it("leaves tailwind's theme variables on :root, under obsidian's body values", () => {
|
|
expect(one(":root,:host{--spacing:.25rem}")).toBe(
|
|
":root, :host { --spacing: .25rem; }"
|
|
);
|
|
});
|
|
|
|
it("scopes the subject, so an ancestor outside the root still matches", () => {
|
|
expect(one(".theme-dark .hue-chip{c:1}")).toBe(
|
|
".theme-dark :is(.beaver-root, .beaver-root *).hue-chip { c: 1; }"
|
|
);
|
|
expect(one(".a > button{c:2}")).toBe(
|
|
".a > button:is(.beaver-root, .beaver-root *) { c: 2; }"
|
|
);
|
|
expect(one(".a *{c:3}")).toBe(
|
|
".a :is(.beaver-root, .beaver-root *) { c: 3; }"
|
|
);
|
|
});
|
|
|
|
it("leaves rules that already name the root alone", () => {
|
|
const reset = ".beaver-root.beaver-root button{cursor:pointer}";
|
|
expect(one(reset)).toBe(
|
|
".beaver-root.beaver-root button { cursor: pointer; }"
|
|
);
|
|
expect(one(".theme-dark .beaver-root{--note:red}")).toBe(
|
|
".theme-dark .beaver-root { --note: red; }"
|
|
);
|
|
});
|
|
|
|
it("scopes variant selectors without breaking their inner :where()", () => {
|
|
const out = one(".dark\\:bg-x:where(.theme-dark,.theme-dark *){b:1}");
|
|
expect(out).toBe(
|
|
":is(.beaver-root, .beaver-root *).dark\\:bg-x:where(.theme-dark, .theme-dark *) { b: 1; }"
|
|
);
|
|
});
|
|
});
|