-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: init some component tests (#898)
Co-authored-by: Mark R. Florkowski <[email protected]>
- Loading branch information
1 parent
7478a92
commit cf6337f
Showing
4 changed files
with
317 additions
and
13 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,229 @@ | ||
// @vitest-environment happy-dom | ||
/// <reference types="@testing-library/jest-dom/vitest" /> | ||
|
||
import { cleanup, fireEvent, render, waitFor } from "@testing-library/react"; | ||
import { http, HttpResponse } from "msw"; | ||
import { setupServer } from "msw/node"; | ||
import { | ||
afterAll, | ||
afterEach, | ||
beforeAll, | ||
describe, | ||
expect, | ||
it, | ||
vi, | ||
} from "vitest"; | ||
|
||
import { | ||
createRouteHandler, | ||
createUploadthing, | ||
extractRouterConfig, | ||
} from "uploadthing/server"; | ||
|
||
import { generateUploadButton } from "../src"; | ||
|
||
const noop = vi.fn(); | ||
|
||
const f = createUploadthing(); | ||
const testRouter = { | ||
image: f({ image: {} }).onUploadComplete(noop), | ||
audio: f({ audio: {} }).onUploadComplete(noop), | ||
pdf: f({ "application/pdf": {} }).onUploadComplete(noop), | ||
multi: f({ image: { maxFileCount: 4 } }).onUploadComplete(noop), | ||
}; | ||
const routeHandler = createRouteHandler({ router: testRouter }); | ||
const UploadButton = generateUploadButton<typeof testRouter>(); | ||
|
||
const utGet = vi.fn<[Request]>(); | ||
const utPost = vi.fn<[{ request: Request; body: any }]>(); | ||
const server = setupServer( | ||
http.get("/api/uploadthing", ({ request }) => { | ||
utGet(request); | ||
return routeHandler.GET(request); | ||
}), | ||
http.post("/api/uploadthing", async ({ request }) => { | ||
const body = await request.json(); | ||
utPost({ request, body }); | ||
return HttpResponse.json([ | ||
// empty array, we're not testing the upload endpoint here | ||
// we have other tests for that... | ||
]); | ||
}), | ||
); | ||
|
||
beforeAll(() => server.listen()); | ||
afterEach(() => { | ||
server.resetHandlers(); | ||
cleanup(); | ||
}); | ||
afterAll(() => server.close()); | ||
|
||
/** | ||
* This is a basic suite of tests for the UploadButton component. | ||
* This is not meant to be a comprehensive test suite, but rather a | ||
* basic check of core functionality. | ||
* | ||
* In the future, the goal is to use these and other tests to ensure | ||
* consistency across the components in alll of the supported libraries | ||
* (React, Solid, Svelte, Vue, etc.). Ideally core test logic should be | ||
* shared as much as possible, so that we don;t have to maintain the test | ||
* implementations in addition to the component implementations. | ||
* | ||
* In #886, we attempted to bring all of the libraries in line with each | ||
* other, and at that time we manually went verified the following: | ||
* - components all accept the same props/have similar APIs | ||
* - styles match across libraries in each component state | ||
* - copy matches across libraries | ||
* - components are reactive | ||
* - selecting files changes the text on the button | ||
* - uploading changes style and text on button | ||
* - paste works | ||
* - abort works | ||
* | ||
* Some other items we did not check but probably should have: | ||
* - functions are triggered at the right times | ||
* - onChange should occur on select, clear, and drop | ||
* - custom styling overrides are available and functioning (and always apply | ||
* to the same parts of the component) | ||
*/ | ||
|
||
describe("UploadButton - basic", () => { | ||
it("fetches and displays route config", async () => { | ||
const utils = render(<UploadButton endpoint="image" />); | ||
const label = utils.container.querySelector("label"); | ||
|
||
// Previously, when component was disabled, it would show "Loading..." | ||
// expect(label).toHaveTextContent("Loading..."); | ||
|
||
// then eventually we load in the data, and we should be in the ready state | ||
await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); | ||
expect(label).toHaveTextContent("Choose File"); | ||
|
||
expect(utGet).toHaveBeenCalledOnce(); | ||
expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); | ||
}); | ||
|
||
it("picks up route config from global and skips fetch", async () => { | ||
(window as any).__UPLOADTHING = extractRouterConfig(testRouter); | ||
|
||
const utils = render(<UploadButton endpoint="image" />); | ||
expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); | ||
expect(utGet).not.toHaveBeenCalled(); | ||
|
||
delete (window as any).__UPLOADTHING; | ||
}); | ||
|
||
it("requests URLs when a file is selected", async () => { | ||
const utils = render(<UploadButton endpoint="image" />); | ||
const label = utils.container.querySelector("label"); | ||
await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); | ||
|
||
fireEvent.change(utils.getByLabelText("Choose File"), { | ||
target: { files: [new File(["foo"], "foo.txt", { type: "text/plain" })] }, | ||
}); | ||
await waitFor(() => { | ||
expect(utPost).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
body: { | ||
files: [{ name: "foo.txt", type: "text/plain", size: 3 }], | ||
}, | ||
}), | ||
); | ||
}); | ||
}); | ||
|
||
it("manual mode requires extra click", async () => { | ||
const utils = render( | ||
<UploadButton endpoint="image" config={{ mode: "manual" }} />, | ||
); | ||
const label = utils.container.querySelector("label"); | ||
await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); | ||
|
||
fireEvent.change(utils.getByLabelText("Choose File"), { | ||
target: { files: [new File([""], "foo.txt")] }, | ||
}); | ||
expect(label).toHaveTextContent("Upload 1 file"); | ||
|
||
fireEvent.click(label!); | ||
await waitFor(() => { | ||
expect(utPost).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
body: { | ||
files: [expect.objectContaining({ name: "foo.txt" })], | ||
}, | ||
}), | ||
); | ||
}); | ||
}); | ||
|
||
// https://discord.com/channels/966627436387266600/1102510616326967306/1267098160468197409 | ||
it.skip("disabled", async () => { | ||
const utils = render(<UploadButton endpoint="image" disabled />); | ||
const label = utils.container.querySelector("label"); | ||
await waitFor(() => expect(label).toHaveTextContent("Choose File")); | ||
expect(label).toHaveAttribute("disabled"); | ||
}); | ||
}); | ||
|
||
describe("UploadButton - lifecycle hooks", () => { | ||
it("onBeforeUploadBegin alters the requested files", async () => { | ||
const utils = render( | ||
<UploadButton | ||
endpoint="image" | ||
onBeforeUploadBegin={() => { | ||
return [new File([""], "bar.txt")]; | ||
}} | ||
/>, | ||
); | ||
await waitFor(() => { | ||
expect(utils.getByText("Choose File")).toBeInTheDocument(); | ||
}); | ||
|
||
fireEvent.change(utils.getByLabelText("Choose File"), { | ||
target: { files: [new File([""], "foo.txt")] }, | ||
}); | ||
await waitFor(() => { | ||
expect(utPost).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
body: { | ||
files: [expect.objectContaining({ name: "bar.txt" })], | ||
}, | ||
}), | ||
); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("UploadButton - Theming", () => { | ||
it("renders custom styles", async () => { | ||
const utils = render( | ||
<UploadButton | ||
endpoint="image" | ||
appearance={{ | ||
button: { backgroundColor: "red" }, | ||
}} | ||
/>, | ||
); | ||
await waitFor(() => { | ||
expect(utils.getByText("Choose File")).toHaveStyle({ | ||
backgroundColor: "red", | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("UploadButton - Content Customization", () => { | ||
it("renders custom content", async () => { | ||
const utils = render( | ||
<UploadButton | ||
endpoint="image" | ||
content={{ | ||
allowedContent: "Allowed content", | ||
}} | ||
/>, | ||
); | ||
await waitFor(() => { | ||
expect(utils.getByText("Allowed content")).toBeInTheDocument(); | ||
}); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -1 +1,9 @@ | ||
export { default } from "../../vitest.config"; | ||
import { mergeConfig } from "vitest/config"; | ||
|
||
import baseConfig from "../../vitest.config"; | ||
|
||
export default mergeConfig(baseConfig, { | ||
test: { | ||
setupFiles: ["@testing-library/jest-dom/vitest"], | ||
}, | ||
}); |
Oops, something went wrong.