Skip to content

Commit 0a48e84

Browse files
authored
Merge pull request #8 from cloudhx/iss1
Basic testing has been included and is ready for merging into master
2 parents 379b7d7 + 1c14207 commit 0a48e84

File tree

14 files changed

+1437
-11
lines changed

14 files changed

+1437
-11
lines changed

package-lock.json

Lines changed: 578 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
]
3636
},
3737
"devDependencies": {
38-
"redux-immutable-state-invariant": "^2.1.0"
38+
"@testing-library/jest-dom": "^4.1.0",
39+
"@testing-library/react": "^9.3.0",
40+
"enzyme": "^3.10.0",
41+
"enzyme-adapter-react-16": "^1.14.0",
42+
"fetch-mock": "^7.5.1",
43+
"node-fetch": "^2.6.0",
44+
"react-test-renderer": "^16.10.2",
45+
"redux-immutable-state-invariant": "^2.1.0",
46+
"redux-mock-store": "^1.5.3"
3947
}
4048
}

src/App.test.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/components/App.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
import { MemoryRouter } from "react-router-dom";
4+
import App from "./App";
5+
6+
it("renders without crashing", () => {
7+
const div = document.createElement("div");
8+
ReactDOM.render(
9+
<MemoryRouter>
10+
<App />
11+
</MemoryRouter>,
12+
div
13+
);
14+
ReactDOM.unmountComponentAtNode(div);
15+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import "@testing-library/jest-dom/extend-expect";
4+
import { MemoryRouter } from "react-router-dom";
5+
import IssueList from "./IssueList";
6+
import { issues } from "../../tools/mockData";
7+
8+
function renderIssueList(args) {
9+
let defaultProps = {
10+
issues: [],
11+
closing: false,
12+
onCloseClick: () => {}
13+
};
14+
const props = { ...defaultProps, ...args };
15+
return render(
16+
<MemoryRouter>
17+
<IssueList {...props} />
18+
</MemoryRouter>
19+
);
20+
}
21+
22+
test("lists all issues", () => {
23+
const { getAllByText } = renderIssueList({
24+
issues: issues
25+
});
26+
27+
expect(getAllByText(/close/i)).toHaveLength(issues.length);
28+
// just like a manual tester, we'll instruct our test to wait for the alert
29+
// to show up before continuing with our assertions.
30+
//const alert = await findByRole("alert");
31+
32+
// .toHaveTextContent() comes from jest-dom's assertions
33+
// otherwise you could use expect(alert.textContent).toMatch(/congrats/i)
34+
// but jest-dom will give you better error messages which is why it's recommended
35+
//expect(alert).toHaveTextContent(/congrats/i);
36+
//expect(window.localStorage.getItem("token")).toEqual(fakeUserResponse.token);
37+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import "@testing-library/jest-dom/extend-expect";
4+
import TokenForm from "./TokenForm";
5+
6+
function renderTokenForm(args) {
7+
let defaultProps = {
8+
token: "",
9+
errors: {},
10+
saving: false,
11+
onSave: () => {},
12+
onChange: () => {}
13+
};
14+
15+
const props = { ...defaultProps, ...args };
16+
return render(<TokenForm {...props} />);
17+
}
18+
19+
test('labels save button as "Save" when not saving', () => {
20+
const { getByText } = renderTokenForm();
21+
getByText("Save").closest("button");
22+
});
23+
24+
test('labels save button as "Validating..." when saving', () => {
25+
const { getByText } = renderTokenForm({ saving: true });
26+
getByText("Validating...").closest("button");
27+
});
28+
29+
test("disables save button when saving", () => {
30+
const { getByText } = renderTokenForm({ saving: true });
31+
expect(getByText("Validating...").closest("button")).toHaveAttribute(
32+
"disabled"
33+
);
34+
});

src/components/token/TokenPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import PropTypes from "prop-types";
66
import TokenForm from "./TokenForm";
77
import { toast } from "react-toastify";
88

9-
function TokenPage({
9+
export function TokenPage({
1010
user,
1111
saveToken,
1212
removeToken,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
import { mount } from "enzyme";
3+
import { TokenPage } from "./TokenPage";
4+
5+
function render(args) {
6+
const defaultProps = {
7+
token: "",
8+
user: {},
9+
history: {},
10+
saveToken: jest.fn(),
11+
removeToken: jest.fn(),
12+
loadUser: jest.fn(),
13+
match: {}
14+
};
15+
16+
const props = { ...defaultProps, ...args };
17+
18+
return mount(<TokenPage {...props} />);
19+
}
20+
21+
it("sets error when attempting to save an empty token field", () => {
22+
const wrapper = render();
23+
wrapper.find("form").simulate("submit");
24+
const error = wrapper.find(".alert").first();
25+
expect(error.text()).toBe("Personal access token is required.");
26+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as issueActions from "./issueActions";
2+
import * as types from "./actionTypes";
3+
import { issues } from "../../tools/mockData";
4+
import thunk from "redux-thunk";
5+
import fetchMock from "fetch-mock";
6+
import configureMockStore from "redux-mock-store";
7+
8+
// Test an async action
9+
const middleware = [thunk];
10+
const mockStore = configureMockStore(middleware);
11+
12+
describe("Tests Async Actions", () => {
13+
afterEach(() => {
14+
fetchMock.restore();
15+
});
16+
17+
describe("Load Issues Thunk", () => {
18+
it("creates BEGIN_API_CALL and LOAD_ISSUES_SUCCESS when loading issues", () => {
19+
fetchMock.mock("*", {
20+
body: issues,
21+
headers: { "content-type": "application/json" }
22+
});
23+
24+
const expectedActions = [
25+
{ type: types.BEGIN_API_CALL },
26+
{ type: types.LOAD_ISSUES_SUCCESS, issues }
27+
];
28+
29+
const store = mockStore({ issues: [] });
30+
return store.dispatch(issueActions.loadIssues()).then(() => {
31+
expect(store.getActions()).toEqual(expectedActions);
32+
});
33+
});
34+
});
35+
});
36+
37+
describe("Tests Issue Actions", () => {
38+
it("tests LOAD_ISSUES_SUCCESS action", () => {
39+
//arrange
40+
const expectedAction = {
41+
type: types.LOAD_ISSUES_SUCCESS,
42+
issues
43+
};
44+
45+
//act
46+
const action = issueActions.loadIssuesSuccess(issues);
47+
48+
//assert
49+
expect(action).toEqual(expectedAction);
50+
});
51+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as tokenActions from "./tokenActions";
2+
import * as types from "./actionTypes";
3+
import { token } from "../../tools/mockData";
4+
5+
describe("Tests Token Actions", () => {
6+
it("tests SAVE_TOKEN action", () => {
7+
//arrange
8+
const expectedAction = {
9+
type: types.SAVE_TOKEN,
10+
token
11+
};
12+
13+
//act
14+
const action = tokenActions.saveToken(token);
15+
16+
//assert
17+
expect(action).toEqual(expectedAction);
18+
});
19+
20+
it("tests REMOVE_TOKEN action", () => {
21+
//arrange
22+
const expectedAction = {
23+
type: types.REMOVE_TOKEN
24+
};
25+
26+
//act
27+
const action = tokenActions.removeToken();
28+
29+
//assert
30+
expect(action).toEqual(expectedAction);
31+
});
32+
});

0 commit comments

Comments
 (0)