-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test coverage for spec patch and runtime ops
- Loading branch information
Showing
3 changed files
with
389 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,377 @@ | ||
import "@testing-library/jest-dom"; | ||
import { act, fireEvent, render, screen } from "@testing-library/react"; | ||
import * as OPFSSharedInternalsService from "@/services/opfs-projects-shared-internals"; | ||
import { RequestsSpecsContextProvider } from "./RequestsSpecsContextProvider"; | ||
import { Runtime } from "./Runtime"; | ||
|
||
jest.mock("wouter", () => ({ | ||
useParams: jest.fn().mockReturnValue({}), | ||
})); | ||
|
||
jest.mock("@/services/opfs-projects-shared-internals", () => ({ | ||
retrieveProject: jest.fn(), | ||
persistProject: jest.fn(), | ||
})); | ||
|
||
interface MockedGlobalWithFetch { | ||
fetch: (url: string) => Promise<MockedResponse>; | ||
} | ||
|
||
interface MockedResponse { | ||
text: () => Promise<string>; | ||
status: number; | ||
ok: boolean; | ||
headers: [string, string][]; | ||
} | ||
|
||
describe("Runtime component, given a selected specification", () => { | ||
beforeAll(() => { | ||
(global as unknown as MockedGlobalWithFetch).fetch = async () => ({ | ||
text: async () => "", | ||
status: -1, | ||
ok: false, | ||
headers: [], | ||
}); | ||
}); | ||
|
||
beforeEach(async () => { | ||
jest.clearAllMocks(); | ||
|
||
jest | ||
.spyOn(OPFSSharedInternalsService, "retrieveProject") | ||
.mockResolvedValue({ | ||
uuid: "7fde4f8e-b6ac-4218-ae20-1b866e61ec56", | ||
name: "Zippopotamus", | ||
sections: [], | ||
specs: [ | ||
{ | ||
uuid: "0b761507-a24c-4a81-8391-9cee4a6e7c34", | ||
url: "https://api.zippopotam.us/us/33162", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "f62f869f-cb12-4997-b679-ceec1096040b", | ||
url: "https://api.zippopotam.us/br/70150904", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "304f0780-33a3-45e4-b9ea-e099f4306832", | ||
url: "https://api.zippopotam.us/blabla", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "a78ee247-4bf2-4393-bec0-57a1b0a8a23d", | ||
url: "https://catfact.ninja/non-existing", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
], | ||
}); | ||
|
||
jest | ||
.spyOn(OPFSSharedInternalsService, "persistProject") | ||
.mockResolvedValue(); | ||
|
||
jest | ||
.spyOn(global as unknown as MockedGlobalWithFetch, "fetch") | ||
.mockImplementation(async (url: string) => { | ||
switch (url) { | ||
case "https://api.zippopotam.us/us/33162": | ||
return { | ||
text: async () => | ||
JSON.stringify({ | ||
"post code": "33162", | ||
country: "United States", | ||
"country abbreviation": "US", | ||
places: [ | ||
{ | ||
"place name": "Miami", | ||
longitude: "-80.183", | ||
state: "Florida", | ||
"state abbreviation": "FL", | ||
latitude: "25.9286", | ||
}, | ||
], | ||
}), | ||
status: 200, | ||
ok: true, | ||
headers: [ | ||
["content-type", "application/json"], | ||
["x-custom-header", "my-custom-h-value"], | ||
], | ||
}; | ||
|
||
case "https://api.zippopotam.us/br/70150904": | ||
return { | ||
text: async () => JSON.stringify({}), | ||
status: 400, | ||
ok: false, | ||
headers: [["content-type", "application/json"]], | ||
}; | ||
|
||
case "https://api.zippopotam.us/blabla": | ||
return { | ||
text: async () => ` | ||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> | ||
<html> | ||
<head> | ||
<title>Error: 404 Not Found</title> | ||
<style type="text/css"> | ||
html {background-color: #eee; font-family: sans;} body {background-color: #fff; border: 1px solid #ddd; padding: 15px; margin: 15px;} pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>Error: 404 Not Found</h1> <p>Sorry, the requested URL <tt>'http://api.zippopotam.us/blabla'</tt> caused an error:</p> <pre>Not found: '/blabla'</pre> | ||
</body> | ||
</html> | ||
`, | ||
status: 404, | ||
ok: false, | ||
headers: [["content-type", "text/html; charset=UTF-8"]], | ||
}; | ||
|
||
case "https://catfact.ninja/non-existing": | ||
throw new Error("CORS Error (mocked)."); | ||
|
||
default: | ||
throw new Error( | ||
"Unexpected `fetch` call during test scenario. Unmocked URL." | ||
); | ||
} | ||
}); | ||
}); | ||
|
||
it("displays such selected spec, with already editable url", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="0b761507-a24c-4a81-8391-9cee4a6e7c34" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const url = screen.getByLabelText(/URL/i); | ||
expect(url).toHaveValue("https://api.zippopotam.us/us/33162"); | ||
}); | ||
|
||
it("can edit only (and really only) its URL while typing", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="0b761507-a24c-4a81-8391-9cee4a6e7c34" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const url = screen.getByLabelText(/URL/i); | ||
await act(async () => | ||
fireEvent.change(url, { | ||
target: { value: "https://api-completetly-different" }, | ||
}) | ||
); | ||
|
||
// (waiting a little bit, cause patch calls are debounced) | ||
await new Promise((resolve) => setTimeout(resolve, 500)); | ||
|
||
expect(OPFSSharedInternalsService.persistProject).toHaveBeenCalledTimes(1); | ||
expect(OPFSSharedInternalsService.persistProject).toHaveBeenCalledWith({ | ||
uuid: "7fde4f8e-b6ac-4218-ae20-1b866e61ec56", | ||
name: "Zippopotamus", | ||
sections: [], | ||
specs: [ | ||
{ | ||
uuid: "0b761507-a24c-4a81-8391-9cee4a6e7c34", | ||
url: "https://api-completetly-different", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "f62f869f-cb12-4997-b679-ceec1096040b", | ||
url: "https://api.zippopotam.us/br/70150904", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "304f0780-33a3-45e4-b9ea-e099f4306832", | ||
url: "https://api.zippopotam.us/blabla", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
{ | ||
uuid: "a78ee247-4bf2-4393-bec0-57a1b0a8a23d", | ||
url: "https://catfact.ninja/non-existing", | ||
method: "GET", | ||
headers: [ | ||
{ key: "Accept", value: "application/json", isEnabled: true }, | ||
], | ||
body: "", | ||
}, | ||
], | ||
}); | ||
}); | ||
|
||
it("can perform and display a http 200 with json response", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="0b761507-a24c-4a81-8391-9cee4a6e7c34" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
expect(screen.getByText(/HTTP success/i)).toBeVisible(); | ||
expect(screen.getByText("200")).toBeVisible(); | ||
expect( | ||
screen.getByText( | ||
JSON.stringify({ | ||
"post code": "33162", | ||
country: "United States", | ||
"country abbreviation": "US", | ||
places: [ | ||
{ | ||
"place name": "Miami", | ||
longitude: "-80.183", | ||
state: "Florida", | ||
"state abbreviation": "FL", | ||
latitude: "25.9286", | ||
}, | ||
], | ||
}) | ||
) | ||
).toBeVisible(); | ||
}); | ||
|
||
it("can show detailed request headers", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="0b761507-a24c-4a81-8391-9cee4a6e7c34" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
const requestHeadersBtn = screen.getByRole("button", { | ||
name: "Request headers (1)", | ||
}); | ||
await act(async () => fireEvent.click(requestHeadersBtn)); | ||
|
||
expect(screen.getByText(/Accept.*:.*application\/json/i)).toBeVisible(); | ||
}); | ||
|
||
it("can show detailed response headers", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="0b761507-a24c-4a81-8391-9cee4a6e7c34" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
const requestHeadersBtn = screen.getByRole("button", { | ||
name: "Response headers (2)", | ||
}); | ||
await act(async () => fireEvent.click(requestHeadersBtn)); | ||
|
||
expect( | ||
screen.getByText(/content-type.*:.*application\/json/i) | ||
).toBeVisible(); | ||
expect( | ||
screen.getByText(/x-custom-header.*:.*my-custom-h-value/i) | ||
).toBeVisible(); | ||
}); | ||
|
||
it("can perform and display a http 400 with json response instead of regular success", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="f62f869f-cb12-4997-b679-ceec1096040b" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
expect(screen.queryByText(/HTTP success/i)).toBe(null); | ||
expect(screen.queryByText("200")).toBe(null); | ||
|
||
expect(screen.getByText(/HTTP bad status/i)).toBeVisible(); | ||
expect(screen.getByText("400")).toBeVisible(); | ||
expect(screen.getByText(JSON.stringify({}))).toBeVisible(); | ||
}); | ||
|
||
it("can perform and display a http 404 with any response instead of regular success", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="304f0780-33a3-45e4-b9ea-e099f4306832" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
expect(screen.queryByText(/HTTP success/i)).toBe(null); | ||
expect(screen.queryByText("200")).toBe(null); | ||
|
||
expect(screen.getByText(/HTTP bad status/i)).toBeVisible(); | ||
expect(screen.getByText("404")).toBeVisible(); | ||
expect( | ||
screen.getByText(/Sorry, the requested URL .* caused an error/im) | ||
).toBeVisible(); | ||
}); | ||
|
||
it("can display unknown exceptions/errors differently than http-related errors", async () => { | ||
await act(async () => | ||
render( | ||
<RequestsSpecsContextProvider projectUuid="7fde4f8e-b6ac-4218-ae20-1b866e61ec56"> | ||
<Runtime specUuid="a78ee247-4bf2-4393-bec0-57a1b0a8a23d" /> | ||
</RequestsSpecsContextProvider> | ||
) | ||
); | ||
|
||
const run = screen.getByRole("button", { name: /Run/i }); | ||
await act(async () => fireEvent.click(run)); | ||
|
||
expect(screen.queryByText(/HTTP success/i)).toBe(null); | ||
expect(screen.queryByText("200")).toBe(null); | ||
expect(screen.queryByText(/HTTP bad status/i)).toBe(null); | ||
|
||
expect(screen.getByText("Error")).toBeVisible(); | ||
expect(screen.getByText(/CORS Error/im)).toBeVisible(); | ||
}); | ||
}); |
Oops, something went wrong.