1017 lines · 38.6 KB · TypeScript
RawDownload
import type { EnvironmentId, ProjectId, PullRequestListEntry } from "@t3tools/contracts";import { describe, expect, it } from "vite-plus/test"; import {  filterPullRequestsByInvolvement,  findScopedProject,  mergePullRequestLists,  pullRequestEntryKey,  pullRequestEnvironmentSetKey,  groupPullRequestsByInvolvement,  matchesPullRequestFilters,  matchesPullRequestQuery,  parsePullRequestQuery,  mergePullRequestDiffStats,  narrowPullRequestsToFilters,  partitionPullRequestsWithPriority,  readPullRequestListSnapshot,  writePullRequestListSnapshot,  rankPullRequestMatches,  scorePullRequestMatch,  withDiffStat,  resolveProjectScope,  resolveQueryEnvironmentIds,  resolveSelectedEnvironmentId,  type EnvironmentPullRequestEntry,} from "./pullRequestList.logic"; const VIEWERS = { "github.com": "Bilal" } as const;const NO_VIEWERS = {} as const; function entry(  overrides: Partial<EnvironmentPullRequestEntry> & Pick<PullRequestListEntry, "number">,) {  return {    environmentId: "env-1",    provider: "github",    host: "github.com",    projectId: "project-1",    projectTitle: "t3code",    repository: "pingdotgg/t3code",    title: "Add the pull requests page",    url: `https://github.com/pingdotgg/t3code/pull/${overrides.number}`,    author: { login: "octocat", name: null, avatarUrl: null },    headBranch: `feat/branch-${overrides.number}`,    baseBranch: "main",    state: "open",    isDraft: false,    mergeability: "mergeable",    additions: 1,    deletions: 0,    createdAt: "2026-07-01T00:00:00Z",    updatedAt: "2026-07-02T00:00:00Z",    viewerReviewRequested: false,    labels: [],    ...overrides,  } as EnvironmentPullRequestEntry;} describe("pull request involvement filtering", () => {  const entries = [    entry({ number: 1, author: { login: "Bilal", name: null, avatarUrl: null } }),    entry({ number: 2, viewerReviewRequested: true }),    entry({ number: 3 }),  ];   it("matches the viewer's own pull requests case-insensitively", () => {    expect(      filterPullRequestsByInvolvement(entries, VIEWERS, "authored").map((item) => item.number),    ).toEqual([1]);  });   it("returns nothing for Authored when the viewer is unknown", () => {    expect(filterPullRequestsByInvolvement(entries, NO_VIEWERS, "authored")).toEqual([]);  });   it("uses the server-computed review-request flag for Reviewing", () => {    expect(      filterPullRequestsByInvolvement(entries, VIEWERS, "reviewing").map((item) => item.number),    ).toEqual([2]);  });   it("does not treat a matching login on another host as the viewer", () => {    // The same name on GitLab is a different account, and its viewer is unknown here.    const mixed = [      entry({ number: 1, author: { login: "Bilal", name: null, avatarUrl: null } }),      entry({        number: 2,        provider: "gitlab",        host: "gitlab.com",        author: { login: "Bilal", name: null, avatarUrl: null },      }),    ];    expect(      filterPullRequestsByInvolvement(mixed, VIEWERS, "authored").map((item) => item.number),    ).toEqual([1]);  });   it("leaves the superset untouched for All", () => {    expect(filterPullRequestsByInvolvement(entries, VIEWERS, "all")).toHaveLength(3);  });   it("keeps two hosts of one provider kind as two accounts", () => {    // A GitHub Enterprise install is a different account from github.com, so the viewer for    // one must not claim authorship of the other's change requests.    const mixed = [      entry({ number: 1, author: { login: "Bilal", name: null, avatarUrl: null } }),      entry({        number: 2,        host: "github.acme.dev",        author: { login: "Bilal", name: null, avatarUrl: null },      }),    ];    expect(      filterPullRequestsByInvolvement(mixed, VIEWERS, "authored").map((item) => item.number),    ).toEqual([1]);  });}); describe("pull request grouping", () => {  it("buckets authored above review-requested and drops empty groups", () => {    const groups = groupPullRequestsByInvolvement(      [        entry({ number: 1, author: { login: "bilal", name: null, avatarUrl: null } }),        entry({ number: 2, viewerReviewRequested: true }),      ],      VIEWERS,    );    expect(groups.map((group) => [group.key, group.entries.length])).toEqual([      ["reviewRequested", 1],      ["authored", 1],    ]);  });   it("puts everything in Others when the viewer is unknown", () => {    const groups = groupPullRequestsByInvolvement([entry({ number: 1 })], NO_VIEWERS);    expect(groups.map((group) => group.key)).toEqual(["others"]);  });   it("counts an authored pull request once, even with a review request on it", () => {    const groups = groupPullRequestsByInvolvement(      [        entry({          number: 1,          author: { login: "bilal", name: null, avatarUrl: null },          viewerReviewRequested: true,        }),      ],      VIEWERS,    );    expect(groups.map((group) => group.key)).toEqual(["authored"]);  });}); describe("pull request search", () => {  const target = entry({    number: 4711,    title: "Restore sidebar actions",    headBranch: "fix/sidebar",  });   it("matches the number with or without the leading hash", () => {    expect(matchesPullRequestQuery(target, "#4711")).toBe(true);    expect(matchesPullRequestQuery(target, "4711")).toBe(true);  });   it("matches title, branch and author case-insensitively", () => {    expect(matchesPullRequestQuery(target, "SIDEBAR")).toBe(true);    expect(matchesPullRequestQuery(target, "fix/side")).toBe(true);    expect(matchesPullRequestQuery(target, "octocat")).toBe(true);  });   it("ignores surrounding whitespace and rejects non-matches", () => {    expect(matchesPullRequestQuery(target, "   ")).toBe(true);    expect(matchesPullRequestQuery(target, "kanban")).toBe(false);  });}); describe("carrying rows already read into filters nothing has answered yet", () => {  const rows = [    entry({ number: 1 }),    entry({ number: 2, state: "merged" }),    entry({ number: 3, state: "closed" }),    entry({ number: 4, host: "github.acme.dev" }),    entry({ number: 5, projectId: "project-2" as PullRequestListEntry["projectId"] }),  ];  const everything = { state: "all", projectId: undefined, host: undefined } as const;   it("never lets a merged pull request sit under Open", () => {    expect(      narrowPullRequestsToFilters(rows, { ...everything, state: "open" }).map((row) => row.number),    ).toEqual([1, 4, 5]);  });   it("keeps only the state that was asked for", () => {    expect(      narrowPullRequestsToFilters(rows, { ...everything, state: "merged" }).map(        (row) => row.number,      ),    ).toEqual([2]);  });   it("narrows to one host, which two installs of one kind cannot share", () => {    expect(      narrowPullRequestsToFilters(rows, { ...everything, host: "github.acme.dev" }).map(        (row) => row.number,      ),    ).toEqual([4]);  });   it("narrows to one project", () => {    expect(      narrowPullRequestsToFilters(rows, { ...everything, projectId: "project-2" }).map(        (row) => row.number,      ),    ).toEqual([5]);  });   it("holds on to everything when nothing narrows it", () => {    expect(narrowPullRequestsToFilters(rows, everything)).toEqual(rows);  });}); describe("narrowing rows by the filters a host may not have applied", () => {  const rows = [    entry({ number: 1 }),    entry({ number: 2, isDraft: true }),    entry({ number: 3, reviewDecision: "approved" }),    entry({      number: 4,      labels: [{ name: "Needs design", color: null }],      author: { login: "Hubot", name: null, avatarUrl: null },      reviewDecision: "changes-requested",    }),  ];  const narrow = (filters: Parameters<typeof matchesPullRequestFilters>[1]) =>    rows.filter((row) => matchesPullRequestFilters(row, filters)).map((row) => row.number);   it("keeps or drops drafts as asked", () => {    expect(narrow({ draft: "only" })).toEqual([2]);    expect(narrow({ draft: "hide" })).toEqual([1, 3, 4]);  });   it("keeps the review state that was asked for, and no reviews means none at all", () => {    expect(narrow({ review: "approved" })).toEqual([3]);    expect(narrow({ review: "changes-requested" })).toEqual([4]);    expect(narrow({ review: "none" })).toEqual([1, 2]);  });   it("matches labels and authors however either was capitalized", () => {    expect(narrow({ labels: [["needs DESIGN"]] })).toEqual([4]);    expect(narrow({ excludedLabels: ["needs design"] })).toEqual([1, 2, 3]);    expect(narrow({ author: "hubot" })).toEqual([4]);  });   it("resolves author:me to the given viewer, case-insensitively", () => {    const mine = entry({ number: 30, author: { login: "Bilal", name: null, avatarUrl: null } });    const theirs = entry({ number: 31, author: { login: "Hubot", name: null, avatarUrl: null } });    expect(matchesPullRequestFilters(mine, { author: "me" }, "bilal")).toBe(true);    expect(matchesPullRequestFilters(theirs, { author: "me" }, "bilal")).toBe(false);  });   it("resolves author:@me the same way as author:me", () => {    const mine = entry({ number: 32, author: { login: "Bilal", name: null, avatarUrl: null } });    const theirs = entry({ number: 33, author: { login: "Hubot", name: null, avatarUrl: null } });    expect(matchesPullRequestFilters(mine, { author: "@me" }, "Bilal")).toBe(true);    expect(matchesPullRequestFilters(theirs, { author: "@me" }, "Bilal")).toBe(false);  });   it("compares author:me against the literal name when there is no viewer", () => {    const literalMe = entry({ number: 34, author: { login: "me", name: null, avatarUrl: null } });    const someoneElse = entry({      number: 35,      author: { login: "Bilal", name: null, avatarUrl: null },    });    expect(matchesPullRequestFilters(literalMe, { author: "me" })).toBe(true);    expect(matchesPullRequestFilters(someoneElse, { author: "me" })).toBe(false);  });   it("takes a group of labels as an either-or", () => {    const sized = [      entry({ number: 10, labels: [{ name: "size:S", color: null }] }),      entry({ number: 11, labels: [{ name: "size:XS", color: null }] }),      entry({ number: 12, labels: [{ name: "size:L", color: null }] }),    ];    const kept = (labels: ReadonlyArray<ReadonlyArray<string>>) =>      sized.filter((row) => matchesPullRequestFilters(row, { labels })).map((row) => row.number);     expect(kept([["size:S", "size:XS"]])).toEqual([10, 11]);    expect(kept([["size:XXL"]])).toEqual([]);  });   it("takes two groups as an and, each satisfied on its own", () => {    const row = entry({      number: 20,      labels: [        { name: "size:S", color: null },        { name: "bug", color: null },      ],    });    expect(matchesPullRequestFilters(row, { labels: [["size:S", "size:XS"], ["bug"]] })).toBe(true);    // The second group holds nothing this row carries, so the first one satisfied is not enough.    expect(matchesPullRequestFilters(row, { labels: [["size:S"], ["wip"]] })).toBe(false);  });   it("holds on to every row for a filter no row carries the answer to", () => {    expect(narrow({ checks: "passing" })).toEqual([1, 2, 3, 4]);  });}); describe("reading qualifiers out of a typed query", () => {  it("keeps quoted values whole and separates negation", () => {    expect(parsePullRequestQuery('label:"needs design" -label:wip Fix the parser')).toEqual({      text: "Fix the parser",      filters: { labels: [["needs design"]], excludedLabels: ["wip"] },    });  });   it("reads a comma-separated list as one group either name satisfies", () => {    expect(parsePullRequestQuery("label:size:S,size:XS").filters.labels).toEqual([      ["size:S", "size:XS"],    ]);  });   it("keeps the spaces in a quoted name of a list, and drops an empty part", () => {    expect(parsePullRequestQuery('label:"needs design","wip"').filters.labels).toEqual([      ["needs design", "wip"],    ]);    expect(parsePullRequestQuery("label:a,,b").filters.labels).toEqual([["a", "b"]]);  });   it("excludes every name of a negated list", () => {    expect(parsePullRequestQuery("-label:a,b").filters).toEqual({ excludedLabels: ["a", "b"] });  });   it("makes each label qualifier its own group, so the groups are an AND", () => {    expect(parsePullRequestQuery("label:bug label:a,b").filters.labels).toEqual([      ["bug"],      ["a", "b"],    ]);  });   it("reads the scalar qualifiers in GitHub's own spelling", () => {    expect(      parsePullRequestQuery("author:octocat DRAFT:false review:changes_requested status:failure")        .filters,    ).toEqual({      author: "octocat",      draft: "hide",      review: "changes-requested",      checks: "failing",    });  });   it("leaves an unknown value and a stray colon as text, and reads an unknown key as a label", () => {    const parsed = parsePullRequestQuery("milestone:v2 draft:maybe status: parser");    expect(parsed.filters).toEqual({ labels: [["milestone:v2"]] });    // A known key whose value it does not take is text, so "status:" itself stays findable.    expect(parsed.text).toBe("draft:maybe status: parser");  });   it("reads a quoted name holding a comma as the one label it is", () => {    expect(parsePullRequestQuery('label:"needs,triage"').filters.labels).toEqual([      ["needs,triage"],    ]);    expect(parsePullRequestQuery('-label:"needs,triage"').filters.excludedLabels).toEqual([      "needs,triage",    ]);  });   it("cuts a typed query back to what the contract carries", () => {    const many = Array.from({ length: 14 }, (_, index) => `l${index}`).join(",");    const long = "x".repeat(240);    const parsed = parsePullRequestQuery(`label:${many} label:${long} author:${long}`);    expect(parsed.filters.labels?.[0]).toHaveLength(10);    expect(parsed.filters.labels?.[1]).toEqual(["x".repeat(200)]);    expect(parsed.filters.author).toBe("x".repeat(200));  });   it("cuts a namespaced label after its key is prefixed on", () => {    const parsed = parsePullRequestQuery(`size:${"x".repeat(240)}`);    expect(parsed.filters.labels).toEqual([[`size:${"x".repeat(240)}`.slice(0, 200)]]);  });   it("answers an empty query with an empty everything", () => {    expect(parsePullRequestQuery("   ")).toEqual({ text: "", filters: {} });  });}); describe("resolveProjectScope", () => {  const projects = [{ id: "p1" }, { id: "p2" }];   it("keeps an id the environment has", () => {    expect(resolveProjectScope("p2", projects, true)).toBe("p2");  });   it("drops an id from another environment", () => {    expect(resolveProjectScope("p9", projects, false)).toBe("p9");    expect(resolveProjectScope("p9", projects, true)).toBeUndefined();  });   it("drops an id once the environment is known to have no projects", () => {    expect(resolveProjectScope("p9", [], true)).toBeUndefined();  });   it("keeps an id while the projects are still unknown", () => {    // Dropping here would list every project for a moment before narrowing back down.    expect(resolveProjectScope("p9", [], false)).toBe("p9");  });}); describe("ranking what a search found", () => {  const row = (overrides: Partial<PullRequestListEntry> & { number?: number }) =>    entry({ number: 1, ...overrides });   it("puts the pull request whose title says it above one that merely mentions it", () => {    const titled = row({      number: 1,      title: "Add the welcome wizard",      updatedAt: "2026-07-01T00:00:00Z",    });    const mentioned = row({      number: 2,      title: "Unrelated work",      updatedAt: "2026-08-01T00:00:00Z",    });    expect(      rankPullRequestMatches([mentioned, titled], "welcome wizard").map((entry) => entry.number),    ).toEqual([1, 2]);  });   it("finds the words in any order, which is how people type them", () => {    // Substrings count, so "welcomes" answers "welcome" — a search is not a spelling test.    expect(      scorePullRequestMatch(row({ title: "The wizard that welcomes" }), "welcome wizard"),    ).toBe(70);    expect(      scorePullRequestMatch(row({ title: "Wizard for the welcome flow" }), "welcome wizard"),    ).toBe(70);    // One of the two words is a mention, not an answer, and ranks under both.    expect(scorePullRequestMatch(row({ title: "A wizard, of sorts" }), "welcome wizard")).toBe(30);  });   it("answers a number with the pull request that has it, and nothing else", () => {    expect(scorePullRequestMatch(row({ number: 42 }), "#42")).toBe(100);    expect(scorePullRequestMatch(row({ number: 42 }), "43")).toBe(0);  });   it("keeps recency where nothing tells two results apart", () => {    const older = row({ number: 1, title: "no match here", updatedAt: "2026-07-01T00:00:00Z" });    const newer = row({ number: 2, title: "none here either", updatedAt: "2026-08-01T00:00:00Z" });    expect(rankPullRequestMatches([older, newer], "wizard").map((entry) => entry.number)).toEqual([      2, 1,    ]);  });   it("leaves a listing with no search in the order it arrived", () => {    const first = row({ number: 1, updatedAt: "2026-07-01T00:00:00Z" });    const second = row({ number: 2, updatedAt: "2026-08-01T00:00:00Z" });    expect(rankPullRequestMatches([first, second], "  ")).toEqual([first, second]);  });}); describe("line counts that arrive after the rows", () => {  const stats = new Map([["env-1 project-1 7", { additions: 42, deletions: 3 }]]);   it("fills in a row whose host left the counts for later", () => {    const row = entry({ number: 7, additions: 0, deletions: 0 });    expect(withDiffStat(row, stats)).toEqual({ ...row, additions: 42, deletions: 3 });  });   it("leaves a row the host already counted alone", () => {    const row = entry({ number: 7, additions: 1, deletions: 1 });    expect(withDiffStat(row, stats)).toBe(row);  });   it("leaves a row whose counts have not arrived as it is", () => {    const row = entry({ number: 9, additions: 0, deletions: 0 });    expect(withDiffStat(row, stats)).toBe(row);  });}); describe("merging line counts across keyed stats queries", () => {  it("keeps counts already held while a fresh batch says nothing about them", () => {    const held = mergePullRequestDiffStats(new Map(), [      { environmentId: "env-1", projectId: "project-1", number: 1, additions: 10, deletions: 2 },      { environmentId: "env-1", projectId: "project-1", number: 2, additions: 5, deletions: 1 },    ]);    // A third row appeared; its batch is still pending and contributes nothing yet.    const merged = mergePullRequestDiffStats(held, []);    expect(merged.get("env-1 project-1 1")).toEqual({ additions: 10, deletions: 2 });    expect(merged.get("env-1 project-1 2")).toEqual({ additions: 5, deletions: 1 });  });   it("replaces a count once its replacement arrives, keeping its neighbours", () => {    const held = mergePullRequestDiffStats(new Map(), [      { environmentId: "env-1", projectId: "project-1", number: 1, additions: 10, deletions: 2 },    ]);    const merged = mergePullRequestDiffStats(held, [      { environmentId: "env-1", projectId: "project-1", number: 1, additions: 11, deletions: 2 },      { environmentId: "env-1", projectId: "project-1", number: 3, additions: 7, deletions: 0 },

Lines 1–500 of 1017.