Skip to content

Commit

Permalink
Add a linter, update codebase to confirm. Update readme. Updated depe…
Browse files Browse the repository at this point in the history
…ndencies
  • Loading branch information
Dean177 committed Jan 14, 2018
1 parent 581f228 commit 1531464
Show file tree
Hide file tree
Showing 14 changed files with 1,613 additions and 809 deletions.
40 changes: 40 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:7.10

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4

working_directory: ~/repo

steps:
- checkout

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

- run: yarn install

- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}

# run tests!
- run: yarn test



6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.idea
coverage
dist/
node_modules
publish
yarn-error.log
yarn-error.log
3 changes: 1 addition & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.idea
coverage
node_modules
src
yarn-error.log
yarn.lock

src
63 changes: 42 additions & 21 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# toMatchShapeOf

A jest matcher to verify the structure of an object, particularly useful for api integration tests
[![CircleCI](https://circleci.com/gh/Dean177/jest-to-match-shape-of.svg?style=svg)](https://circleci.com/gh/Dean177/jest-to-match-shape-of)
[![Greenkeeper badge](https://badges.greenkeeper.io/Dean177/jest-to-match-shape-of.svg)](https://greenkeeper.io/)
[![Npm](https://badge.fury.io/js/jest-to-match-shape-of.svg)](https://www.npmjs.com/package/jest-to-match-shape-of)


A [Jest matcher](https://facebook.github.io/jest/docs/en/using-matchers.html) to verify the structure of an object, particularly useful for api integration tests

## Installation

Expand All @@ -9,64 +14,80 @@ yarn add jest-to-match-shape-of
```

```bash
npm install jest-to-match-shape-of
npm install jest-to-match-shape-of --save
```

In your setupTestEnvironment.js
```javascript
// src/setupTestEnvironment.js
require('jest-to-match-shape-of');
require('jest-to-match-shape-of')
```
or if you are using typescript

```typescript
// src/setupTestEnvironment.ts
import 'jest-to-match-shape-of';
import 'jest-to-match-shape-of'
```

Then in the "jest" section of your package.json add the following:
`"setupTestFrameworkScriptFile": "<rootDir>/src/setupTestEnvironment.ts"`
`"setupTestFrameworkScriptFile": "<rootDir>/src/setupTestEnvironment.js"`

or for typescript:
`"setupTestFrameworkScriptFile": "<rootDir>/src/setupTestEnvironment.ts"`

## Usage

```
expect(someThing).toMatchOneOf([someOtherThingA, someOtherThingB, someOtherThingC]);
expect(someThing).toMatchShapeOf(someOtherThing);
expect(someThing).toMatchOneOf([someOtherThingA, someOtherThingB, someOtherThingC])
expect(someThing).toMatchShapeOf(someOtherThing)
```

Works particularly well when being used with typescript to write integration tests e.g.
Works particularly well when being used with [Typescript]() to write integration tests e.g.

```typescript
type Resource = {
someString: string,
maybeNumber: number | null,
};
someString: string,
}

const testResource: Resource = {
someString: 'some realish looking data',
maybeNumber: 6,
};
someString: 'some real looking data',
}

const testResource: Resource = {
someString: 'some realish looking data',
const testResourceAlt: Resource = {
maybeNumber: null,
};
someString: 'some real looking data',
}

describe('an api', () => {
it('returns what I was expecting', () => {
return fetch('/resources/1').then(response => response.json()).then((data) => {
expect(data).toMatchShapeOf(testResource);
});
});
expect(data).toMatchShapeOf(testResource)
})
})

it('could return a couple of different things', () => {
return fetch('/resources/1').then(response => response.json()).then((data) => {
expect(data).toMatchOneOf([testResource, testResourceAlt]);
});
expect(data).toMatchOneOf([testResource, testResourceAlt])
})
})
})

```
```

## Motivation

I wanted to write integration test for my frontend code but found it was tedious, brittle and
hard to debug when I encountered a legitimate failure.

I realised that
- Almost all of the errors were due to bad data from the API, most often missing data
- I did not care about *exactly* what data came back, but more about the *shape* of the data.
- Since I was using React and Typescript I could be confident my app would work as intended if the types were correct
- Thanks to [Enzyme](https://github.com/airbnb/enzyme) I already had a great way to test my component interactions

`toMatchShapeOf` hopefully achieves a lot of the value of full blown integration test written with something like
[Nightwatch](http://nightwatchjs.org/) whilst being simpler to write, understand and debug.

Additionally I found that the test data I created for use with this matcher were useful for other unit tests n my application.
30 changes: 16 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
{
"author": "Dean Merchant",
"dependencies": {
"chalk": "^2.0.1",
"lodash": "^4.17.4"
},
"description": "A jest matcher to verify the structure of an object, particularly useful for api integration tests",
"devDependencies": {
"@types/chalk": "0.4.31",
"@types/jest": "20.0.5",
"@types/lodash": "4.14.71",
"jest": "20.0.4",
"ts-jest": "20.0.7",
"typescript": "2.4.2"
"@types/chalk": "2.2.0",
"@types/jest": "22.0.1",
"@types/lodash": "4.14.92",
"jest": "22.0.6",
"ts-jest": "22.0.1",
"tslint": "5.9.1",
"tslint-react": "3.4.0",
"typescript": "2.6.2"
},
"files": [
"publish"
"dist"
],
"jest": {
"collectCoverageFrom": [
Expand Down Expand Up @@ -42,18 +43,19 @@
}
},
"license": "MIT",
"main": "publish/index.js",
"types": "publish/index.d.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"name": "jest-to-match-shape-of",
"peerDependencies": {
"jest": "20.x"
"jest": "22.x"
},
"repository": "[email protected]:Dean177/jest-to-match-shape-of.git",
"scripts": {
"dev": "tsc --watch",
"build": "rm -rf publish && tsc",
"build": "rm -rf dist && tsc",
"dev": "tsc --noEmit --pretty --watch",
"lint": "tslint ./src/**/*.ts",
"prepublish": "npm test && npm run build",
"test": "jest --coverage"
"test": "jest --watch"
},
"version": "0.1.3"
}
4 changes: 2 additions & 2 deletions src/common-types.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export type MatcherFactory = jasmine.CustomMatcherFactory;
export type Result = jasmine.CustomMatcherResult;
export type MatcherFactory = jasmine.CustomMatcherFactory
export type Result = jasmine.CustomMatcherResult
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { toMatchOneOf, toMatchShapeOf } from './toMatchShapeOf';
import { toMatchOneOf, toMatchShapeOf } from './toMatchShapeOf'

declare global {
namespace jest {
interface Matchers<R> {
toMatchOneOf(expected: Array<any>): R,
toMatchShapeOf(expected: any): R,
toMatchOneOf(expected: Array<any>): R, // tslint:disable-line:no-any
toMatchShapeOf(expected: any): R, // tslint:disable-line:no-any
}
}
}

jasmine.addMatchers({
toMatchOneOf,
toMatchShapeOf,
});
})
44 changes: 22 additions & 22 deletions src/toBeTypeOf.test.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import { toBeTypeOfCompare } from './toBeTypeOf';
import { toBeTypeOfCompare } from './toBeTypeOf'

