Add cross-browser popup status handling
This commit is contained in:
parent
9c65a61ebb
commit
7e17470356
38 changed files with 1242 additions and 199 deletions
62
tests/definition-engine.test.ts
Normal file
62
tests/definition-engine.test.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { DefinitionEngine, matchesUrlPattern, selectDefinition } from "../src/content/definition-engine";
|
||||
import { BUILTIN_DEFINITIONS } from "../src/content/definitions";
|
||||
import type { SiteDefinition } from "../src/shared/types";
|
||||
|
||||
const definition: SiteDefinition = {
|
||||
id: "example-social",
|
||||
name: "Example Social",
|
||||
urlPatterns: ["https://*.example.test/*"],
|
||||
requiredSelectors: [".feed"],
|
||||
post: { rootSelectors: ["article.post"], textSelectors: [".content"], excludedSelectors: [".exclude"], idAttributes: ["data-id"], permalinkSelectors: ["a.permalink[href]"] }
|
||||
};
|
||||
|
||||
describe("DefinitionEngine", () => {
|
||||
it("discovers the supplied root, extracts only visible text, and uses a stable ID", () => {
|
||||
document.body.innerHTML = '<article class="post" data-id="42"><div class="content">Visible <span class="exclude">skip</span><span style="display:none">hidden</span></div></article>';
|
||||
const post = new DefinitionEngine(definition).discover(document.querySelector("article")!)[0];
|
||||
expect(post).toMatchObject({ id: "example-social:42", text: "visible" });
|
||||
});
|
||||
|
||||
it("uses permalink IDs and selects only definitions with URL and marker matches", () => {
|
||||
document.body.innerHTML = '<div class="feed"><article class="post"><div class="content">Hello</div><a class="permalink" href="/users/a/statuses/1">link</a></article></div>';
|
||||
const engine = new DefinitionEngine(definition);
|
||||
expect(engine.discover(document)[0]?.id).toContain("/users/a/statuses/1");
|
||||
expect(selectDefinition([definition], "https://social.example.test/home")).toBe(definition);
|
||||
expect(selectDefinition([definition], "https://other.test/home")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("matches browser-style URL patterns", () => {
|
||||
expect(matchesUrlPattern("https://*.example.test/*", "https://a.example.test/path?q=1")).toBe(true);
|
||||
expect(matchesUrlPattern("https://*.example.test/*", "http://a.example.test/path")).toBe(false);
|
||||
});
|
||||
|
||||
it("covers Mastodon timeline, profile, and detailed reply status containers", () => {
|
||||
document.body.innerHTML = `
|
||||
<div class="status" data-id="timeline"><div class="status__content">Timeline post</div></div>
|
||||
<div class="status" data-id="profile"><div class="status__content">Profile post</div></div>
|
||||
<div class="detailed-status"><a class="detailed-status__datetime" href="/users/a/statuses/3"></a><div class="status__content">Reply in thread</div></div>`;
|
||||
const posts = new DefinitionEngine(BUILTIN_DEFINITIONS[0]!).discover(document);
|
||||
expect(posts.map((post) => post.id)).toEqual(["mastodon:timeline", "mastodon:profile", "mastodon:http://localhost:3000/users/a/statuses/3"]);
|
||||
expect(posts.map((post) => post.text)).toEqual(["timeline post", "profile post", "reply in thread"]);
|
||||
});
|
||||
|
||||
it("does not include hidden Mastodon content-warning text until it is revealed", () => {
|
||||
document.body.innerHTML = '<div class="status" data-id="cw"><div class="status__content"><p>Content warning <a class="status__content__spoiler-link">Show more</a></p><div id="hidden" style="display:none">Hidden post text</div></div></div>';
|
||||
const engine = new DefinitionEngine(BUILTIN_DEFINITIONS[0]!);
|
||||
expect(engine.discover(document)[0]?.text).toBe("content warning");
|
||||
document.querySelector("#hidden")?.removeAttribute("style");
|
||||
expect(engine.discover(document)[0]?.text).toBe("content warning hidden post text");
|
||||
});
|
||||
|
||||
it("rediscovers a Mastodon status when a content warning is revealed", async () => {
|
||||
document.body.innerHTML = '<div class="status" data-id="cw"><div class="status__content"><p>Content warning</p><div id="hidden" style="display:none">Revealed text</div></div></div>';
|
||||
const engine = new DefinitionEngine(BUILTIN_DEFINITIONS[0]!);
|
||||
const observed: string[] = [];
|
||||
const stop = engine.observe((posts) => observed.push(...posts.map((post) => post.text)));
|
||||
document.querySelector("#hidden")?.removeAttribute("style");
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
stop();
|
||||
expect(observed).toContain("content warning revealed text");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue