Skip to content
Snippets Groups Projects
Commit 8fca4bec authored by Anthony REY's avatar Anthony REY
Browse files

Trip service kata TS

parent 9fc19c5c
No related branches found
No related tags found
1 merge request!48Resolve "Trip service (TypeScript)"
Showing
with 7006 additions and 0 deletions
......@@ -19,3 +19,4 @@ include:
- local: "/factory-patterns/.gitlab-ci.yml"
- local: "/exceptions/.gitlab-ci.yml"
- local: "/puissance-4/.gitlab-ci.yml"
- local: "/trip-service-kata-typescript/.gitlab-ci.yml"
......@@ -42,3 +42,4 @@ Ce dépôt Git a pour but de partager les différents ateliers pouvant être ré
| [Factory patterns](/factory-patterns) | Code&coffee | Facile |
| [Exceptions](/exceptions) | Code&coffee | Facile |
| [Puissance 4](/puissance-4) | Kata | Moyenne |
| [Trip service (TypeScript)](/trip-service-kata-typescript) | Kata | Moyenne |
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
],
plugins: ['prettier', '@typescript-eslint'],
rules: {
quotes: ['error', 'single', { avoidEscape: true }],
'comma-dangle': [
'error',
{
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'never',
},
],
'import/order': [
'error',
{
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
'newlines-between': 'always',
},
],
'prettier/prettier': [
'error',
{
singleQuote: true,
trailingComma: 'es5',
printWidth: 140,
},
],
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
'import/core-modules': ['sinon'],
},
parserOptions: {
parser: '@typescript-eslint/parser',
},
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/*.spec.{j,t}s?(x)'],
env: {
jest: true,
},
},
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'no-unused-vars': ['off'],
'no-undef': ['off'],
},
},
],
};
/.idea/
trip-service-kata-typescript:
variables:
PROJECT_FOLDER: "trip-service-kata-typescript"
TZ: "Europe/Paris"
extends: .node14
only:
refs:
- master
- merge_requests
changes:
- ".gitlab-common-ci.yml"
- "trip-service-kata-typescript/**/*"
A Typescript port of the Trip Service Kata.
* Requirements: Recent version of node.js (tested on 8.9.4).
* Install Dependencies: `npm install`
* Run tests with file watching for fast feedback: `npm test -- --watch`
* Coverage `npm test -- --coverage`
\ No newline at end of file
# Trip Service Kata (TypeScript)
Découverte et résolution du kata [TripService](https://github.com/sandromancuso/trip-service-kata)
- **Auteur** : Anthony REY et Colin DAMON
- **Date** : 22/10/2020
- **Langage** : TypeScript
- **Niveau** : Moyen
- **Replay** : [Trip Service Kata en TypeScript avec Anthony & Colin](https://www.youtube.com/watch?v=EJgCY6CxDq0)
## Kata
Kata for legacy code hands-on session. The objective is to test and refactor the legacy TripService class.
The end result should be well-crafted code that express the domain.
You can watch the video with my solution. Although quite long, I explain my whole thought process while writting tests, how I break dependencies, the reasons for refactoring and re-desining the code (tests and production code), and why certain steps are important. I also cover how often I commit and why I do it.
The video is full of tips and tricks that can be used in any language.
https://www.youtube.com/watch?v=_NnElPO5BU0
module.exports = {
roots: ['<rootDir>/test'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "tripservicekata",
"version": "1.0.0",
"description": "Kata for a legacy code hands-on session. The objective is to write tests and refactor the given legacy code.",
"main": "index.js",
"author": "Daniel Perez <danipq26@gmail.com>",
"dependencies": {
"@types/jest": "^26.0.14",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "^7.9.0",
"eslint-import-resolver-typescript": "^2.3.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^3.1.4",
"jest": "^26.4.2",
"prettier": "^2.1.2",
"sinon": "^9.2.0",
"ts-jest": "^26.3.0",
"typescript": "^4.0.2"
},
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll",
"eslint:ci": "eslint './**/*.{ts,js}'",
"eslint": "eslint './**/*.{ts,js}' --fix"
},
"jest": {
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(js?|ts?)$"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sandromancuso/trip-service-kata.git"
},
"keywords": [
"kata"
],
"bugs": {
"url": "https://github.com/sandromancuso/trip-service-kata/issues"
},
"homepage": "https://github.com/sandromancuso/trip-service-kata",
"devDependencies": {
"@types/sinon": "^9.0.8"
}
}
export default class CollaboratorCallException extends Error {}
export default class UserNotLoggedInException extends Error {}
export default class Trip {}
import CollaboratorCallException from '../exception/CollaboratorCallException';
import User from '../user/User';
import Trip from './Trip';
export default class TripDAO {
public static findTripsByUser(user: User): Trip[] {
throw new CollaboratorCallException('TripDAO should not be invoked on an unit test.');
}
}
import UserNotLoggedInException from '../exception/UserNotLoggedInException';
import User from '../user/User';
import userSession, { UserSession } from '../user/UserSession';
import Trip from './Trip';
import TripDAO from './TripDAO';
export default class TripService {
public getTripsByUser(user: User): Trip[] {
return this.getTripsByUserAndSession(user, userSession, TripDAO.findTripsByUser);
}
public getTripsByUserAndSession(user: User, session: UserSession, findTripsByUser: (user: User) => Trip[]): Trip[] {
const loggedUser: User = session.getLoggedUser();
this.assertLoggedUser(loggedUser);
return this.isFriend(user, loggedUser) ? findTripsByUser(user) : [];
}
private isFriend(user: User, loggedUser: User) {
return user.getFriends().some((friend) => friend === loggedUser);
}
assertLoggedUser(user: User): void {
if (user == null) {
throw new UserNotLoggedInException();
}
}
}
import Trip from '../trip/Trip';
export default class User {
private trips: Trip[] = [];
private friends: User[] = [];
public getFriends(): User[] {
return this.friends;
}
public addFriend(user: User): void {
this.friends.push(user);
}
public addTrip(trip: Trip): void {
this.trips.push(trip);
}
public getTrips(): Trip[] {
return this.trips;
}
}
import CollaboratorCallException from '../exception/CollaboratorCallException';
import User from './User';
export class UserSession {
public getLoggedUser(): User {
throw new CollaboratorCallException('UserSession.getLoggedUser() should not be called in an unit test');
}
}
export default new UserSession();
import * as sinon from 'sinon';
import UserNotLoggedInException from '../src/exception/UserNotLoggedInException';
import Trip from '../src/trip/Trip';
import TripService from '../src/trip/TripService';
import User from '../src/user/User';
import { UserSession } from '../src/user/UserSession';
interface UserSessionStub extends UserSession {
getLoggedUser: any;
}
let service: TripService;
const stubSession = (user?: User) => {
const session: UserSessionStub = {
getLoggedUser: sinon.stub().returns(user),
};
return session;
};
describe('TripServiceShould', () => {
beforeEach(() => {
service = new TripService();
});
it('should not get trips without connected user', () => {
expect(() => service.getTripsByUserAndSession(new User(), stubSession(), () => [])).toThrow(UserNotLoggedInException);
});
it('should get empty trips for newcomer', () => {
const trips = service.getTripsByUserAndSession(new User(), stubSession(new User()), () => []);
expect(trips).toHaveLength(0);
});
it('Should get user trips', () => {
const authenticatedUser = new User();
const user = new User();
user.addFriend(authenticatedUser);
const tripToLyon = new Trip();
authenticatedUser.addTrip(tripToLyon);
const findTripsByUser = sinon.mock().withExactArgs(user).returns([tripToLyon]);
const trips = service.getTripsByUserAndSession(user, stubSession(authenticatedUser), findTripsByUser);
const [trip] = trips;
expect(trips).toHaveLength(1);
expect(trip).toEqual(tripToLyon);
});
});
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "dist/main.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment