Skip to content

Commit d3a9426

Browse files
committed
Add story and tests for LibraryRow
1 parent 9175449 commit d3a9426

5 files changed

Lines changed: 361 additions & 3 deletions

File tree

extensions/ql-vscode/src/model-editor/shared/in-progress-methods.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* A class that keeps track of which methods are in progress for each package.
33
*
4-
* This class is immutable and therefore is safe to be used in a react useState hook.
4+
* This class is immutable and therefore is safe to be used in a React useState hook.
55
*/
66
export class InProgressMethods {
77
// A map of in-progress method signatures for each package.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import * as React from "react";
2+
3+
import { Meta, StoryFn } from "@storybook/react";
4+
5+
import { Mode } from "../../model-editor/shared/mode";
6+
import { LibraryRow as LibraryRowComponent } from "../../view/model-editor/LibraryRow";
7+
import { CallClassification } from "../../model-editor/method";
8+
import { InProgressMethods } from "../../model-editor/shared/in-progress-methods";
9+
import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack";
10+
11+
export default {
12+
title: "CodeQL Model Editor/Library Row",
13+
component: LibraryRowComponent,
14+
} as Meta<typeof LibraryRowComponent>;
15+
16+
const Template: StoryFn<typeof LibraryRowComponent> = (args) => (
17+
<LibraryRowComponent {...args} />
18+
);
19+
20+
export const LibraryRow = Template.bind({});
21+
LibraryRow.args = {
22+
title: "sql2o",
23+
libraryVersion: "1.6.0",
24+
methods: [
25+
{
26+
library: "sql2o",
27+
libraryVersion: "1.6.0",
28+
signature: "org.sql2o.Connection#createQuery(String)",
29+
packageName: "org.sql2o",
30+
typeName: "Connection",
31+
methodName: "createQuery",
32+
methodParameters: "(String)",
33+
supported: true,
34+
supportedType: "summary",
35+
usages: Array(10).fill({
36+
label: "createQuery(...)",
37+
url: {
38+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
39+
startLine: 15,
40+
startColumn: 13,
41+
endLine: 15,
42+
endColumn: 56,
43+
},
44+
classification: CallClassification.Source,
45+
}),
46+
},
47+
{
48+
library: "sql2o",
49+
libraryVersion: "1.6.0",
50+
signature: "org.sql2o.Query#executeScalar(Class)",
51+
packageName: "org.sql2o",
52+
typeName: "Query",
53+
methodName: "executeScalar",
54+
methodParameters: "(Class)",
55+
supported: true,
56+
supportedType: "neutral",
57+
usages: Array(2).fill({
58+
label: "executeScalar(...)",
59+
url: {
60+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
61+
startLine: 15,
62+
startColumn: 13,
63+
endLine: 15,
64+
endColumn: 85,
65+
},
66+
classification: CallClassification.Source,
67+
}),
68+
},
69+
{
70+
library: "sql2o",
71+
libraryVersion: "1.6.0",
72+
signature: "org.sql2o.Sql2o#open()",
73+
packageName: "org.sql2o",
74+
typeName: "Sql2o",
75+
methodName: "open",
76+
methodParameters: "()",
77+
supported: false,
78+
supportedType: "none",
79+
usages: Array(28).fill({
80+
label: "open(...)",
81+
url: {
82+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
83+
startLine: 14,
84+
startColumn: 24,
85+
endLine: 14,
86+
endColumn: 35,
87+
},
88+
classification: CallClassification.Source,
89+
}),
90+
},
91+
{
92+
library: "sql2o",
93+
libraryVersion: "1.6.0",
94+
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
95+
packageName: "org.sql2o",
96+
typeName: "Sql2o",
97+
methodName: "Sql2o",
98+
methodParameters: "(String,String,String)",
99+
supported: false,
100+
supportedType: "none",
101+
usages: Array(106).fill({
102+
label: "new Sql2o(...)",
103+
url: {
104+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
105+
startLine: 10,
106+
startColumn: 33,
107+
endLine: 10,
108+
endColumn: 88,
109+
classification: CallClassification.Test,
110+
},
111+
}),
112+
},
113+
{
114+
library: "sql2o",
115+
libraryVersion: "1.6.0",
116+
signature: "org.sql2o.Sql2o#Sql2o(String)",
117+
packageName: "org.sql2o",
118+
typeName: "Sql2o",
119+
methodName: "Sql2o",
120+
methodParameters: "(String)",
121+
supported: false,
122+
supportedType: "none",
123+
usages: [
124+
...Array(4).fill({
125+
label: "new Sql2o(...)",
126+
url: {
127+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
128+
startLine: 23,
129+
startColumn: 23,
130+
endLine: 23,
131+
endColumn: 36,
132+
},
133+
classification: CallClassification.Test,
134+
}),
135+
{
136+
label: "new Sql2o(...)",
137+
url: {
138+
uri: "file:/home/runner/work/sql2o-example/sql2o-example/build/generated/java/org/example/HelloControllerGenerated.java",
139+
startLine: 23,
140+
startColumn: 23,
141+
endLine: 23,
142+
endColumn: 36,
143+
},
144+
classification: CallClassification.Generated,
145+
},
146+
],
147+
},
148+
],
149+
modeledMethods: {
150+
"org.sql2o.Sql2o#Sql2o(String)": {
151+
type: "sink",
152+
input: "Argument[0]",
153+
output: "",
154+
kind: "jndi-injection",
155+
provenance: "df-generated",
156+
signature: "org.sql2o.Sql2o#Sql2o(String)",
157+
packageName: "org.sql2o",
158+
typeName: "Sql2o",
159+
methodName: "Sql2o",
160+
methodParameters: "(String)",
161+
},
162+
"org.sql2o.Connection#createQuery(String)": {
163+
type: "summary",
164+
input: "Argument[this]",
165+
output: "ReturnValue",
166+
kind: "taint",
167+
provenance: "df-manual",
168+
signature: "org.sql2o.Connection#createQuery(String)",
169+
packageName: "org.sql2o",
170+
typeName: "Connection",
171+
methodName: "createQuery",
172+
methodParameters: "(String)",
173+
},
174+
"org.sql2o.Sql2o#open()": {
175+
type: "summary",
176+
input: "Argument[this]",
177+
output: "ReturnValue",
178+
kind: "taint",
179+
provenance: "manual",
180+
signature: "org.sql2o.Sql2o#open()",
181+
packageName: "org.sql2o",
182+
typeName: "Sql2o",
183+
methodName: "open",
184+
methodParameters: "()",
185+
},
186+
"org.sql2o.Query#executeScalar(Class)": {
187+
type: "neutral",
188+
input: "",
189+
output: "",
190+
kind: "",
191+
provenance: "df-generated",
192+
signature: "org.sql2o.Query#executeScalar(Class)",
193+
packageName: "org.sql2o",
194+
typeName: "Query",
195+
methodName: "executeScalar",
196+
methodParameters: "(Class)",
197+
},
198+
"org.sql2o.Sql2o#Sql2o(String,String,String)": {
199+
type: "neutral",
200+
input: "",
201+
output: "",
202+
kind: "",
203+
provenance: "df-generated",
204+
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
205+
packageName: "org.sql2o",
206+
typeName: "Sql2o",
207+
methodName: "Sql2o",
208+
methodParameters: "(String,String,String)",
209+
},
210+
},
211+
modifiedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]),
212+
inProgressMethods: new InProgressMethods(),
213+
viewState: {
214+
extensionPack: createMockExtensionPack(),
215+
showFlowGeneration: true,
216+
showLlmButton: true,
217+
mode: Mode.Application,
218+
},
219+
hideModeledMethods: false,
220+
};

extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const ButtonsContainer = styled.div`
6767
margin-right: 1rem;
6868
`;
6969

70-
type Props = {
70+
export type LibraryRowProps = {
7171
title: string;
7272
libraryVersion?: string;
7373
methods: Method[];
@@ -110,7 +110,7 @@ export const LibraryRow = ({
110110
onStopGenerateFromLlmClick,
111111
onGenerateFromSourceClick,
112112
onModelDependencyClick,
113-
}: Props) => {
113+
}: LibraryRowProps) => {
114114
const modeledPercentage = useMemo(() => {
115115
return calculateModeledPercentage(methods);
116116
}, [methods]);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import * as React from "react";
2+
import { render as reactRender, screen } from "@testing-library/react";
3+
import { createMethod } from "../../../../test/factories/data-extension/method-factories";
4+
import { LibraryRow, LibraryRowProps } from "../LibraryRow";
5+
import { InProgressMethods } from "../../../model-editor/shared/in-progress-methods";
6+
import { createMockExtensionPack } from "../../../../test/factories/model-editor/extension-pack";
7+
import { Mode } from "../../../model-editor/shared/mode";
8+
import { ModelEditorViewState } from "../../../model-editor/shared/view-state";
9+
import userEvent from "@testing-library/user-event";
10+
11+
describe(LibraryRow.name, () => {
12+
const method = createMethod();
13+
const onChange = jest.fn();
14+
const onSaveModelClick = jest.fn();
15+
const onGenerateFromLlmClick = jest.fn();
16+
const onStopGenerateFromLlmClick = jest.fn();
17+
const onModelDependencyClick = jest.fn();
18+
19+
const viewState: ModelEditorViewState = {
20+
mode: Mode.Application,
21+
showFlowGeneration: false,
22+
showLlmButton: false,
23+
extensionPack: createMockExtensionPack(),
24+
};
25+
26+
const render = (props: Partial<LibraryRowProps> = {}) =>
27+
reactRender(
28+
<LibraryRow
29+
title="sql2o"
30+
libraryVersion="1.6.0"
31+
methods={[method]}
32+
modeledMethods={{
33+
[method.signature]: {
34+
...method,
35+
type: "sink",
36+
input: "Argument[0]",
37+
output: "",
38+
kind: "jndi-injection",
39+
provenance: "df-generated",
40+
},
41+
}}
42+
modifiedSignatures={new Set([method.signature])}
43+
inProgressMethods={new InProgressMethods()}
44+
viewState={viewState}
45+
hideModeledMethods={false}
46+
onChange={onChange}
47+
onSaveModelClick={onSaveModelClick}
48+
onGenerateFromLlmClick={onGenerateFromLlmClick}
49+
onStopGenerateFromLlmClick={onStopGenerateFromLlmClick}
50+
onGenerateFromSourceClick={jest.fn()}
51+
onModelDependencyClick={onModelDependencyClick}
52+
{...props}
53+
/>,
54+
);
55+
56+
it("renders the row", () => {
57+
render();
58+
59+
expect(screen.queryByText("sql2o@1.6.0")).toBeInTheDocument();
60+
expect(screen.queryByText("Model from source")).not.toBeInTheDocument();
61+
expect(screen.queryByText("Model with AI")).not.toBeInTheDocument();
62+
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
63+
});
64+
65+
it("renders the row when flow generation is enabled", () => {
66+
render({
67+
viewState: {
68+
...viewState,
69+
showFlowGeneration: true,
70+
},
71+
});
72+
73+
expect(screen.queryByText("Model from source")).toBeInTheDocument();
74+
expect(screen.queryByText("Model with AI")).not.toBeInTheDocument();
75+
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
76+
});
77+
78+
it("renders the row when LLM is enabled", () => {
79+
render({
80+
viewState: {
81+
...viewState,
82+
showLlmButton: true,
83+
},
84+
});
85+
86+
expect(screen.queryByText("Model from source")).not.toBeInTheDocument();
87+
expect(screen.queryByText("Model with AI")).toBeInTheDocument();
88+
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
89+
});
90+
91+
it("renders the row when flow generation and LLM are enabled", () => {
92+
render({
93+
viewState: {
94+
...viewState,
95+
showFlowGeneration: true,
96+
showLlmButton: true,
97+
},
98+
});
99+
100+
expect(screen.queryByText("Model from source")).toBeInTheDocument();
101+
expect(screen.queryByText("Model with AI")).toBeInTheDocument();
102+
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
103+
});
104+
105+
it("can expand the row", async () => {
106+
render();
107+
108+
expect(screen.queryByText("Save")).not.toBeInTheDocument();
109+
110+
await userEvent.click(
111+
screen.getByRole("button", {
112+
name: /sql2o@1.6.0/,
113+
}),
114+
);
115+
116+
expect(screen.getByText("Save")).toBeInTheDocument();
117+
});
118+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ExtensionPack } from "../../../src/model-editor/shared/extension-pack";
2+
import { join } from "path";
3+
4+
export function createMockExtensionPack({
5+
path = "/path/to/extension-pack",
6+
...data
7+
}: Partial<ExtensionPack> = {}): ExtensionPack {
8+
return {
9+
path,
10+
yamlPath: join(path, "codeql-pack.yml"),
11+
name: "sql2o",
12+
version: "0.0.0",
13+
language: "java",
14+
extensionTargets: {
15+
"codeql/java-all": "*",
16+
},
17+
dataExtensions: ["models/**/*.yml"],
18+
...data,
19+
};
20+
}

0 commit comments

Comments
 (0)