describe('toBeTypeOf', () => {
it('Returns no error when the types match', () => {
expect(toBeTypeOfCompare(false, true).pass).toBeTruthy();
expect(toBeTypeOfCompare(false, true).pass).toBe(true)

expect(toBeTypeOfCompare(1, 0).pass).toBeTruthy();
expect(toBeTypeOfCompare(1, 0).pass).toBe(true)

expect(toBeTypeOfCompare('a', 'b').pass).toBeTruthy();
expect(toBeTypeOfCompare('a', '').pass).toBeTruthy();
expect(toBeTypeOfCompare('a', 'b').pass).toBe(true)
expect(toBeTypeOfCompare('a', '').pass).toBe(true)

expect(toBeTypeOfCompare([1, 2, 3], [1, 2, 3]).pass).toBeTruthy();
expect(toBeTypeOfCompare([1, 2, 3], []).pass).toBeTruthy();
expect(toBeTypeOfCompare([1, 2, 3], ['a', 'b', 'c']).pass).toBeTruthy();
expect(toBeTypeOfCompare([1, 2, 3], [1, 2, 3]).pass).toBe(true)
expect(toBeTypeOfCompare([1, 2, 3], []).pass).toBe(true)
expect(toBeTypeOfCompare([1, 2, 3], ['a', 'b', 'c']).pass).toBe(true)

expect(toBeTypeOfCompare(() => {}, () => {}).pass).toBeTruthy();
expect(toBeTypeOfCompare(() => {}, function() {}).pass).toBeTruthy();
expect(toBeTypeOfCompare(() => true, () => false).pass).toBe(true)
expect(toBeTypeOfCompare(() => 6, function() { return 7 }).pass).toBe(true)

expect(toBeTypeOfCompare(null, null).pass).toBeTruthy();
expect(toBeTypeOfCompare(null, null).pass).toBe(true)

expect(toBeTypeOfCompare(undefined, undefined).pass).toBeTruthy();
});
expect(toBeTypeOfCompare(undefined, undefined).pass).toBe(true)
})

it(`fails when the types don't match`, () => {
expect(toBeTypeOfCompare('a', false).pass).toBeFalsy();
expect(toBeTypeOfCompare(1, 'b').pass).toBeFalsy();
expect(toBeTypeOfCompare('a', []).pass).toBeFalsy();
expect(toBeTypeOfCompare(null, 1).pass).toBeFalsy();
expect(toBeTypeOfCompare(null, () => {}).pass).toBeFalsy();

expect(toBeTypeOfCompare({}, []).pass).toBeFalsy();
});
});
expect(toBeTypeOfCompare('a', false).pass).toBe(false)
expect(toBeTypeOfCompare(1, 'b').pass).toBe(false)
expect(toBeTypeOfCompare('a', []).pass).toBe(false)
expect(toBeTypeOfCompare(null, 1).pass).toBe(false)
expect(toBeTypeOfCompare(null, () => null).pass).toBe(false)

expect(toBeTypeOfCompare({}, []).pass).toBe(false)
})
})
30 changes: 18 additions & 12 deletions src/toBeTypeOf.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import { green, red } from 'chalk';
import { Result } from './common-types';
import chalk from 'chalk'
import { Result } from './common-types'

const { green, red } = chalk

// tslint:disable-next-line:no-any
export const toBeTypeOfCompare = (actual: any, expected: any): Result => {
if (actual == null && expected != null) {
return {
message: `expected '${green(typeof expected)}' but got '${red(actual)}'`,
pass: false,
};
}
}

if (Array.isArray(expected)) {
const pass = Array.isArray(actual);
const message = (!pass) ?
`expected '${green('array')}', but was '${red(typeof actual)}'` :
'';
return { message, pass };
const hasPassed = Array.isArray(actual)
return {
message: (!hasPassed)
? `expected '${green('array')}', but was '${red(typeof actual)}'`
: '',
pass: hasPassed,
}
}

const pass = typeof actual === typeof expected;
const pass = typeof actual === typeof expected
const message = (!pass) ?
`expected '${green(typeof expected)}', but was '${red(typeof actual)}' for ${JSON.stringify(actual)}` :
'';
return { message, pass };
};
''

return { message, pass }
}
Loading

0 comments on commit 1531464

Please sign in to comment.