Add Front End Testing:
1. Added JEST test support 2. Upgraded some dependencies (mainly typescript and eslint-typescript) 3. Replaced mocha with jest (as jest works better with TS) 4. Fixed .eslintrc to match the new eslint-typescript version 5. ADD two sample tests (footer.test.tsx and shortener.test.tsx)
This commit is contained in:
parent
d0c4fba792
commit
97ea40cb45
@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
@ -14,6 +14,7 @@
|
||||
"no-useless-return": "warn",
|
||||
"no-var": "warn",
|
||||
"no-console": "warn",
|
||||
"no-unused-vars": "off",
|
||||
"max-len": ["warn", { "comments": 80 }],
|
||||
"no-param-reassign": 0,
|
||||
"require-atomic-updates": 0,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Flex, BoxProps } from "reflexbox/styled-components";
|
||||
import styled, { css, keyframes } from "styled-components";
|
||||
import { withProp, prop, ifProp } from "styled-tools";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import { Flex } from "reflexbox/styled-components";
|
||||
import { FC } from "react";
|
||||
|
||||
|
@ -204,6 +204,7 @@ const Shortener = () => {
|
||||
placeholder="Paste your long URL"
|
||||
placeholderSize={[16, 17, 18]}
|
||||
fontSize={[18, 20, 22]}
|
||||
aria-label="target"
|
||||
width={1}
|
||||
height={[58, 64, 72]}
|
||||
px={0}
|
||||
@ -212,7 +213,7 @@ const Shortener = () => {
|
||||
autoFocus
|
||||
data-lpignore
|
||||
/>
|
||||
<SubmitIconWrapper onClick={onSubmit}>
|
||||
<SubmitIconWrapper onClick={onSubmit} role="button" aria-label="submit">
|
||||
<Icon
|
||||
name={loading ? "spinner" : "send"}
|
||||
size={[22, 26, 28]}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import { switchProp, ifNotProp, ifProp } from "styled-tools";
|
||||
import { Box, BoxProps } from "reflexbox/styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
|
52
client/components/__tests__/footer.test.tsx
Normal file
52
client/components/__tests__/footer.test.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { StoreProvider } from "easy-peasy";
|
||||
import { initializeStore } from "../../store";
|
||||
import Footer from "../Footer";
|
||||
import getConfig from "next/config";
|
||||
|
||||
describe("<Footer /> component test", () => {
|
||||
let app;
|
||||
|
||||
beforeEach(() => {
|
||||
const store = initializeStore();
|
||||
app = (
|
||||
<StoreProvider store={store}>
|
||||
<Footer />
|
||||
</StoreProvider>
|
||||
);
|
||||
});
|
||||
|
||||
it("should contain a github link", () => {
|
||||
const screen = render(app);
|
||||
const githubLink = screen.getByRole("link", { name: "GitHub" });
|
||||
expect(githubLink).toHaveAttribute("href", "https://github.com/thedevs-network/kutt");
|
||||
});
|
||||
|
||||
it("should contain a TOS link", () => {
|
||||
const config = getConfig();
|
||||
const screen = render(app);
|
||||
const tosLink = screen.getByRole("link", { name: "Terms of Service" });
|
||||
|
||||
expect(tosLink).toHaveAttribute("href", "/terms");
|
||||
});
|
||||
|
||||
it("should show contact email if defined", () => {
|
||||
const config = getConfig();
|
||||
config.publicRuntimeConfig.CONTACT_EMAIL = 'foobar';
|
||||
const screen = render(app);
|
||||
const emailLink = screen.getByRole("link", { name: "Contact us" });
|
||||
|
||||
expect(emailLink).toHaveAttribute("href", "mailto:foobar");
|
||||
});
|
||||
|
||||
it("should NOT show contact email if none is defined", () => {
|
||||
const config = getConfig();
|
||||
delete(config.publicRuntimeConfig.CONTACT_EMAIL);
|
||||
const screen = render(app);
|
||||
const emailLink= screen.queryByRole("link", { name: "Contact us" });
|
||||
|
||||
expect(emailLink).toBeNull();
|
||||
});
|
||||
})
|
||||
|
59
client/components/__tests__/shortener.test.tsx
Normal file
59
client/components/__tests__/shortener.test.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { StoreProvider, createStore, thunk } from "easy-peasy";
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { store } from "../../store";
|
||||
import Shortener from "../Shortener";
|
||||
|
||||
describe("<Shortener /> component test", () => {
|
||||
let app;
|
||||
|
||||
beforeEach(() => {
|
||||
store.links = {
|
||||
...store.links,
|
||||
submit: thunk(async (actions, payload) => {
|
||||
return {
|
||||
id: "0",
|
||||
address: "localhost:3000/foobar",
|
||||
banned: false,
|
||||
created_at: "now",
|
||||
link: "localhost:3000/foobar",
|
||||
target: "",
|
||||
updated_at: "now",
|
||||
visit_count: 0
|
||||
};
|
||||
})
|
||||
};
|
||||
const testStore = createStore(store);
|
||||
app = (
|
||||
<StoreProvider store={testStore}>
|
||||
<Shortener />
|
||||
</StoreProvider>
|
||||
);
|
||||
});
|
||||
|
||||
it("Should show the short URL", async () => {
|
||||
const screen = render(app);
|
||||
const urlInput = screen.getByRole("textbox", { name: "target" });
|
||||
userEvent.type(urlInput, "https://easy-peasy.now.sh/docs/api/thunk.html");
|
||||
const submitButton = screen.getByRole("button", { name: "submit" });
|
||||
userEvent.click(submitButton);
|
||||
const msg = await screen.findByText(/localhost:3000\/foobar/i);
|
||||
expect(msg).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Should empty target input", async () => {
|
||||
const screen = render(app);
|
||||
let urlInput: HTMLInputElement = screen.getByRole("textbox", {
|
||||
name: "target"
|
||||
}) as HTMLInputElement;
|
||||
userEvent.type(urlInput, "https://easy-peasy.now.sh/docs/api/thunk.html");
|
||||
const submitButton = screen.getByRole("button", { name: "submit" });
|
||||
userEvent.click(submitButton);
|
||||
await screen.findByText(/localhost:3000\/foobar/i);
|
||||
urlInput = screen.getByRole("textbox", {
|
||||
name: "target"
|
||||
}) as HTMLInputElement;
|
||||
expect(urlInput.value).toEqual("");
|
||||
});
|
||||
});
|
@ -26,7 +26,7 @@ const StatsPage: NextPage<Props> = ({ id }) => {
|
||||
const { isAuthenticated } = useStoreState(s => s.auth);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const [data, setData] = useState();
|
||||
const [data, setData] = useState<Record<string, any> | undefined>();
|
||||
const [period, setPeriod] = useState("lastDay");
|
||||
|
||||
const stats = data && data[period];
|
||||
|
@ -10,7 +10,7 @@ export interface StoreModel {
|
||||
links: Links;
|
||||
loading: Loading;
|
||||
settings: Settings;
|
||||
reset: Action;
|
||||
reset: Action<StoreModel>;
|
||||
}
|
||||
|
||||
let initState: any = {};
|
||||
|
5
jest-setup.ts
Normal file
5
jest-setup.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import "@testing-library/jest-dom";
|
||||
import nextConfig from "./next.config";
|
||||
|
||||
jest.mock('next/config', () => () => nextConfig);
|
||||
|
13
jest.config.js
Normal file
13
jest.config.js
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
|
||||
"preset": "ts-jest",
|
||||
"transform": {
|
||||
"^.+\\.js$": "babel-jest"
|
||||
},
|
||||
"testEnvironment": "jsdom",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"tsconfig": "<rootDir>/tsconfig.test.json"
|
||||
}
|
||||
}
|
||||
};
|
7613
package-lock.json
generated
7613
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -4,10 +4,10 @@
|
||||
"description": "Modern URL shortener.",
|
||||
"main": "./production-server/server.js",
|
||||
"scripts": {
|
||||
"test": "mocha --compilers js:@babel/register ./client/**/__test__/*.js",
|
||||
"test": "jest",
|
||||
"docker:build": "docker build -t kutt .",
|
||||
"docker:run": "docker run -p 3000:3000 --env-file .env -d kutt:latest",
|
||||
"dev": "npm run migrate && nodemon server/server.ts",
|
||||
"dev": "npm run migrate && NODE_ENV=development nodemon server/server.ts",
|
||||
"build": "rimraf production-server && tsc --project tsconfig.json && copyfiles -f \"server/mail/*.html\" production-server/mail && next build client/ ",
|
||||
"start": "npm run migrate && cross-env NODE_ENV=production node production-server/server.js",
|
||||
"migrate": "knex migrate:latest --env production",
|
||||
@ -47,7 +47,7 @@
|
||||
"cross-env": "^7.0.2",
|
||||
"date-fns": "^2.9.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"easy-peasy": "^3.3.0",
|
||||
"easy-peasy": "^5.0.3",
|
||||
"email-validator": "^1.2.3",
|
||||
"envalid": "^6.0.0",
|
||||
"express": "^4.17.1",
|
||||
@ -99,19 +99,24 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.3",
|
||||
"@babel/core": "^7.8.3",
|
||||
"@babel/core": "^7.12.17",
|
||||
"@babel/node": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"@babel/preset-env": "^7.12.17",
|
||||
"@babel/register": "^7.8.3",
|
||||
"@testing-library/jest-dom": "^5.11.9",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/body-parser": "^1.17.1",
|
||||
"@types/bull": "^3.12.0",
|
||||
"@types/chai": "^4.2.15",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/date-fns": "^2.6.0",
|
||||
"@types/dotenv": "^4.0.3",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/helmet": "0.0.38",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/jsonwebtoken": "^7.2.8",
|
||||
"@types/jwt-decode": "^2.2.1",
|
||||
"@types/mongodb": "^3.3.14",
|
||||
@ -128,16 +133,18 @@
|
||||
"@types/react-tooltip": "^3.11.0",
|
||||
"@types/redis": "^2.8.14",
|
||||
"@types/reflexbox": "^4.0.0",
|
||||
"@types/styled-components": "^4.1.8",
|
||||
"@typescript-eslint/eslint-plugin": "^2.16.0",
|
||||
"@typescript-eslint/parser": "^2.16.0",
|
||||
"@types/sinon": "^9.0.10",
|
||||
"@types/styled-components": "^5.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.2",
|
||||
"@typescript-eslint/parser": "^4.15.2",
|
||||
"babel": "^6.23.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.6",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-styled-components": "^1.10.6",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai": "^4.3.0",
|
||||
"copyfiles": "^2.2.0",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"eslint": "^5.16.0",
|
||||
@ -148,6 +155,7 @@
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "^7.18.0",
|
||||
"husky": "^0.15.0-rc.13",
|
||||
"jest": "^26.6.3",
|
||||
"mocha": "^5.2.0",
|
||||
"nock": "^9.3.3",
|
||||
"nodemon": "^1.19.4",
|
||||
@ -155,6 +163,8 @@
|
||||
"redoc": "^2.0.0-rc.20",
|
||||
"rimraf": "^3.0.0",
|
||||
"sinon": "^6.0.0",
|
||||
"typescript": "^3.7.5"
|
||||
"ts-jest": "^26.5.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ const postgres = knex({
|
||||
);
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
resolve(null);
|
||||
},
|
||||
onError(error) {
|
||||
session.close();
|
||||
|
17
tsconfig.test.json
Normal file
17
tsconfig.test.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"outDir": "production-server",
|
||||
"noUnusedLocals": false,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": false,
|
||||
"jsx": "react",
|
||||
"allowJs": true
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user