Public: anyone can read this; the crew can change it.
A new issue's and pull's length limits live in one module, read by both forms #5605
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/5587-a-new-issue-s-and-pull-s-length-limits-l → main 2 commits fork
4 files
changed +48
−4
src/actions/thread_limits_words.ts added +11
| @@ -0,0 +1,11 @@ | ||
| 1 | /** | |
| 2 | * What a new issue or pull says when its title or description is too long | |
| 3 | * (#5587): one place, read by the issue form and the pull form alike, so the | |
| 4 | * two never drift apart. | |
| 5 | */ | |
| 6 | ||
| 7 | /** A title past MAX_TITLE in src/core/issues.ts. */ | |
| 8 | export const TITLE_TOO_LONG = "That title is too long (256 characters at most)."; | |
| 9 | ||
| 10 | /** A description past the body limit. */ | |
| 11 | export const BODY_TOO_LONG = "That description is too long (64 KB at most)."; |
src/web/pages/issues.ts +3 −2
| @@ -13,6 +13,7 @@ import { | ||
| 13 | 13 | import type { Issue } from "../../core/issues.ts"; |
| 14 | 14 | import { canRead, type Repo } from "../../core/repos.ts"; |
| 15 | 15 | import { privateRepoForSealed } from "../../actions/repos.ts"; |
| 16 | import { BODY_TOO_LONG, TITLE_TOO_LONG } from "../../actions/thread_limits_words.ts"; | |
| 16 | 17 | import { COMMENT_EMPTY, COMMENT_NOT_POSTED } from "../../actions/comment_words.ts"; |
| 17 | 18 | import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts"; |
| 18 | 19 | import type { Ctx } from "../../http/context.ts"; |
| @@ -196,9 +197,9 @@ export async function newIssueSubmit(ctx: Ctx): Promise<Response> { | ||
| 196 | 197 | (created.error === "empty_title" |
| 197 | 198 | ? "An issue needs a title." |
| 198 | 199 | : created.error === "title_too_long" |
| 199 | ? "That title is too long (256 characters at most)." | |
| 200 | ? TITLE_TOO_LONG | |
| 200 | 201 | : created.error === "body_too_long" |
| 201 | ? "That description is too long (64 KB at most)." | |
| 202 | ? BODY_TOO_LONG | |
| 202 | 203 | : "You cannot open an issue here."); |
| 203 | 204 | return composePage(ctx, repo, values, message); |
| 204 | 205 | } |
src/web/pages/pulls.ts +3 −2
| @@ -6,6 +6,7 @@ | ||
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | import { openPull } from "../../actions/pulls.ts"; |
| 9 | import { BODY_TOO_LONG, TITLE_TOO_LONG } from "../../actions/thread_limits_words.ts"; | |
| 9 | 10 | import type { Label } from "../../core/issues.ts"; |
| 10 | 11 | import type { Pull } from "../../core/pulls.ts"; |
| 11 | 12 | import { |
| @@ -215,8 +216,8 @@ export async function newPullPage(ctx: Ctx): Promise<Response> { | ||
| 215 | 216 | |
| 216 | 217 | const COMPOSE_ERRORS: Record<string, string> = { |
| 217 | 218 | empty_title: "A title is required.", |
| 218 | title_too_long: "That title is too long (256 characters at most).", | |
| 219 | body_too_long: "That description is too long (64 KB at most).", | |
| 219 | title_too_long: TITLE_TOO_LONG, | |
| 220 | body_too_long: BODY_TOO_LONG, | |
| 220 | 221 | same_branch: "Base and compare branches must differ.", |
| 221 | 222 | unknown_base: "That base branch does not exist.", |
| 222 | 223 | unknown_head: "That compare branch does not exist.", |
test/thread-limits-words.test.ts added +31
| @@ -0,0 +1,31 @@ | ||
| 1 | /** | |
| 2 | * A new issue's and pull's length limits are said from one module (#5587): | |
| 3 | * the issue form and the pull form import the sentences, and neither types | |
| 4 | * a copy back. | |
| 5 | */ | |
| 6 | ||
| 7 | import { expect, test } from "bun:test"; | |
| 8 | import { readFileSync } from "node:fs"; | |
| 9 | import { join } from "node:path"; | |
| 10 | import * as words from "../src/actions/thread_limits_words.ts"; | |
| 11 | ||
| 12 | const sentences = Object.entries(words) as [string, string][]; | |
| 13 | const readers = ["src/web/pages/issues.ts", "src/web/pages/pulls.ts"]; | |
| 14 | ||
| 15 | test("each sentence is said, and not empty", () => { | |
| 16 | expect(sentences.length).toBe(2); | |
| 17 | for (const [name, sentence] of sentences) { | |
| 18 | expect(typeof sentence, name).toBe("string"); | |
| 19 | expect(sentence.trim().length, name).toBeGreaterThan(0); | |
| 20 | } | |
| 21 | }); | |
| 22 | ||
| 23 | test("the forms import the sentences and type no copy of them", () => { | |
| 24 | for (const file of readers) { | |
| 25 | const source = readFileSync(join(import.meta.dir, "..", file), "utf8"); | |
| 26 | expect(source, file).toContain("actions/thread_limits_words.ts"); | |
| 27 | for (const [, sentence] of sentences) { | |
| 28 | expect(source.includes(sentence), `${file}: ${sentence}`).toBe(false); | |
| 29 | } | |
| 30 | } | |
| 31 | }); |
new above
offline