Skip to content

Commit dd67c5e

Browse files
committed
fix: excludes Dep Dashboard from summary, adds missing tests
1 parent 1da5632 commit dd67c5e

File tree

5 files changed

+186
-4
lines changed

5 files changed

+186
-4
lines changed

src/app/components/dashboard/PersonalSummaryStrip.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ export default function PersonalSummaryStrip(props: PersonalSummaryStripProps) {
2525
return ids;
2626
});
2727

28-
// Single-pass over issues to count assigned (excludes ignored)
28+
// Single-pass over issues to count assigned (excludes ignored + Dep Dashboard)
2929
const issueCounts = createMemo(() => {
3030
const login = props.userLogin.toLowerCase();
3131
if (!login) return { assignedIssues: 0 };
3232
const ignored = ignoredIds();
3333
let assignedIssues = 0;
3434
for (const i of props.issues) {
3535
if (ignored.has(String(i.id))) continue;
36+
if (viewState.hideDepDashboard && i.title === "Dependency Dashboard") continue;
3637
if (i.assigneeLogins.some((a) => a.toLowerCase() === login)) assignedIssues++;
3738
}
3839
return { assignedIssues };

tests/components/dashboard/IssuesTab.test.tsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ vi.mock("../../../src/app/lib/url", () => ({
2929
// ── Imports ───────────────────────────────────────────────────────────────────
3030

3131
import IssuesTab from "../../../src/app/components/dashboard/IssuesTab";
32-
import { setTabFilter, setAllExpanded, resetViewState } from "../../../src/app/stores/view";
32+
import { viewState, setTabFilter, setAllExpanded, resetViewState } from "../../../src/app/stores/view";
3333
import type { TrackedUser } from "../../../src/app/stores/config";
3434

3535
// ── Setup ─────────────────────────────────────────────────────────────────────
@@ -545,3 +545,51 @@ describe("IssuesTab — scope filter with undefined surfacedBy (non-monitored re
545545
screen.getByText("Legacy issue");
546546
});
547547
});
548+
549+
// ── IssuesTab — scope chip visibility ──────────────────────────────────────
550+
551+
describe("IssuesTab — scope chip visibility", () => {
552+
it("does not show Scope chip when no monitored repos and no tracked users", () => {
553+
const issues = [makeIssue({ id: 1, title: "Issue", repoFullName: "org/repo", surfacedBy: ["me"] })];
554+
555+
const { container } = render(() => (
556+
<IssuesTab issues={issues} userLogin="me" monitoredRepos={[]} />
557+
));
558+
559+
expect(container.textContent).not.toContain("Scope:");
560+
});
561+
562+
it("shows Scope chip when monitored repos exist", () => {
563+
const issues = [makeIssue({ id: 1, title: "Issue", repoFullName: "org/repo", surfacedBy: ["me"] })];
564+
565+
const { container } = render(() => (
566+
<IssuesTab issues={issues} userLogin="me" monitoredRepos={[{ owner: "org", name: "mon", fullName: "org/mon" }]} />
567+
));
568+
569+
expect(container.textContent).toContain("Scope:");
570+
});
571+
572+
it("shows Scope chip when tracked users exist (allUsers > 1)", () => {
573+
const issues = [makeIssue({ id: 1, title: "Issue", repoFullName: "org/repo", surfacedBy: ["me"] })];
574+
575+
const { container } = render(() => (
576+
<IssuesTab issues={issues} userLogin="me" monitoredRepos={[]}
577+
allUsers={[{ login: "me", label: "Me" }, { login: "other", label: "other" }]}
578+
/>
579+
));
580+
581+
expect(container.textContent).toContain("Scope:");
582+
});
583+
584+
it("auto-resets scope to involves_me when scope chip becomes hidden", () => {
585+
setTabFilter("issues", "scope", "all");
586+
expect(viewState.tabFilters.issues.scope).toBe("all");
587+
588+
// Render with no monitored repos and no tracked users — scope chip hidden, effect should reset
589+
render(() => (
590+
<IssuesTab issues={[]} userLogin="me" monitoredRepos={[]} />
591+
));
592+
593+
expect(viewState.tabFilters.issues.scope).toBe("involves_me");
594+
});
595+
});

tests/components/dashboard/PersonalSummaryStrip.test.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import PersonalSummaryStrip from "../../../src/app/components/dashboard/Personal
55
import IssuesTab from "../../../src/app/components/dashboard/IssuesTab";
66
import PullRequestsTab from "../../../src/app/components/dashboard/PullRequestsTab";
77
import type { Issue, PullRequest, WorkflowRun } from "../../../src/app/services/api";
8-
import { viewState, setAllExpanded, ignoreItem } from "../../../src/app/stores/view";
8+
import { viewState, updateViewState, setAllExpanded, ignoreItem } from "../../../src/app/stores/view";
99

1010
// ── Setup ─────────────────────────────────────────────────────────────────────
1111

@@ -699,3 +699,25 @@ describe("PersonalSummaryStrip — excludes ignored items", () => {
699699
expect(blockedButton.textContent).toContain("1");
700700
});
701701
});
702+
703+
describe("PersonalSummaryStrip — hideDepDashboard exclusion", () => {
704+
it("excludes Dependency Dashboard issues from assigned count when hideDepDashboard is true", () => {
705+
const issues = [
706+
makeIssue({ id: 1, title: "Dependency Dashboard", assigneeLogins: ["me"] }),
707+
];
708+
// hideDepDashboard defaults to true via resetViewStore
709+
710+
const { container } = renderStrip({ issues });
711+
expect(container.innerHTML).toBe("");
712+
});
713+
714+
it("includes Dependency Dashboard issues when hideDepDashboard is false", () => {
715+
updateViewState({ hideDepDashboard: false });
716+
const issues = [
717+
makeIssue({ id: 1, title: "Dependency Dashboard", assigneeLogins: ["me"] }),
718+
];
719+
720+
renderStrip({ issues });
721+
screen.getByText(/assigned/);
722+
});
723+
});

