Skip to content

Commit 2c6f7f0

Browse files
danyelfclaude
andcommitted
test(schema): cover SchemaView inline-profile wiring
Adds the integration coverage the self-review flagged as missing: the 'Profile all columns' button gate (both server flags + model node), the unsupported banner, and — the regression guard for the node-keyed opt-in — that navigating to a different node clears the all-columns opt-in instead of carrying it over. Nodes use empty columns so the grid never renders; the controls sit above it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Danyel Fisher <danyel@gmail.com>
1 parent 0e52eb8 commit 2c6f7f0

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* @file SchemaView.test.tsx
3+
* @description Wiring tests for the schema-diff view's inline-profile controls:
4+
* the "Profile all columns" button gate, the unsupported banner, and the
5+
* node-scoped opt-in reset. The pure scope decision is covered in
6+
* selectInlineProfileScope.test.ts; this pins how SchemaView wires it up.
7+
*
8+
* Nodes are given empty `columns` so the grid (ag-grid) never renders — the
9+
* button and banner sit above it and render regardless, so we can exercise the
10+
* wiring without standing up the full grid.
11+
*/
12+
13+
import CssBaseline from "@mui/material/CssBaseline";
14+
import { ThemeProvider } from "@mui/material/styles";
15+
import { render, screen } from "@testing-library/react";
16+
import userEvent from "@testing-library/user-event";
17+
import { beforeEach, describe, expect, it, vi } from "vitest";
18+
import type { NodeData } from "../../../api";
19+
import { theme } from "../../../theme";
20+
import { SchemaView } from "../SchemaView";
21+
22+
const { flags, distribution, lineageViewContext } = vi.hoisted(() => ({
23+
flags: {
24+
current: { new_cll_experience: true, inline_profile: true } as Record<
25+
string,
26+
boolean
27+
>,
28+
},
29+
// biome-ignore lint/suspicious/noExplicitAny: minimal hook-return stub
30+
distribution: { current: {} as any },
31+
lineageViewContext: {
32+
current: {
33+
impactedColumnIds: new Set<string>(),
34+
wholeModelChangedNodeIds: new Set<string>(),
35+
viewOptions: {},
36+
showColumnLevelLineage: vi.fn(),
37+
} as unknown,
38+
},
39+
}));
40+
41+
vi.mock("../../../contexts", () => ({
42+
useRecceServerFlag: () => ({ data: flags.current }),
43+
useLineageViewContext: () => lineageViewContext.current,
44+
useLineageGraphContext: () => ({
45+
lineageGraph: { catalogMetadata: { base: {}, current: {} } },
46+
isActionAvailable: () => true,
47+
}),
48+
}));
49+
50+
vi.mock("../../../hooks/useInlineProfileDistribution", () => ({
51+
useInlineProfileDistribution: () => distribution.current,
52+
}));
53+
54+
const model = (id: string): NodeData =>
55+
({
56+
id,
57+
name: id.split(".").pop(),
58+
resource_type: "model",
59+
columns: {},
60+
}) as NodeData;
61+
62+
const wrap = (node: NodeData) => (
63+
<ThemeProvider theme={theme}>
64+
<CssBaseline />
65+
<SchemaView base={node} current={node} />
66+
</ThemeProvider>
67+
);
68+
69+
const BUTTON = { name: "Profile all columns" } as const;
70+
71+
beforeEach(() => {
72+
flags.current = { new_cll_experience: true, inline_profile: true };
73+
distribution.current = {
74+
status: "disabled",
75+
columns: {},
76+
baseTotal: 0,
77+
currentTotal: 0,
78+
unsupportedReason: undefined,
79+
error: undefined,
80+
isLoading: false,
81+
};
82+
});
83+
84+
describe("SchemaView inline-profile wiring", () => {
85+
it("shows the 'Profile all columns' button when both flags are on for a model node", () => {
86+
render(wrap(model("model.shop.orders")));
87+
expect(screen.getByRole("button", BUTTON)).toBeInTheDocument();
88+
});
89+
90+
it("hides the button when new_cll_experience is off", () => {
91+
flags.current = { new_cll_experience: false, inline_profile: true };
92+
render(wrap(model("model.shop.orders")));
93+
expect(screen.queryByRole("button", BUTTON)).not.toBeInTheDocument();
94+
});
95+
96+
it("hides the button when inline_profile is off", () => {
97+
flags.current = { new_cll_experience: true, inline_profile: false };
98+
render(wrap(model("model.shop.orders")));
99+
expect(screen.queryByRole("button", BUTTON)).not.toBeInTheDocument();
100+
});
101+
102+
it("renders the unsupported banner when the run is unsupported", () => {
103+
distribution.current = {
104+
...distribution.current,
105+
status: "unsupported",
106+
unsupportedReason: "Adapter 'snowflake' lacks APPROX_PERCENTILE.",
107+
};
108+
render(wrap(model("model.shop.orders")));
109+
expect(
110+
screen.getByTestId("profile-distribution-unsupported-banner"),
111+
).toBeInTheDocument();
112+
});
113+
114+
it("clears the all-columns opt-in when navigating to a different node", async () => {
115+
const user = userEvent.setup();
116+
distribution.current = { ...distribution.current, status: "ok" };
117+
const { rerender } = render(wrap(model("model.shop.orders")));
118+
119+
// Opt in → the button hides (the run now covers every column).
120+
await user.click(screen.getByRole("button", BUTTON));
121+
expect(screen.queryByRole("button", BUTTON)).not.toBeInTheDocument();
122+
123+
// Navigate to a different node → the opt-in must NOT carry over; the
124+
// changed-columns default is restored, so the button returns.
125+
rerender(wrap(model("model.shop.customers")));
126+
expect(screen.getByRole("button", BUTTON)).toBeInTheDocument();
127+
});
128+
});

0 commit comments

Comments
 (0)