1498 lines · 54.2 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,  pullRequestStatsBatches,  pullRequestStatsKeysToRequest,  pullRequestStatsRefreshBatches,  pullRequestStatsRequestBatches,  mergePullRequestDiffStats,  narrowPullRequestsToFilters,  partitionPullRequestsWithPriority,  readPullRequestListSnapshot,  writePullRequestListSnapshot,  rankPullRequestMatches,  rankPullRequestsByMergeReadiness,  scorePullRequestMatch,  sortPullRequestGroups,  retainVisiblePullRequestStatsBatches,  withDiffStat,  resolveProjectScope,  resolveQueryEnvironmentIds,  resolveSelectedEnvironmentId,  type EnvironmentPullRequestEntry,} from "./pullRequestList.logic";import {  pullRequestListPreferences,  readPullRequestListPreferences,  writePullRequestListPreferences,} from "./pullRequestListPreferences"; 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("visible pull request line-count targets", () => {  it("reuses counts supplied by the listing and only requests missing counts", () => {    const entries = [      entry({ number: 1, additions: 12, deletions: 0 }),      entry({ number: 2, additions: 0, deletions: 7 }),      entry({ number: 3, additions: 0, deletions: 0 }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const keys = pullRequestStatsKeysToRequest(      entriesByKey,      new Set(entries.map(pullRequestEntryKey)),      [],      new Map(),    );     expect(pullRequestStatsBatches(entriesByKey, keys)[0]?.input.refs).toEqual([      { projectId: "project-1", repository: "pingdotgg/t3code", number: 3 },    ]);  });   it("does not request a row again after its received batch is pruned", () => {    const entries = [      entry({ additions: 0, deletions: 0, number: 1 }),      entry({ additions: 0, deletions: 0, number: 2 }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const firstKey = pullRequestEntryKey(entries[0]!);    const secondKey = pullRequestEntryKey(entries[1]!);    const completedStats = mergePullRequestDiffStats(new Map(), [      {        environmentId: ENV_1,        projectId: "project-1",        number: 1,        additions: 1,        deletions: 1,      },    ]);     const keys = pullRequestStatsKeysToRequest(      entriesByKey,      new Set([firstKey, secondKey]),      [],      completedStats,    );    expect([...keys]).toEqual([secondKey]);    expect(pullRequestStatsBatches(entriesByKey, keys)[0]?.input.refs).toEqual([      { projectId: "project-1", repository: "pingdotgg/t3code", number: 2 },    ]);  });   it("drops historical rows after a long scroll so refresh stays bounded to the viewport", () => {    const entries = Array.from({ length: 500 }, (_, index) =>      entry({ additions: 0, deletions: 0, number: index + 1 }),    );    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const batches = entries.map(      (item) => pullRequestStatsBatches(entriesByKey, new Set([pullRequestEntryKey(item)]))[0]!,    );    const visibleKeys = new Set(entries.slice(-12).map(pullRequestEntryKey));     const retained = retainVisiblePullRequestStatsBatches(batches, visibleKeys);    expect(retained).toHaveLength(12);    expect(retained.flatMap((batch) => batch.input.refs.map((ref) => ref.number))).toEqual(      entries.slice(-12).map((item) => item.number),    );  });   it("keeps every per-environment batch within the stats contract limit", () => {    const entries = Array.from({ length: 501 }, (_, index) =>      entry({ additions: 0, deletions: 0, number: index + 1 }),    );    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const batches = pullRequestStatsBatches(      entriesByKey,      new Set(entries.map(pullRequestEntryKey)),    );     expect(batches.map((batch) => batch.input.refs.length)).toEqual([500, 1]);    expect(batches.flatMap((batch) => [...batch.keys])).toEqual(entries.map(pullRequestEntryKey));  });   it("selects visible rows for date modes and every uncached row for size modes", () => {    const entries = [      entry({ additions: 0, deletions: 0, number: 1 }),      entry({ additions: 0, deletions: 0, number: 2 }),      entry({ additions: 0, deletions: 0, number: 3, environmentId: "env-2" as EnvironmentId }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const [firstKey, secondKey] = entries.map(pullRequestEntryKey);    const cachedStats = mergePullRequestDiffStats(new Map(), [      {        environmentId: ENV_1,        projectId: "project-1",        number: 1,        additions: 1,        deletions: 1,      },    ]);     const visible = pullRequestStatsRequestBatches({      entriesByKey,      candidateKeys: new Set([firstKey!, secondKey!]),      policy: "visible",      activeBatches: [],      statsByRow: cachedStats,    });    expect(visible.flatMap((batch) => batch.input.refs.map((ref) => ref.number))).toEqual([2]);     const eager = pullRequestStatsRequestBatches({      entriesByKey,      candidateKeys: new Set([firstKey!]),      policy: "eager",      activeBatches: [],      statsByRow: cachedStats,    });    expect(eager.map((batch) => batch.environmentId)).toEqual([ENV_1, "env-2"]);    expect(eager.flatMap((batch) => batch.input.refs.map((ref) => ref.number))).toEqual([2, 3]);  });   it("refreshes only visible rows unless size sorting needs every loaded row", () => {    const entries = [      entry({ additions: 0, deletions: 0, number: 1 }),      entry({ additions: 0, deletions: 0, number: 2 }),      entry({ additions: 0, deletions: 0, number: 3 }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const visibleKeys = new Set([pullRequestEntryKey(entries[1]!)]);    const cachedStats = mergePullRequestDiffStats(      new Map(),      entries.map((item) => ({        environmentId: ENV_1,        projectId: item.projectId,        number: item.number,        additions: item.additions,        deletions: item.deletions,      })),    );     const visible = pullRequestStatsRequestBatches({      entriesByKey,      candidateKeys: visibleKeys,      policy: "visible",      activeBatches: [],      statsByRow: cachedStats,      refresh: true,    });    expect(visible[0]?.input.refs.map((ref) => ref.number)).toEqual([2]);     const eager = pullRequestStatsRequestBatches({      entriesByKey,      candidateKeys: visibleKeys,      policy: "eager",      activeBatches: [],      statsByRow: cachedStats,      refresh: true,    });    expect(eager[0]?.input.refs.map((ref) => ref.number)).toEqual([1, 2, 3]);  });   it("ignores a late refresh after the filter or stats policy changes", () => {    const entries = [      entry({ additions: 0, deletions: 0, number: 1 }),      entry({ additions: 0, deletions: 0, number: 2 }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const visibleKeys = new Set([pullRequestEntryKey(entries[1]!)]);    const requestedScope = { key: "open", policy: "visible" } as const;    const refresh = (currentScope: { key: string; policy: "visible" | "eager" }) =>      pullRequestStatsRefreshBatches({        requestedScope,        currentScope,        entriesByKey,        candidateKeys: visibleKeys,        statsByRow: new Map(),      });     expect(refresh(requestedScope)?.[0]?.input.refs.map((ref) => ref.number)).toEqual([2]);    expect(refresh({ key: "closed", policy: "visible" })).toBeNull();    expect(refresh({ key: "open", policy: "eager" })).toBeNull();  });   it("does not add request batches again while rows are active or cached", () => {    const entries = [      entry({ additions: 0, deletions: 0, number: 1 }),      entry({ additions: 0, deletions: 0, number: 2 }),    ];    const entriesByKey = new Map(entries.map((item) => [pullRequestEntryKey(item), item]));    const first = pullRequestStatsRequestBatches({      entriesByKey,      candidateKeys: new Set(),      policy: "eager",      activeBatches: [],      statsByRow: new Map(),    });     expect(      pullRequestStatsRequestBatches({        entriesByKey,        candidateKeys: new Set(),        policy: "eager",        activeBatches: first,        statsByRow: new Map(),      }),    ).toEqual([]);     const cachedStats = mergePullRequestDiffStats(      new Map(),      entries.map((item) => ({        environmentId: ENV_1,        projectId: item.projectId,        number: item.number,        additions: item.additions,        deletions: item.deletions,      })),    );    expect(      pullRequestStatsRequestBatches({        entriesByKey,        candidateKeys: new Set(entries.map(pullRequestEntryKey)),        policy: "visible",        activeBatches: [],        statsByRow: cachedStats,      }),    ).toEqual([]);  });}); 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([      ["authored", 1],      ["reviewRequested", 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);  }); 

Lines 1–500 of 1498.