Monorepo for Tangled
tangled.org
1import { test, describe, beforeAll } from "bun:test";
2import { writeFileSync, mkdirSync, readFileSync } from "fs";
3import { join } from "path";
4import { h, type VNode } from "preact";
5import { renderCard } from "../lib/render";
6import { RepositoryCard } from "../components/cards/repository";
7import { IssueCard } from "../components/cards/issue";
8import { PullRequestCard } from "../components/cards/pull-request";
9import {
10 repositoryCardSchema,
11 issueCardSchema,
12 pullRequestCardSchema,
13} from "../validation";
14import {
15 createRepoData,
16 createIssueData,
17 createPullRequestData,
18 createLongTitleIssueData,
19 createLongTitlePullRequestData,
20} from "./fixtures";
21
22const outputDir = join(process.cwd(), "output");
23let avatarDataUri: string;
24
25const loadAvatar = (): string => {
26 const avatarPath = join(
27 process.cwd(),
28 "src",
29 "__tests__",
30 "assets",
31 "avatar.jpg",
32 );
33 const avatarBase64 = readFileSync(avatarPath).toString("base64");
34 return `data:image/jpeg;base64,${avatarBase64}`;
35};
36
37beforeAll(() => {
38 mkdirSync(outputDir, { recursive: true });
39 avatarDataUri = loadAvatar();
40});
41
42const savePng = (filename: string, buffer: Uint8Array) => {
43 writeFileSync(join(outputDir, filename), buffer);
44};
45
46const saveSvg = (filename: string, svg: string) => {
47 writeFileSync(join(outputDir, filename), svg);
48};
49
50const renderAndSave = async <P>(component: VNode<P>, filename: string) => {
51 const { svg, png } = await renderCard(component as VNode);
52 saveSvg(filename.replace(".png", ".svg"), svg);
53 savePng(filename, png);
54};
55
56describe("repository card", () => {
57 test("renders repository card", async () => {
58 const data = createRepoData(avatarDataUri);
59 const validated = repositoryCardSchema.parse(data);
60 await renderAndSave(h(RepositoryCard, validated), "repository-card.png");
61 });
62});
63
64describe("issue cards", () => {
65 test("renders open issue", async () => {
66 const data = createIssueData(avatarDataUri);
67 const validated = issueCardSchema.parse(data);
68 await renderAndSave(h(IssueCard, validated), "issue-card.png");
69 });
70
71 test("renders closed issue", async () => {
72 const data = createIssueData(avatarDataUri, {
73 issueNumber: 5,
74 status: "closed",
75 labels: [{ name: "wontfix", color: "#6a737d" }],
76 reactionCount: 2,
77 });
78 const validated = issueCardSchema.parse(data);
79 await renderAndSave(h(IssueCard, validated), "issue-card-closed.png");
80 });
81
82 test("renders issue with long title", async () => {
83 const data = createLongTitleIssueData(avatarDataUri, {
84 issueNumber: 42,
85 });
86 const validated = issueCardSchema.parse(data);
87 await renderAndSave(h(IssueCard, validated), "issue-card-long-title.png");
88 });
89
90 test("renders issue with long author handle", async () => {
91 const data = createIssueData(avatarDataUri, {
92 authorHandle: "very.very.long.tangled.org",
93 });
94 const validated = issueCardSchema.parse(data);
95 await renderAndSave(
96 h(IssueCard, validated),
97 "issue-card-long-handle.png",
98 );
99 });
100});
101
102describe("pull request cards", () => {
103 test("renders open pull request", async () => {
104 const data = createPullRequestData(avatarDataUri);
105 const validated = pullRequestCardSchema.parse(data);
106 await renderAndSave(h(PullRequestCard, validated), "pull-request-card.png");
107 });
108
109 test("renders merged pull request", async () => {
110 const data = createPullRequestData(avatarDataUri, {
111 pullRequestNumber: 2,
112 status: "merged",
113 title: "Implement OAuth2 authentication flow",
114 filesChanged: 5,
115 additions: 342,
116 deletions: 28,
117 });
118 const validated = pullRequestCardSchema.parse(data);
119 await renderAndSave(
120 h(PullRequestCard, validated),
121 "pull-request-card-merged.png",
122 );
123 });
124
125 test("renders closed pull request", async () => {
126 const data = createPullRequestData(avatarDataUri, {
127 pullRequestNumber: 3,
128 status: "closed",
129 title: "WIP: Experimental feature",
130 });
131 const validated = pullRequestCardSchema.parse(data);
132 await renderAndSave(
133 h(PullRequestCard, validated),
134 "pull-request-card-closed.png",
135 );
136 });
137
138 test("renders pull request with long title", async () => {
139 const data = createLongTitlePullRequestData(avatarDataUri, {
140 pullRequestNumber: 42,
141 });
142 const validated = pullRequestCardSchema.parse(data);
143 await renderAndSave(
144 h(PullRequestCard, validated),
145 "pull-request-card-long-title.png",
146 );
147 });
148
149 test("renders pull request with long author handle", async () => {
150 const data = createPullRequestData(avatarDataUri, {
151 authorHandle: "very.very.long.tangled.org",
152 });
153 const validated = pullRequestCardSchema.parse(data);
154 await renderAndSave(
155 h(PullRequestCard, validated),
156 "pull-request-card-long-handle.png",
157 );
158 });
159});