mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Added with-jest-typescript example (#4124)
* Added with-jest-typescript example with files and readme * Updated package.json As per the comment, updated the package.json so the info is same as in other examples. * Proper name in package.json
This commit is contained in:
parent
f6949349c1
commit
c5c5564dbf
44
examples/with-jest-typescript/README.md
Normal file
44
examples/with-jest-typescript/README.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Example app with Jest tests inside a NextJS TypeScript app
|
||||
|
||||
## How to use
|
||||
|
||||
### Using `create-next-app`
|
||||
|
||||
Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example with-jest-typescript with-jest-typescript-app
|
||||
# or
|
||||
yarn create next-app --example with-jest-typescript with-jest-typescript-app
|
||||
```
|
||||
|
||||
### Download manually
|
||||
|
||||
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||
|
||||
```bash
|
||||
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-jest-typescript
|
||||
cd with-jest-typescript
|
||||
```
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
# or
|
||||
yarn
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Run Jest tests
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
# or
|
||||
yarn test
|
||||
```
|
||||
|
||||
## The idea behind the example
|
||||
|
||||
This example shows a configuration and several examples for a running Jest tests in a NextJS TypeScript app
|
21
examples/with-jest-typescript/jest.config.js
Normal file
21
examples/with-jest-typescript/jest.config.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
const TEST_REGEX = '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|js?|tsx?|ts?)$'
|
||||
|
||||
module.exports = {
|
||||
setupFiles: ['<rootDir>/jest.setup.js'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
'useBabelrc': true
|
||||
}
|
||||
},
|
||||
testRegex: TEST_REGEX,
|
||||
transform: {
|
||||
'^.+\\.tsx?$': 'ts-jest'
|
||||
},
|
||||
testPathIgnorePatterns: [
|
||||
'<rootDir>/.next/', '<rootDir>/node_modules/'
|
||||
],
|
||||
moduleFileExtensions: [
|
||||
'ts', 'tsx', 'js', 'jsx'
|
||||
],
|
||||
collectCoverage: true
|
||||
}
|
4
examples/with-jest-typescript/jest.setup.js
Normal file
4
examples/with-jest-typescript/jest.setup.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
const Enzyme = require('enzyme')
|
||||
const Adapter = require('enzyme-adapter-react-16')
|
||||
|
||||
Enzyme.configure({adapter: new Adapter()})
|
2
examples/with-jest-typescript/next.config.js
Normal file
2
examples/with-jest-typescript/next.config.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const withTypescript = require('@zeit/next-typescript')
|
||||
module.exports = withTypescript()
|
29
examples/with-jest-typescript/package.json
Normal file
29
examples/with-jest-typescript/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "with-jest-typescript",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"test": "NODE_ENV=test jest",
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^5.0.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^22.2.2",
|
||||
"@types/next": "^2.4.8",
|
||||
"@types/react": "^16.0.41",
|
||||
"@types/react-dom": "^16.0.4",
|
||||
"@zeit/next-typescript": "^0.0.11",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"jest": "^22.4.3",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"ts-jest": "^22.4.2",
|
||||
"typescript": "^2.7.2"
|
||||
}
|
||||
}
|
5
examples/with-jest-typescript/pages/cars.tsx
Normal file
5
examples/with-jest-typescript/pages/cars.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import CarsOverview from './../src/modules/cars/Overview';
|
||||
|
||||
const CarsPage = () => <CarsOverview/>;
|
||||
|
||||
export default CarsPage;
|
3
examples/with-jest-typescript/pages/index.tsx
Normal file
3
examples/with-jest-typescript/pages/index.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
const IndexPage = () => <h1>Testing Next.js App written in TypeScript with Jest</h1>;
|
||||
|
||||
export default IndexPage;
|
5
examples/with-jest-typescript/pages/login.tsx
Normal file
5
examples/with-jest-typescript/pages/login.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Login from './../src/modules/auth/Login';
|
||||
|
||||
const LoginPage = () => <Login/>;
|
||||
|
||||
export default LoginPage;
|
|
@ -0,0 +1,20 @@
|
|||
import * as React from 'react';
|
||||
|
||||
interface NiceCheckboxProps {
|
||||
rootID : string;
|
||||
id : string;
|
||||
name : string;
|
||||
value : string;
|
||||
label : string;
|
||||
}
|
||||
|
||||
const NiceCheckbox : React.SFC < NiceCheckboxProps > = (props : NiceCheckboxProps) : JSX.Element => {
|
||||
return (
|
||||
<div className="NiceCheckbox" id={props.rootID}>
|
||||
<input type="checkbox" id={props.id} name={props.name} value={props.value}/>
|
||||
<label htmlFor={props.id}>{props.label}</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NiceCheckbox;
|
|
@ -0,0 +1,17 @@
|
|||
/* eslint-env jest */
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import NiceCheckbox from './index';
|
||||
|
||||
describe('NiceCheckbox', () => {
|
||||
it('renders the checkbox with correct label', () => {
|
||||
const wrapper = shallow(<NiceCheckbox
|
||||
rootID="NiceCarCheckbox"
|
||||
id="CarAvailability"
|
||||
name="is_available"
|
||||
value="yes"
|
||||
label="Is this car available?"/>);
|
||||
expect(wrapper.find('#NiceCarCheckbox').find('label').text()).toEqual('Is this car available?');
|
||||
});
|
||||
});
|
70
examples/with-jest-typescript/src/modules/auth/Login.tsx
Normal file
70
examples/with-jest-typescript/src/modules/auth/Login.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import React, {ChangeEvent} from 'react';
|
||||
import Router from 'next/router';
|
||||
|
||||
import * as T from './types';
|
||||
|
||||
export interface LoginProps {}
|
||||
|
||||
export interface LoginState {
|
||||
credentials : T.LoginCredentials;
|
||||
isLoginLoading : boolean;
|
||||
}
|
||||
|
||||
export default class Login extends React.Component < LoginProps,
|
||||
LoginState > {
|
||||
constructor(props : LoginProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isLoginLoading: false,
|
||||
credentials: {
|
||||
email: null,
|
||||
password: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCredentialsChange = (e : ChangeEvent < HTMLInputElement >) => {
|
||||
let {credentials} = this.state;
|
||||
credentials[e.target.name] = e.target.value;
|
||||
|
||||
this.setState({credentials});
|
||||
}
|
||||
|
||||
handleLoginSubmit = (e : React.MouseEvent < HTMLElement >) => {
|
||||
e.preventDefault();
|
||||
this.setState({isLoginLoading: true});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({isLoginLoading: false});
|
||||
Router.replace('/cars');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {credentials} = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
<form>
|
||||
<input
|
||||
id="formEmail"
|
||||
name="email"
|
||||
type="text"
|
||||
value={credentials.email}
|
||||
onChange={this.handleCredentialsChange}/>
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
value={credentials.password}
|
||||
onChange={this.handleCredentialsChange}/>
|
||||
|
||||
<button id="loginSubmit" onClick={this.handleLoginSubmit}>{this.state.isLoginLoading
|
||||
? 'Logging in...'
|
||||
: 'Log in'}</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* eslint-env jest */
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import Login from './../Login';
|
||||
|
||||
describe('Login', () => {
|
||||
it('renders the h1 title', () => {
|
||||
const login = shallow(<Login/>);
|
||||
expect(login.find('h1').text()).toEqual('Login');
|
||||
});
|
||||
|
||||
it('renders the form', () => {
|
||||
const login = shallow(<Login/>);
|
||||
expect(login.find('form')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('changes the text of email', () => {
|
||||
const login = shallow(<Login/>);
|
||||
login
|
||||
.find('#formEmail')
|
||||
.simulate('change', {
|
||||
target: {
|
||||
name: 'email',
|
||||
value: 'some@test.com'
|
||||
}
|
||||
});
|
||||
expect(login.update().find('#formEmail').props().value).toEqual('some@test.com');
|
||||
});
|
||||
|
||||
it('changes the text of login button after clicking it', () => {
|
||||
const login = shallow(<Login/>);
|
||||
login
|
||||
.find('#loginSubmit')
|
||||
.simulate('click', {preventDefault() {}});
|
||||
expect(login.update().find('#loginSubmit').text()).toEqual('Logging in...');
|
||||
});
|
||||
});
|
4
examples/with-jest-typescript/src/modules/auth/types.ts
Normal file
4
examples/with-jest-typescript/src/modules/auth/types.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface LoginCredentials {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
25
examples/with-jest-typescript/src/modules/cars/Detail.tsx
Normal file
25
examples/with-jest-typescript/src/modules/cars/Detail.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import * as T from './types';
|
||||
|
||||
interface DetailProps {
|
||||
car : T.Car;
|
||||
}
|
||||
|
||||
const Detail : React.SFC < DetailProps > = ({car} : DetailProps) => {
|
||||
return (
|
||||
<div className="CarDetail">
|
||||
<h1>{`${car.make} ${car.model}`}</h1>
|
||||
<p>Engine : {car.engine}</p>
|
||||
<p>Year : {car.year}</p>
|
||||
<p>Mileage : {car.mileage}</p>
|
||||
<p>Equipment :
|
||||
</p>
|
||||
<ul>{car.equipment && car
|
||||
.equipment
|
||||
.map((e : string, index : number) => <li key={index}>{e}</li>)}</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Detail;
|
65
examples/with-jest-typescript/src/modules/cars/Overview.tsx
Normal file
65
examples/with-jest-typescript/src/modules/cars/Overview.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import * as T from './types';
|
||||
|
||||
export interface CarsOverviewProps {
|
||||
cars : T.CarList;
|
||||
}
|
||||
|
||||
export interface CarsOverviewState {
|
||||
selectedCar : T.Car;
|
||||
}
|
||||
|
||||
export default class CarsOverview extends React.Component < CarsOverviewProps,
|
||||
CarsOverviewState > {
|
||||
constructor(props : CarsOverviewProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedCar: null
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectCar = (car : T.Car) : void => {
|
||||
this.setState({selectedCar: car});
|
||||
}
|
||||
|
||||
renderCarsList = (cars : T.CarList) : JSX.Element => {
|
||||
if (!cars || cars.length === 0) {
|
||||
return (
|
||||
<p>No cars</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ul>{cars.map((car : T.Car, index : number) : JSX.Element => <li key={index} onClick={() => this.handleSelectCar(car)}>{car.make} {car.model}</li>)}</ul>
|
||||
);
|
||||
}
|
||||
|
||||
renderCarInfo = (car : T.Car) : JSX.Element => {
|
||||
if (!car) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="CarInfo">
|
||||
<h2>{`${car.make} ${car.model}`}</h2>
|
||||
<section>{car.engine}</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Cars Overview</h1>
|
||||
|
||||
<div className="Cars__List">
|
||||
{this.renderCarsList(this.props.cars)}
|
||||
</div>
|
||||
|
||||
{this.renderCarInfo(this.state.selectedCar)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* eslint-env jest */
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import Detail from './../Detail';
|
||||
|
||||
const __CAR__ = {
|
||||
make: 'Volvo',
|
||||
model: 'XC60',
|
||||
engine: 'T5',
|
||||
year: 2018,
|
||||
mileage: 123,
|
||||
equipment: ['Leather', 'Seat heating', 'City Safety']
|
||||
};
|
||||
|
||||
describe('Car detail', () => {
|
||||
it('renders correct title', () => {
|
||||
const detail = shallow(<Detail car={__CAR__}/>);
|
||||
expect(detail.find('h1').text()).toEqual('Volvo XC60');
|
||||
});
|
||||
|
||||
it('renders correct list item', () => {
|
||||
const detail = shallow(<Detail car={__CAR__}/>);
|
||||
expect(detail.childAt(1).text()).toEqual('Engine : T5');
|
||||
});
|
||||
|
||||
it('renders correct equipment list item', () => {
|
||||
const detail = shallow(<Detail car={__CAR__}/>);
|
||||
expect(detail.children().find('ul').childAt(1).text()).toEqual('Seat heating');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/* eslint-env jest */
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import Overview from './../Overview';
|
||||
|
||||
const __CARS__ = [
|
||||
{
|
||||
make: 'Volvo',
|
||||
model: 'C30',
|
||||
engine: 'T5',
|
||||
year: 2018,
|
||||
mileage: 123,
|
||||
equipment: ['Leather', 'Seat heating', 'City Safety']
|
||||
}, {
|
||||
make: 'Volvo',
|
||||
model: 'XC60',
|
||||
engine: 'D5',
|
||||
year: 2018,
|
||||
mileage: 456,
|
||||
equipment: ['Leather', 'Seat heating', 'City Safety']
|
||||
}, {
|
||||
make: 'Volvo',
|
||||
model: 'XC90',
|
||||
engine: 'T6',
|
||||
year: 2018,
|
||||
mileage: 789,
|
||||
equipment: ['Leather', 'Seat heating', 'City Safety']
|
||||
}
|
||||
];
|
||||
|
||||
describe('Cars overview', () => {
|
||||
it('renders the h1 title', () => {
|
||||
const overview = shallow(<Overview cars={[]}/>);
|
||||
expect(overview.find('h1').text()).toEqual('Cars Overview');
|
||||
});
|
||||
|
||||
it('renders empty cars list when no cars are provided', () => {
|
||||
const overview = shallow(<Overview cars={[]}/>);
|
||||
expect(overview.find('.Cars__List').children().find('p').text()).toEqual('No cars');
|
||||
});
|
||||
|
||||
it('renders cars list with 3 items when 3 cars are provided', () => {
|
||||
const overview = shallow(<Overview cars={__CARS__}/>);
|
||||
expect(overview.find('.Cars__List').children().find('ul').children()).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('renders cars list with the expected item on third place', () => {
|
||||
const overview = shallow(<Overview cars={__CARS__}/>);
|
||||
expect(overview.find('.Cars__List').children().find('ul').childAt(2).text()).toEqual('Volvo XC90');
|
||||
});
|
||||
|
||||
it('renders car detail after clicking on an item in cars list', () => {
|
||||
const overview = shallow(<Overview cars={__CARS__}/>);
|
||||
overview
|
||||
.find('.Cars__List')
|
||||
.children()
|
||||
.find('ul')
|
||||
.childAt(1)
|
||||
.simulate('click', {preventDefault() {}});
|
||||
|
||||
expect(overview.update().find('.CarInfo').find('h2').text()).toEqual('Volvo XC60');
|
||||
});
|
||||
});
|
10
examples/with-jest-typescript/src/modules/cars/types.ts
Normal file
10
examples/with-jest-typescript/src/modules/cars/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export interface Car {
|
||||
make : string;
|
||||
model : string;
|
||||
engine : string;
|
||||
year : number;
|
||||
mileage : number;
|
||||
equipment : string[];
|
||||
}
|
||||
|
||||
export type CarList = Array < Car >;
|
33
examples/with-jest-typescript/tsconfig.json
Normal file
33
examples/with-jest-typescript/tsconfig.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"jsx": "preserve",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2016"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx",
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue