Add story and tests for LibraryRow

This commit is contained in:
Koen Vlaswinkel
2023-09-14 14:06:32 +02:00
parent 9175449323
commit d3a9426411
5 changed files with 361 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
/**
* A class that keeps track of which methods are in progress for each package.
*
* This class is immutable and therefore is safe to be used in a react useState hook.
* This class is immutable and therefore is safe to be used in a React useState hook.
*/
export class InProgressMethods {
// A map of in-progress method signatures for each package.

View File

@@ -0,0 +1,220 @@
import * as React from "react";
import { Meta, StoryFn } from "@storybook/react";
import { Mode } from "../../model-editor/shared/mode";
import { LibraryRow as LibraryRowComponent } from "../../view/model-editor/LibraryRow";
import { CallClassification } from "../../model-editor/method";
import { InProgressMethods } from "../../model-editor/shared/in-progress-methods";
import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack";
export default {
title: "CodeQL Model Editor/Library Row",
component: LibraryRowComponent,
} as Meta<typeof LibraryRowComponent>;
const Template: StoryFn<typeof LibraryRowComponent> = (args) => (
<LibraryRowComponent {...args} />
);
export const LibraryRow = Template.bind({});
LibraryRow.args = {
title: "sql2o",
libraryVersion: "1.6.0",
methods: [
{
library: "sql2o",
libraryVersion: "1.6.0",
signature: "org.sql2o.Connection#createQuery(String)",
packageName: "org.sql2o",
typeName: "Connection",
methodName: "createQuery",
methodParameters: "(String)",
supported: true,
supportedType: "summary",
usages: Array(10).fill({
label: "createQuery(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 56,
},
classification: CallClassification.Source,
}),
},
{
library: "sql2o",
libraryVersion: "1.6.0",
signature: "org.sql2o.Query#executeScalar(Class)",
packageName: "org.sql2o",
typeName: "Query",
methodName: "executeScalar",
methodParameters: "(Class)",
supported: true,
supportedType: "neutral",
usages: Array(2).fill({
label: "executeScalar(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 85,
},
classification: CallClassification.Source,
}),
},
{
library: "sql2o",
libraryVersion: "1.6.0",
signature: "org.sql2o.Sql2o#open()",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "open",
methodParameters: "()",
supported: false,
supportedType: "none",
usages: Array(28).fill({
label: "open(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 14,
startColumn: 24,
endLine: 14,
endColumn: 35,
},
classification: CallClassification.Source,
}),
},
{
library: "sql2o",
libraryVersion: "1.6.0",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "Sql2o",
methodParameters: "(String,String,String)",
supported: false,
supportedType: "none",
usages: Array(106).fill({
label: "new Sql2o(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 10,
startColumn: 33,
endLine: 10,
endColumn: 88,
classification: CallClassification.Test,
},
}),
},
{
library: "sql2o",
libraryVersion: "1.6.0",
signature: "org.sql2o.Sql2o#Sql2o(String)",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "Sql2o",
methodParameters: "(String)",
supported: false,
supportedType: "none",
usages: [
...Array(4).fill({
label: "new Sql2o(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 23,
startColumn: 23,
endLine: 23,
endColumn: 36,
},
classification: CallClassification.Test,
}),
{
label: "new Sql2o(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/build/generated/java/org/example/HelloControllerGenerated.java",
startLine: 23,
startColumn: 23,
endLine: 23,
endColumn: 36,
},
classification: CallClassification.Generated,
},
],
},
],
modeledMethods: {
"org.sql2o.Sql2o#Sql2o(String)": {
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String)",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "Sql2o",
methodParameters: "(String)",
},
"org.sql2o.Connection#createQuery(String)": {
type: "summary",
input: "Argument[this]",
output: "ReturnValue",
kind: "taint",
provenance: "df-manual",
signature: "org.sql2o.Connection#createQuery(String)",
packageName: "org.sql2o",
typeName: "Connection",
methodName: "createQuery",
methodParameters: "(String)",
},
"org.sql2o.Sql2o#open()": {
type: "summary",
input: "Argument[this]",
output: "ReturnValue",
kind: "taint",
provenance: "manual",
signature: "org.sql2o.Sql2o#open()",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "open",
methodParameters: "()",
},
"org.sql2o.Query#executeScalar(Class)": {
type: "neutral",
input: "",
output: "",
kind: "",
provenance: "df-generated",
signature: "org.sql2o.Query#executeScalar(Class)",
packageName: "org.sql2o",
typeName: "Query",
methodName: "executeScalar",
methodParameters: "(Class)",
},
"org.sql2o.Sql2o#Sql2o(String,String,String)": {
type: "neutral",
input: "",
output: "",
kind: "",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
packageName: "org.sql2o",
typeName: "Sql2o",
methodName: "Sql2o",
methodParameters: "(String,String,String)",
},
},
modifiedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]),
inProgressMethods: new InProgressMethods(),
viewState: {
extensionPack: createMockExtensionPack(),
showFlowGeneration: true,
showLlmButton: true,
mode: Mode.Application,
},
hideModeledMethods: false,
};

