Skip to content

Commit

Permalink
Add full test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
zackify committed Mar 12, 2021
1 parent 3495dd0 commit 39fd196
Show file tree
Hide file tree
Showing 10 changed files with 6,730 additions and 1,036 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
lib
cjs
cjs
coverage
.DS_Store
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This has been rewritten into a single file for simplicity, and the function signature has gotten much smaller.

Todo: Test coverage
100% Test Coverage :)

### What is it?

Expand All @@ -13,7 +13,7 @@ It has upload progress due to using XHR, and can be used for uploading file dire
### Install

```js
npm install react-use-upload@1.0.0-beta2
npm install react-use-upload
```

### Usage
Expand Down
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
};
7,491 changes: 6,462 additions & 1,029 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 20 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "react-use-upload",
"version": "1.0.0-beta2",
"version": "1.0.0",
"description": "",
"main": "cjs/index.js",
"module": "lib/index.js",
"scripts": {
"test": "tsc",
"test": "jest",
"build": "npx babel src --out-dir lib --extensions \".ts,.tsx\" && npx babel --plugins @babel/plugin-transform-modules-commonjs src --out-dir cjs --extensions \".ts,.tsx\" && npm run create-types",
"create-types": "tsc --emitDeclarationOnly && tsc --emitDeclarationOnly --outDir cjs",
"watch": "npx babel --watch src --out-dir lib"
Expand All @@ -14,12 +14,28 @@
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.8",
"@babel/core": "^7.13.10",
"@babel/plugin-transform-modules-commonjs": "^7.13.8",
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.13.0",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^26.0.20",
"@types/react": "^17.0.2",
"babel-jest": "^26.6.3",
"jest": "^26.6.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"typescript": "^4.2.3"
},
"dependencies": {}
"dependencies": {},
"jest": {
"setupFiles": [
"./tests/setup.js"
],
"collectCoverageFrom": [
"./src/**/*.ts"
]
}
}
123 changes: 123 additions & 0 deletions tests/BasicTests.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from "react";
import { screen, render, act } from "@testing-library/react";
import { BasicTestUpload } from "./TestComponents";
import userEvent from "@testing-library/user-event";

test("It uploads a file correctly", async () => {
global.xhrOpen = jest.fn();
global.xhrSend = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);
//loading is hidden
expect(screen.queryByText("loading")).toEqual(null);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);

//loading is now shown
screen.getByText("loading");

await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrOpen.mock.calls[0][0]).toEqual("POST");
expect(global.xhrOpen.mock.calls[0][1]).toEqual("github.gov");
//check the type sent to the server
expect(global.xhrSend.mock.calls[0][0].name).toEqual("hello.png");
expect(global.xhrSend.mock.calls[0][0].type).toEqual("image/png");
});

test("Renders done after upload is complete", async () => {
global.xhrListener = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrListener.mock.calls[1][0]).toEqual("load");
expect(screen.queryByText("done")).toEqual(null);

//call the load method on the xhr mock
act(() => global.xhrListener.mock.calls[1][1]());

expect(screen.getByText("done")).toBeTruthy();
});

test("Renders upload progress", async () => {
global.xhrListener = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrListener.mock.calls[0][0]).toEqual("progress");
expect(screen.queryByText("done")).toEqual(null);

//call the load method on the xhr mock
act(() => global.xhrListener.mock.calls[0][1]({ loaded: 20, total: 100 }));

expect(screen.getByText("loading")).toBeTruthy();
expect(screen.getByText("20% progress")).toBeTruthy();
});

test("Renders error message if xhr fails", async () => {
global.xhrListener = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrListener.mock.calls[2][0]).toEqual("error");
expect(screen.queryByText("bad error!")).toEqual(null);

//call the load method on the xhr mock
act(() => global.xhrListener.mock.calls[2][1]("bad error!"));

expect(screen.getByText("bad error!")).toBeTruthy();
});

test("Renders error message if xhr aborts", async () => {
global.xhrListener = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrListener.mock.calls[3][0]).toEqual("abort");
expect(screen.queryByText("bad abort!")).toEqual(null);

//call the load method on the xhr mock
act(() => global.xhrListener.mock.calls[3][1]("bad abort!"));

expect(screen.getByText("bad abort!")).toBeTruthy();
});

test("Skips upload if options return null", async () => {
let skipOptions = jest.fn();
render(
<BasicTestUpload
method="POST"
url="github.gov"
skipOptionsCb={skipOptions}
/>
);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(skipOptions.mock.calls.length).toEqual(1);
});
45 changes: 45 additions & 0 deletions tests/ResponseHeaders.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import { screen, render, act } from "@testing-library/react";
import { BasicTestUpload } from "./TestComponents";
import userEvent from "@testing-library/user-event";

test("Sets response headers into an object", async () => {
global.xhrListener = jest.fn();
render(<BasicTestUpload method="POST" url="github.gov" />);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrListener.mock.calls[1][0]).toEqual("load");
expect(screen.queryByText("done")).toEqual(null);

//call the load method on the xhr mock
act(() => global.xhrListener.mock.calls[1][1]());

expect(screen.getByText("The test response header")).toBeTruthy();
});

test("Sets request headers from options object", async () => {
global.xhrRequestHeader = jest.fn();
render(
<BasicTestUpload
method="POST"
url="github.gov"
headers={{ yo: "awesome", another: "cool" }}
/>
);

//upload a file into the input
const file = new File(["hello"], "hello.png", { type: "image/png" });
userEvent.upload(screen.getByLabelText("upload"), file);
//wait for the next tick
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));

expect(global.xhrRequestHeader.mock.calls[0][0]).toEqual("yo");
expect(global.xhrRequestHeader.mock.calls[0][1]).toEqual("awesome");
expect(global.xhrRequestHeader.mock.calls[1][0]).toEqual("another");
expect(global.xhrRequestHeader.mock.calls[1][1]).toEqual("cool");
});
50 changes: 50 additions & 0 deletions tests/TestComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { useEffect } from "react";
import { useUpload } from "../src";

type Props = {
method: string;
url: string;
headers?: any;
skipOptionsCb?: () => any;
};
export const BasicTestUpload = ({
skipOptionsCb,
method,
url,
headers,
}: Props) => {
let [upload, { error, responseHeaders, progress, done, loading }] = useUpload(
({ files }) => {
if (skipOptionsCb) {
skipOptionsCb();
return undefined;
}
return {
method,
url,
headers,
body: files[0],
};
}
);

return (
<div>
{responseHeaders ? <div>{responseHeaders.Test}</div> : null}
{error ? <div>{error}</div> : null}
{done ? <div>done</div> : null}
{loading ? <div>loading</div> : null}
{loading ? `${progress}% progress` : null}
<input
type="file"
aria-label="upload"
onChange={(e) => {
if (e.target.files) {
upload({ files: e.target.files });
}
}}
/>
</div>
);
};
4 changes: 4 additions & 0 deletions tests/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare var xhrOpen: jest.Mock;
declare var xhrSend: jest.Mock;
declare var xhrListener: jest.Mock;
declare var xhrRequestHeader: jest.Mock;
15 changes: 15 additions & 0 deletions tests/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
global.XMLHttpRequest = function () {
let addEventListener = (name, callback) =>
global.xhrListener?.(name, callback);
let fake = () => true;
return {
open: global.xhrOpen ?? fake,
send: global.xhrSend ?? fake,
upload: {
addEventListener,
},
addEventListener,
setRequestHeader: global.xhrRequestHeader ?? fake,
getAllResponseHeaders: () => "Test: The test response header",
};
};

0 comments on commit 39fd196

Please sign in to comment.