Newer
Older
Phoomparin Mano
committed
import { act } from "@testing-library/react";
Oisin Coveney
committed
import userEvent from "@testing-library/user-event";
import fetchMock from "fetch-mock";
Oisin Coveney
committed
import {
setupCardEndpoints,
setupCardQueryEndpoints,
setupUnauthorizedCardEndpoints,
} from "__support__/server-mocks";
import {
renderWithProviders,
screen,
waitForLoaderToBeRemoved,
within,
} from "__support__/ui";
import { createMockAuthProviderUriConfig } from "embedding-sdk/test/mocks/config";
import type { Card } from "metabase-types/api";
Oisin Coveney
committed
import {
createMockCard,
createMockColumn,
createMockDataset,
createMockDatasetData,
createMockParameter,
Oisin Coveney
committed
} from "metabase-types/api/mocks";
import type { StaticQuestionProps } from "./";
Oisin Coveney
committed
import { StaticQuestion } from "./";
const TEST_QUESTION_ID = 1;
const TEST_COLUMN = createMockColumn({
display_name: "Test Column",
name: "Test Column",
});
const TEST_DATASET = createMockDataset({
data: createMockDatasetData({
cols: [TEST_COLUMN],
rows: [["Test Row"]],
}),
});
const TEST_PARAM = createMockParameter({
type: "number/=",
slug: "product_id",
target: ["variable", ["template-tag", "product_id"]],
});
Oisin Coveney
committed
const VISUALIZATION_TYPES: Record<
string,
{
container: string;
button: string;
}
> = {
Table: { container: "Table-container", button: "Table-button" },
Number: {
container: "Number-container",
button: "Number-button",
},
Gauge: {
container: "Gauge-container",
button: "Gauge-button",
},
Detail: {
container: "Detail-container",
button: "Detail-button",
},
Progress: {
container: "Progress-container",
button: "Progress-button",
},
};
const setup = ({
showVisualizationSelector = false,
isValidCard = true,
card = createMockCard(),
parameterValues,
}: Partial<StaticQuestionProps> & {
card?: Card;
Oisin Coveney
committed
isValidCard?: boolean;
} = {}) => {
if (isValidCard) {
setupCardEndpoints(card);
Oisin Coveney
committed
} else {
setupUnauthorizedCardEndpoints(card);
Oisin Coveney
committed
}
setupCardQueryEndpoints(card, TEST_DATASET);
Oisin Coveney
committed
Phoomparin Mano
committed
return renderWithProviders(
Oisin Coveney
committed
<StaticQuestion
questionId={TEST_QUESTION_ID}
showVisualizationSelector={showVisualizationSelector}
parameterValues={parameterValues}
Oisin Coveney
committed
/>,
{
mode: "sdk",
sdkProviderProps: {
config: createMockAuthProviderUriConfig({
authProviderUri: "http://TEST_URI/sso/metabase",
Oisin Coveney
committed
},
);
};
describe("StaticQuestion", () => {
it("should render a loader on initialization", () => {
setup();
expect(screen.getByTestId("loading-indicator")).toBeInTheDocument();
Oisin Coveney
committed
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
});
it("should render question if question is valid", async () => {
setup();
await waitForLoaderToBeRemoved();
expect(
within(screen.getByTestId("header-cell")).getByText(TEST_COLUMN.name),
).toBeInTheDocument();
expect(
within(screen.getByRole("gridcell")).getByText("Test Row"),
).toBeInTheDocument();
});
it("should render an error if a question isn't found", async () => {
setup({ isValidCard: false });
await waitForLoaderToBeRemoved();
expect(
screen.getByText("You don't have permissions to do that."),
).toBeInTheDocument();
});
it("should render a visualization selector if showVisualizationSelector is true", async () => {
setup({ showVisualizationSelector: true });
await waitForLoaderToBeRemoved();
expect(screen.getByTestId("chart-type-settings")).toBeInTheDocument();
Oisin Coveney
committed
});
it("should not render a visualization selector if showVisualizationSelector is false", async () => {
setup();
await waitForLoaderToBeRemoved();
expect(screen.queryByTestId("chart-type-settings")).not.toBeInTheDocument();
Oisin Coveney
committed
});
it("should change the visualization if a different visualization is selected", async () => {
setup({ showVisualizationSelector: true });
await waitForLoaderToBeRemoved();
expect(screen.getByTestId("chart-type-settings")).toBeInTheDocument();
Oisin Coveney
committed
for (const visType of Object.keys(VISUALIZATION_TYPES)) {
await userEvent.click(
screen.getByTestId(VISUALIZATION_TYPES[visType].button),
);
expect(
screen.getByTestId(VISUALIZATION_TYPES[visType].container),
).toHaveAttribute("aria-selected", "true");
}
});
it("should query with the parameters in a parameterized question", async () => {
const card = createMockCard({ parameters: [TEST_PARAM] });
setup({ card, parameterValues: { product_id: 1024 } });
await waitForLoaderToBeRemoved();
const lastQuery = fetchMock.lastCall(`path:/api/card/${card.id}/query`);
const queryRequest = await lastQuery?.request?.json();
expect(queryRequest.parameters?.[0]).toMatchObject({
id: TEST_PARAM.id,
type: TEST_PARAM.type,
target: TEST_PARAM.target,
value: 1024,
});
});
Phoomparin Mano
committed
it("should cancel the request when the component unmounts", async () => {
const abortSpy = jest.spyOn(AbortController.prototype, "abort");
const { unmount } = setup();
await act(async () => unmount());
// two requests should've been made initially
Phoomparin Mano
committed
expect(fetchMock.calls(`path:/api/card/1`).length).toBe(1);
expect(fetchMock.calls(`path:/api/card/1/query`).length).toBe(1);
// consequently, two abort calls should've been made for the two requests
expect(abortSpy).toHaveBeenCalledTimes(2);
abortSpy.mockRestore();
Phoomparin Mano
committed
});