feat(ui): capture Lighthouse context on tenant admin pages (#12285)

This commit is contained in:
Alejandro Bailo
2026-08-04 13:40:34 +02:00
committed by GitHub
parent f0d2972969
commit ab13d111c2
6 changed files with 310 additions and 16 deletions
@@ -67,6 +67,88 @@ describe("LighthouseCurrentContextBadge", () => {
expect(tooltip).not.toHaveTextContent("status-summary");
});
it("should say when only the page name is shared", async () => {
// Given an unregistered route contributes a bare, filterless page item
const user = userEvent.setup();
render(<LighthouseCurrentContextBadge context={barePageContext()} />);
// When
await user.hover(screen.getByLabelText("Manage Groups context"));
// Then
const tooltip = await screen.findByRole("tooltip");
expect(tooltip).toHaveTextContent("Manage Groups");
expect(tooltip).toHaveTextContent("Only the current page name is shared.");
});
it("should treat empty filter entries as a bare page", async () => {
// Given a stored envelope whose page filters only carry empty values
const user = userEvent.setup();
const bareContext = barePageContext();
const [pageItem] = bareContext.items;
if (pageItem.kind !== "page") throw new Error("expected page item");
render(
<LighthouseCurrentContextBadge
context={{
...bareContext,
items: [{ ...pageItem, filters: { status: [] } }],
}}
/>,
);
// When
await user.hover(screen.getByLabelText("Manage Groups context"));
// Then no filters line renders and the page-only notice does
const tooltip = await screen.findByRole("tooltip");
expect(tooltip).not.toHaveTextContent("Filters:");
expect(tooltip).toHaveTextContent("Only the current page name is shared.");
});
it("should not claim a bare page for a single non-page item", async () => {
// Given a historical envelope whose only item is not a page item
const user = userEvent.setup();
const context: LighthouseContextEnvelope = {
schemaVersion: 1,
transport: "inline",
items: [
{
kind: "scan",
id: "scan-1",
source: "selection",
scopeKey: "scans:/scans",
label: "Selected scan",
scanId: "scan-1",
},
],
};
render(<LighthouseCurrentContextBadge context={context} />);
// When
await user.hover(screen.getByLabelText("Context context"));
// Then
const tooltip = await screen.findByRole("tooltip");
expect(tooltip).not.toHaveTextContent(
"Only the current page name is shared.",
);
});
it("should not claim a bare page when filters or items travel too", async () => {
// Given
const user = userEvent.setup();
render(<LighthouseCurrentContextBadge context={findingsContext()} />);
// When
await user.hover(screen.getByLabelText("Findings context"));
// Then
const tooltip = await screen.findByRole("tooltip");
expect(tooltip).not.toHaveTextContent(
"Only the current page name is shared.",
);
});
it.each([
["resource", resourceContext(), "Resource: resource-1 (bucket-1)"],
["scan", scanContext(), "Scan: scan-1"],
@@ -98,6 +180,23 @@ describe("LighthouseContextBadge", () => {
});
});
function barePageContext(): LighthouseContextEnvelope {
return {
schemaVersion: 1,
transport: "inline",
items: [
{
kind: "page",
id: "other",
source: "automatic",
scopeKey: "other:/manage-groups",
label: "Manage Groups",
path: "/manage-groups",
},
],
};
}
function overviewContext(): LighthouseContextEnvelope {
return {
schemaVersion: 1,
+17 -5
View File
@@ -85,15 +85,26 @@ function LighthouseContextTooltip({ context }: LighthouseContextBadgeProps) {
const page = context.items.find(
(item) => item.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE,
);
const filters =
// Entries with no values (possible in stored envelopes) carry nothing, so
// they count neither for the filters line nor against the page-only notice.
const filterEntries =
page?.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE
? Object.entries(page.filters ?? {})
.map(([key, values]) => `${key}: ${values.join(", ")}`)
.join("; ")
: "";
? Object.entries(page.filters ?? {}).filter(
([, values]) => values.length > 0,
)
: [];
const filters = filterEntries
.map(([key, values]) => `${key}: ${values.join(", ")}`)
.join("; ");
const itemDescriptions = context.items
.map(getContextItemDescription)
.filter((description) => description !== null);
// A single filterless page item means the model only learns which page the
// user is on — say so instead of implying richer context travels with it.
const sharesOnlyPage =
page?.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE &&
context.items.length === 1 &&
filterEntries.length === 0;
return (
<TooltipContent maxWidth="md">
@@ -104,6 +115,7 @@ function LighthouseContextTooltip({ context }: LighthouseContextBadgeProps) {
</p>
)}
{filters && <p>Filters: {filters}</p>}
{sharesOnlyPage && <p>Only the current page name is shared.</p>}
{itemDescriptions.map(({ id, text }) => (
<p key={id}>{text}</p>
))}