Skip to content

Commit

Permalink
Improve RequestFormExecution tests coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
dobrac committed Apr 18, 2024
1 parent 6897ef1 commit a4b5c9a
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import "../../../../../tests/mocks/grpc-web";
import "../../../../../tests/mocks/react-hook-form";
import { act, render } from "@testing-library/react";
import MethodContextProvider from "@/contexts/MethodContext";
import MethodContextProvider, {
MethodContextData,
MethodRequest,
MethodResponse,
SourceContext,
} from "@/contexts/MethodContext";
import { context } from "../../../../../tests/protobufjs-source";
import RequestFormExecution from "@/components/parts/method/request/RequestFormExecution";
import { GrpcWebMockMetadataResultError } from "../../../../../tests/mocks/grpc-web";
import { GrpcWebFormat } from "@/types/grpc-web";
import {
MetadataContext,
MetadataContextData,
} from "@/contexts/MetadataContext";
import { mockFormContext } from "../../../../../tests/mocks/react-hook-form";

describe("RequestFormExecution", () => {
const service = context.lookupService("helloworld.Greeter");
Expand Down Expand Up @@ -37,4 +50,161 @@ describe("RequestFormExecution", () => {
renderedElement.queryByTestId("method-cancel-button"),
).not.toBeInTheDocument();
});

it("renders - execution - error", async () => {
const method = service.methods["SayHello"];
method.resolve();

let request: MethodRequest = {
format: GrpcWebFormat.TEXT,
};
let response: MethodResponse | undefined;

const contextData: MethodContextData = {
processing: false,
request,
response,
functions: {
setRequest: (it) => {
if (typeof it === "function") {
request = it(request);
} else {
request = it;
}
return request;
},
setResponse: (it) => {
if (typeof it === "function") {
response = it(response);
} else {
response = it;
}
return response;
},
setCancelFunction: jest.fn(),
},
};

const contextMetadata: MetadataContextData = {
metadata: {
expectedResult: GrpcWebMockMetadataResultError,
},
setMetadata: jest.fn(),
};

const renderedElement = await act(async () => {
return render(
<MethodContextProvider method={method}>
<MetadataContext.Provider value={contextMetadata}>
<SourceContext.Provider value={contextData}>
<RequestFormExecution service={service} method={method} />
</SourceContext.Provider>
</MetadataContext.Provider>
</MethodContextProvider>,
);
});

const element = await renderedElement.findByTestId("method-execute-button");
expect(element).toBeInTheDocument();

const consoleError = jest.fn();
console.error = consoleError;
await act(async () => {
element.click();
});

expect(response?.error).toBeDefined();
expect(consoleError).toHaveBeenCalled();
});

it("renders - execution - cancel", async () => {
const method = service.methods["SayHello"];
method.resolve();

const cancelFunction = jest.fn();

const contextData: MethodContextData = {
processing: true,
request: {
format: GrpcWebFormat.TEXT,
},
functions: {
setRequest: jest.fn(),
setResponse: jest.fn(),
cancel: cancelFunction,
setCancelFunction: jest.fn(),
},
};

const renderedElement = await act(async () => {
return render(
<MethodContextProvider method={method}>
<SourceContext.Provider value={contextData}>
<RequestFormExecution service={service} method={method} />
</SourceContext.Provider>
</MethodContextProvider>,
);
});

const cancelButton = await renderedElement.findByTestId(
"method-cancel-button",
);
expect(cancelButton).toBeInTheDocument();

await act(async () => {
cancelButton.click();
});

expect(cancelFunction).toHaveBeenCalled();
});

it("renders - execution - validation error", async () => {
const method = service.methods["SayHello"];
method.resolve();

const VALIDATION_ERROR = "Error has occurred";

// Mock requestType verify error
if (!method.resolvedRequestType) {
throw new Error("Request type not resolved");
}
method.resolvedRequestType.verify = jest.fn(() => {
return VALIDATION_ERROR;
});

const contextData: MethodContextData = {
processing: false,
request: {
format: GrpcWebFormat.TEXT,
},
functions: {
setRequest: jest.fn(),
setResponse: jest.fn(),
setCancelFunction: jest.fn(),
},
};

const renderedElement = await act(async () => {
return render(
<MethodContextProvider method={method}>
<SourceContext.Provider value={contextData}>
<RequestFormExecution service={service} method={method} />
</SourceContext.Provider>
</MethodContextProvider>,
);
});

const element = await renderedElement.findByTestId("method-execute-button");
expect(element).toBeInTheDocument();

await act(async () => {
element.click();
});

expect(mockFormContext.setError).toHaveBeenCalled();
expect(mockFormContext.setError).toHaveBeenCalledWith(VALIDATION_ERROR, {
type: "manual",
message: expect.any(String),
});
});
});
18 changes: 10 additions & 8 deletions web/src/contexts/MethodContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import { Metadata } from "@/contexts/MetadataContext";

type CancelFunction = () => void;

interface MethodRequest {
export interface MethodRequest {
format: GrpcWebFormat;
metadata?: Metadata;
message?: object;
}

interface MethodResponse {
export interface MethodResponse {
error?: Error;
headers?: Metadata;
trailers?: Metadata;
Expand All @@ -40,14 +40,14 @@ export interface MethodFunctions {
setCancelFunction: Dispatch<SetStateAction<CancelFunction | undefined>>;
}

interface MethodContextData {
export interface MethodContextData {
functions: MethodFunctions;
request: MethodRequest;
response?: MethodResponse;
processing: boolean;
}

const defaultValues: MethodContextData = {
export const defaultMethodContextData: MethodContextData = {
processing: false,
request: {
format: GrpcWebFormat.TEXT,
Expand All @@ -59,7 +59,9 @@ const defaultValues: MethodContextData = {
},
};

export const SourceContext = createContext<MethodContextData>(defaultValues);
export const SourceContext = createContext<MethodContextData>(
defaultMethodContextData,
);

export function useMethodContext() {
return useContext(SourceContext);
Expand All @@ -78,13 +80,13 @@ export default function MethodContextProvider({

const [cancelFunction, setCancelFunction] = useState<
CancelFunction | undefined
>(defaultValues.functions.cancel);
>(defaultMethodContextData.functions.cancel);

const [request, setRequest] = useState<MethodRequest>({
...defaultValues.request,
...defaultMethodContextData.request,
});
const [response, setResponse] = useState<MethodResponse | undefined>(
defaultValues.response,
defaultMethodContextData.response,
);

const processing = !!cancelFunction;
Expand Down
18 changes: 18 additions & 0 deletions web/tests/mocks/react-hook-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const mockFormContext = {
formState: {
isValid: true,
},
setError: jest.fn(),
};

jest.mock("react-hook-form", () => {
const module = jest.requireActual("react-hook-form");
const useFormContext = () => ({
...module.useFormContext(),
...mockFormContext,
});
return {
...module,
useFormContext,
};
});

0 comments on commit a4b5c9a

Please sign in to comment.