View File

@@ -67,7 +67,7 @@ const ButtonsContainer = styled.div`
margin-right: 1rem;
`;
type Props = {
export type LibraryRowProps = {
title: string;
libraryVersion?: string;
methods: Method[];
@@ -110,7 +110,7 @@ export const LibraryRow = ({
onStopGenerateFromLlmClick,
onGenerateFromSourceClick,
onModelDependencyClick,
}: Props) => {
}: LibraryRowProps) => {
const modeledPercentage = useMemo(() => {
return calculateModeledPercentage(methods);
}, [methods]);

View File

@@ -0,0 +1,118 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { createMethod } from "../../../../test/factories/data-extension/method-factories";
import { LibraryRow, LibraryRowProps } from "../LibraryRow";
import { InProgressMethods } from "../../../model-editor/shared/in-progress-methods";
import { createMockExtensionPack } from "../../../../test/factories/model-editor/extension-pack";
import { Mode } from "../../../model-editor/shared/mode";
import { ModelEditorViewState } from "../../../model-editor/shared/view-state";
import userEvent from "@testing-library/user-event";
describe(LibraryRow.name, () => {
const method = createMethod();
const onChange = jest.fn();
const onSaveModelClick = jest.fn();
const onGenerateFromLlmClick = jest.fn();
const onStopGenerateFromLlmClick = jest.fn();
const onModelDependencyClick = jest.fn();
const viewState: ModelEditorViewState = {
mode: Mode.Application,
showFlowGeneration: false,
showLlmButton: false,
extensionPack: createMockExtensionPack(),
};
const render = (props: Partial<LibraryRowProps> = {}) =>
reactRender(
<LibraryRow
title="sql2o"
libraryVersion="1.6.0"
methods={[method]}
modeledMethods={{
[method.signature]: {
...method,
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
},
}}
modifiedSignatures={new Set([method.signature])}
inProgressMethods={new InProgressMethods()}
viewState={viewState}
hideModeledMethods={false}
onChange={onChange}
onSaveModelClick={onSaveModelClick}
onGenerateFromLlmClick={onGenerateFromLlmClick}
onStopGenerateFromLlmClick={onStopGenerateFromLlmClick}
onGenerateFromSourceClick={jest.fn()}
onModelDependencyClick={onModelDependencyClick}
{...props}
/>,
);
it("renders the row", () => {
render();
expect(screen.queryByText("sql2o@1.6.0")).toBeInTheDocument();
expect(screen.queryByText("Model from source")).not.toBeInTheDocument();
expect(screen.queryByText("Model with AI")).not.toBeInTheDocument();
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
});
it("renders the row when flow generation is enabled", () => {
render({
viewState: {
...viewState,
showFlowGeneration: true,
},
});
expect(screen.queryByText("Model from source")).toBeInTheDocument();
expect(screen.queryByText("Model with AI")).not.toBeInTheDocument();
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
});
it("renders the row when LLM is enabled", () => {
render({
viewState: {
...viewState,
showLlmButton: true,
},
});
expect(screen.queryByText("Model from source")).not.toBeInTheDocument();
expect(screen.queryByText("Model with AI")).toBeInTheDocument();
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
});
it("renders the row when flow generation and LLM are enabled", () => {
render({
viewState: {
...viewState,
showFlowGeneration: true,
showLlmButton: true,
},
});
expect(screen.queryByText("Model from source")).toBeInTheDocument();
expect(screen.queryByText("Model with AI")).toBeInTheDocument();
expect(screen.queryByText("Model dependency")).toBeInTheDocument();
});
it("can expand the row", async () => {
render();
expect(screen.queryByText("Save")).not.toBeInTheDocument();
await userEvent.click(
screen.getByRole("button", {
name: /sql2o@1.6.0/,
}),
);
expect(screen.getByText("Save")).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,20 @@
import { ExtensionPack } from "../../../src/model-editor/shared/extension-pack";
import { join } from "path";
export function createMockExtensionPack({
path = "/path/to/extension-pack",
...data
}: Partial<ExtensionPack> = {}): ExtensionPack {
return {
path,
yamlPath: join(path, "codeql-pack.yml"),
name: "sql2o",
version: "0.0.0",
language: "java",
extensionTargets: {
"codeql/java-all": "*",
},
dataExtensions: ["models/**/*.yml"],
...data,
};
}