tests/components/dashboard/PullRequestsTab.test.tsx

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ vi.mock("../../../src/app/lib/url", () => ({
2929
// ── Imports ───────────────────────────────────────────────────────────────────
3030

3131
import PullRequestsTab from "../../../src/app/components/dashboard/PullRequestsTab";
32-
import { setTabFilter, setAllExpanded, resetViewState } from "../../../src/app/stores/view";
32+
import { viewState, setTabFilter, setAllExpanded, resetViewState } from "../../../src/app/stores/view";
3333
import type { TrackedUser } from "../../../src/app/stores/config";
3434

3535
// ── Setup ─────────────────────────────────────────────────────────────────────
@@ -473,3 +473,62 @@ describe("PullRequestsTab — star count in repo headers", () => {
473473
expect(container.textContent).not.toContain("★");
474474
});
475475
});
476+
477+
// ── PullRequestsTab — scope chip visibility ────────────────────────────────
478+
479+
describe("PullRequestsTab — scope chip visibility", () => {
480+
it("does not show Scope chip when no monitored repos and no tracked users", () => {
481+
const prs = [makePullRequest({ id: 1, title: "PR", repoFullName: "org/repo", surfacedBy: ["me"] })];
482+
483+
const { container } = render(() => (
484+
<PullRequestsTab pullRequests={prs} userLogin="me" monitoredRepos={[]} />
485+
));
486+
487+
expect(container.textContent).not.toContain("Scope:");
488+
});
489+
490+
it("shows Scope chip when monitored repos exist", () => {
491+
const prs = [makePullRequest({ id: 1, title: "PR", repoFullName: "org/repo", surfacedBy: ["me"] })];
492+
493+
const { container } = render(() => (
494+
<PullRequestsTab pullRequests={prs} userLogin="me"
495+
monitoredRepos={[{ owner: "org", name: "mon", fullName: "org/mon" }]}
496+
/>
497+
));
498+
499+
expect(container.textContent).toContain("Scope:");
500+
});
501+
502+
it("auto-resets scope to involves_me when scope chip becomes hidden", () => {
503+
setTabFilter("pullRequests", "scope", "all");
504+
expect(viewState.tabFilters.pullRequests.scope).toBe("all");
505+
506+
render(() => (
507+
<PullRequestsTab pullRequests={[]} userLogin="me" monitoredRepos={[]} />
508+
));
509+
510+
expect(viewState.tabFilters.pullRequests.scope).toBe("involves_me");
511+
});
512+
});
513+
514+
// ── PullRequestsTab — blocked composite filter ────────────────────────────
515+
516+
describe("PullRequestsTab — checkStatus=blocked filter", () => {
517+
it("shows both failure and conflict PRs when checkStatus=blocked", () => {
518+
const prs = [
519+
makePullRequest({ id: 1, title: "Failing PR", repoFullName: "org/repo", checkStatus: "failure", surfacedBy: ["me"], enriched: true }),
520+
makePullRequest({ id: 2, title: "Conflict PR", repoFullName: "org/repo", checkStatus: "conflict", surfacedBy: ["me"], enriched: true }),
521+
makePullRequest({ id: 3, title: "Passing PR", repoFullName: "org/repo", checkStatus: "success", surfacedBy: ["me"], enriched: true }),
522+
];
523+
setTabFilter("pullRequests", "checkStatus", "blocked");
524+
setAllExpanded("pullRequests", ["org/repo"], true);
525+
526+
render(() => (
527+
<PullRequestsTab pullRequests={prs} userLogin="me" monitoredRepos={[]} />
528+
));
529+
530+
screen.getByText("Failing PR");
531+
screen.getByText("Conflict PR");
532+
expect(screen.queryByText("Passing PR")).toBeNull();
533+
});
534+
});

tests/lib/reorderHighlight.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,56 @@ describe("createReorderHighlight", () => {
210210
disposeRoot();
211211
vi.useRealTimers();
212212
});
213+
214+
it("suppresses highlight when filter key changes simultaneously with reorder", () => {
215+
let highlighted!: Accessor<ReadonlySet<string>>;
216+
let setOrder!: (v: string[]) => void;
217+
let setFilterKey!: (v: string) => void;
218+
let disposeRoot!: () => void;
219+
220+
createRoot((dispose) => {
221+
const [order, _setOrder] = createSignal<string[]>(["a", "b", "c"]);
222+
const [locked] = createSignal<string[]>([]);
223+
const [ignored] = createSignal(0);
224+
const [filterKey, _setFilterKey] = createSignal("initial");
225+
setOrder = _setOrder;
226+
setFilterKey = _setFilterKey;
227+
highlighted = createReorderHighlight(order, locked, ignored, filterKey);
228+
disposeRoot = dispose;
229+
});
230+
231+
// Reorder AND filter change in single batch — should suppress
232+
batch(() => {
233+
setFilterKey("changed");
234+
setOrder(["c", "a", "b"]);
235+
});
236+
expect(highlighted().size).toBe(0);
237+
238+
// Next reorder without filter change — should highlight
239+
setOrder(["b", "c", "a"]);
240+
expect(highlighted().size).toBeGreaterThan(0);
241+
242+
disposeRoot();
243+
});
244+
245+
it("highlights normally when getFilterKey is not provided", () => {
246+
let highlighted!: Accessor<ReadonlySet<string>>;
247+
let setOrder!: (v: string[]) => void;
248+
let disposeRoot!: () => void;
249+
250+
createRoot((dispose) => {
251+
const [order, _setOrder] = createSignal<string[]>(["a", "b", "c"]);
252+
const [locked] = createSignal<string[]>([]);
253+
const [ignored] = createSignal(0);
254+
setOrder = _setOrder;
255+
// No getFilterKey argument — optional parameter
256+
highlighted = createReorderHighlight(order, locked, ignored);
257+
disposeRoot = dispose;
258+
});
259+
260+
setOrder(["c", "a", "b"]);
261+
expect(highlighted().size).toBeGreaterThan(0);
262+
263+
disposeRoot();
264+
});
213265
});

0 commit comments

Comments
 (0)