Skip to content

Commit 1f7ac8e

Browse files
authored
feat(storybook): msw handlers for endpoint groups (#257)
1 parent 39834e6 commit 1f7ac8e

31 files changed

+37243
-1890
lines changed

.storybook/main.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,11 @@ const config: StorybookConfig = {
2121
getAbsolutePath("@storybook/addon-essentials"),
2222
getAbsolutePath("@chromatic-com/storybook"),
2323
getAbsolutePath("@storybook/addon-interactions"),
24-
getAbsolutePath("@storybook/addon-queryparams"),
2524
],
2625
framework: {
2726
name: getAbsolutePath("@storybook/react-webpack5"),
2827
options: {},
2928
},
30-
docs: {
31-
autodocs: "tag",
32-
},
3329
staticDirs: ["../public"],
3430
typescript: {
3531
reactDocgen: "react-docgen-typescript",
@@ -45,7 +41,7 @@ const config: StorybookConfig = {
4541
config.resolve.plugins = [
4642
...(config.resolve.plugins || []),
4743
new TsconfigPathsPlugin({
48-
configFile: "client/tsconfig.json",
44+
configFile: path.resolve(__dirname, "../client/tsconfig.json"),
4945
extensions: config.resolve.extensions,
5046
}),
5147
];

.storybook/preview.tsx

+17-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,22 @@ import "@patternfly/patternfly/utilities/Spacing/spacing.css";
88
import type { Preview } from "@storybook/react";
99
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
1010
import { initialize, mswLoader } from "msw-storybook-addon";
11-
11+
import { handlers } from "@mocks/handlers";
1212
const queryClient = new QueryClient();
1313

14+
if (typeof jest === "undefined") {
15+
// Mock Jest in the Storybook runtime
16+
globalThis.jest = {
17+
fn: () => {
18+
const mockFn = () => {};
19+
mockFn.mockReturnValue = () => mockFn;
20+
mockFn.mockReturnValueOnce = () => mockFn;
21+
mockFn.mockImplementation = () => mockFn;
22+
return mockFn;
23+
},
24+
} as any;
25+
}
26+
1427
/*
1528
* Initializes MSW
1629
* See https://github.com/mswjs/msw-storybook-addon#configuring-msw
@@ -34,6 +47,9 @@ const preview: Preview = {
3447
date: /Date$/i,
3548
},
3649
},
50+
msw: {
51+
handlers: [...handlers],
52+
},
3753
},
3854
};
3955

client/jest.setup.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@testing-library/jest-dom";

client/package.json

+11
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,16 @@
110110
},
111111
"msw": {
112112
"workerDirectory": "public"
113+
},
114+
"imports": {
115+
"#useSbomsOfVulnerability": {
116+
"storybook": "./src/app/hooks/domain-controls/useSbomsOfVulnerability.mock.ts",
117+
"default": "./src/app/hooks/domain-controls/useSbomsOfVulnerability.ts"
118+
},
119+
"#*": [
120+
"./*",
121+
"./*.ts",
122+
"./*.tsx"
123+
]
113124
}
114125
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { fn } from "@storybook/test";
2+
import * as actual from "./useSbomsOfVulnerability";
3+
4+
export * from "./useSbomsOfVulnerability";
5+
6+
export const useSbomsOfVulnerability = fn(
7+
actual.useSbomsOfVulnerability
8+
).mockName("useSbomsOfVulnerability");

client/src/app/hooks/domain-controls/useSbomsOfVulnerability.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface SbomOfVulnerability {
1616
sbom?: SbomSummary;
1717
}
1818

19-
interface SbomOfVulnerabilitySummary {
19+
export interface SbomOfVulnerabilitySummary {
2020
total: number;
2121
status: { [key in VulnerabilityStatus]: number };
2222
}
@@ -40,7 +40,7 @@ export const useSbomsOfVulnerability = (sbomId: string) => {
4040
const [isFetchingSboms, setIsFetchingSboms] = React.useState(false);
4141

4242
React.useEffect(() => {
43-
if (vulnerability?.advisories.length === 0) {
43+
if (vulnerability?.advisories?.length === 0) {
4444
return;
4545
}
4646

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import React from "react";
2+
3+
import type { Meta, StoryObj } from "@storybook/react";
4+
import { SbomsCount } from "./SbomsCount";
5+
import { sbomList } from "@mocks/data";
6+
import { SbomOfVulnerabilitySummary } from "@app/hooks/domain-controls/useSbomsOfVulnerability";
7+
import { AxiosError } from "axios";
8+
import { http, HttpResponse } from "msw";
9+
import { cveDetails } from "@mocks/handlers";
10+
11+
import { useSbomsOfVulnerability as mockUseSbomsOfVulnerability } from "#useSbomsOfVulnerability.mock";
12+
13+
const useSbomsOfVulnerability = mockUseSbomsOfVulnerability;
14+
15+
type StoryArgs = {
16+
fetchError?: AxiosError<unknown, any>;
17+
isFetching?: boolean;
18+
summary?: SbomOfVulnerabilitySummary;
19+
vulnerabilityId: string;
20+
};
21+
22+
const meta: Meta<typeof SbomsCount> = {
23+
title: "Components/VulnerabilityList/SbomsCount",
24+
component: SbomsCount,
25+
};
26+
27+
export default meta;
28+
29+
type Story = StoryObj<StoryArgs>;
30+
31+
export const DefaultState: Story = {
32+
args: {
33+
isFetching: true,
34+
vulnerabilityId: "CVE-2023-0481",
35+
},
36+
decorators: [
37+
(Story) => {
38+
// useSbomsOfVulnerability.mockReturnValueOnce({
39+
// isFetching: true,
40+
// fetchError: null,
41+
// summary: {
42+
// total: 0,
43+
// status: {
44+
// affected: 0,
45+
// fixed: 0,
46+
// not_affected: 0,
47+
// known_not_affected: 0,
48+
// },
49+
// },
50+
// sboms: [],
51+
// });
52+
53+
// mockUseFetchVulnerabilityById.mockReturnValueOnce({
54+
// vulnerability: cve20230481 as VulnerabilityDetails,
55+
// isFetching: true,
56+
// fetchError: {
57+
// message: "A message here",
58+
// isAxiosError: false,
59+
// toJSON: () => new Object(),
60+
// name: "A name here",
61+
// },
62+
// });
63+
64+
// mockUseSbomsOfVulnerability.mockReturnValueOnce({
65+
// isFetching: true,
66+
// fetchError: {
67+
// message: "A message here",
68+
// isAxiosError: false,
69+
// toJSON: () => new Object(),
70+
// name: "A name here",
71+
// },
72+
// summary: {
73+
// total: 0,
74+
// status: {
75+
// affected: 0,
76+
// fixed: 0,
77+
// not_affected: 0,
78+
// known_not_affected: 0,
79+
// },
80+
// },
81+
// sboms: [],
82+
// });
83+
return <Story />;
84+
},
85+
],
86+
parameters: {
87+
msw: {
88+
handlers: [
89+
// override the handler for vulnerability ID to provide
90+
// custom state
91+
http.get("/api/v1/vulnerability/:id", ({ params }) => {
92+
const { id } = params;
93+
94+
if (!id) {
95+
return new HttpResponse(null, { status: 404 });
96+
}
97+
98+
const data = cveDetails[id as string];
99+
100+
if (!data) {
101+
return new HttpResponse("CVE not found", { status: 404 });
102+
}
103+
104+
return HttpResponse.json({
105+
...data,
106+
advisories: [],
107+
fetchError: {
108+
message: "A message here",
109+
isAxiosError: false,
110+
toJSON: () => new Object(),
111+
name: "A name here",
112+
},
113+
error: false,
114+
});
115+
}),
116+
],
117+
},
118+
},
119+
// async beforeEach() {
120+
// useSbomsOfVulnerability.mockReturnValue({
121+
// isFetching: true,
122+
// fetchError: {
123+
// message: "A message here",
124+
// isAxiosError: false,
125+
// toJSON: () => new Object(),
126+
// name: "A name here",
127+
// },
128+
// summary: {
129+
// total: 0,
130+
// status: {
131+
// affected: 0,
132+
// fixed: 0,
133+
// not_affected: 0,
134+
// known_not_affected: 0,
135+
// },
136+
// },
137+
// sboms: [],
138+
// });
139+
// }
140+
};
141+
142+
export const ErrorState: Story = {
143+
args: {
144+
vulnerabilityId: "CVE-2022-45787",
145+
},
146+
async beforeEach() {
147+
useSbomsOfVulnerability.mockReturnValue({
148+
isFetching: false,
149+
fetchError: {
150+
message: "A message here",
151+
isAxiosError: false,
152+
toJSON: () => new Object(),
153+
name: "A name here",
154+
},
155+
summary: {
156+
total: 5,
157+
status: {
158+
affected: 3,
159+
fixed: 2,
160+
not_affected: 0,
161+
known_not_affected: 0,
162+
},
163+
},
164+
sboms: [],
165+
});
166+
},
167+
parameters: {
168+
isFetching: false,
169+
fetchError: {
170+
isAxiosError: true,
171+
message: "Oh, no. Something's gone wrong.",
172+
name: "Human coder error",
173+
toJSON: (data?: any) => new Object(data),
174+
},
175+
summary: {
176+
total: 5,
177+
status: { fixed: 0, not_affected: 0, known_not_affected: 0, affected: 0 },
178+
},
179+
mockReturnValue: {
180+
"CVE-2022-45787": {
181+
vulnerability: null,
182+
isFetching: false,
183+
fetchError: "Error fetching data",
184+
},
185+
},
186+
},
187+
};
188+
189+
export const PopulatedState: Story = {
190+
args: {
191+
isFetching: false,
192+
vulnerabilityId: "CVE-2023-0044",
193+
},
194+
async beforeEach() {
195+
useSbomsOfVulnerability.mockReturnValue({
196+
isFetching: false,
197+
fetchError: {
198+
message: "A message here",
199+
isAxiosError: false,
200+
toJSON: () => new Object(),
201+
name: "A name here",
202+
},
203+
summary: {
204+
total: 0,
205+
status: {
206+
affected: 0,
207+
fixed: 0,
208+
not_affected: 0,
209+
known_not_affected: 0,
210+
},
211+
},
212+
sboms: [],
213+
});
214+
},
215+
parameters: {
216+
mockReturnValue: sbomList,
217+
},
218+
};

0 commit comments

Comments
 (0)