diff --git a/.all-contributorsrc b/.all-contributorsrc index a03ede10..eafb828e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -277,7 +277,9 @@ "avatar_url": "https://avatars2.githubusercontent.com/u/980783?v=4", "profile": "http://foss-geek.blogspot.com/", "contributions": [ - "code" + "code", + "test", + "doc" ] }, { diff --git a/README.md b/README.md index 3b17b5c8..d8e8d1aa 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ If you've come here to help contribute - Thanks! Take a look at the [contributin * [.toBeFalse()](#tobefalse) * [Date](#date) * [.toBeDate()](#tobedate) + * [.toBeValidDate()](#tobevaliddate) * Further proposals in [#117](https://github.com/jest-community/jest-extended/issues/117) PRs welcome * [Function](#function) * [.toBeFunction()](#tobefunction) @@ -332,6 +333,20 @@ test('passes when value is a date', () => { }); ``` +### .toBeValidDate() + +Use `.toBeValidDate` when checking if a given `Date` object is valid. + +``` +test('passes when Date is valid', () => { + expect(new Date()).toBeValidDate(); + expect('01/01/2018').not.toBeValidDate(); + expect(new Date('01/01/2018').toBeValidDate(); + expect(new Date('01/90/2018').not.toBeValidDate(); + expect(undefined).not.toBeValidDate(); +}); +``` + ### Function #### .toBeFunction() diff --git a/src/matchers/index.js b/src/matchers/index.js index 6231572e..c5de2ca9 100644 --- a/src/matchers/index.js +++ b/src/matchers/index.js @@ -43,6 +43,7 @@ import toBeEmpty from './toBeEmpty'; import toBeSealed from './toBeSealed'; import toIncludeRepeated from './toIncludeRepeated'; import toHaveBeenCalledBefore from './toHaveBeenCalledBefore'; +import toBeValidDate from './toBeValidDate'; export default [ toBeEven, @@ -89,5 +90,6 @@ export default [ toBeSealed, toSatisfy, toIncludeRepeated, - toHaveBeenCalledBefore + toHaveBeenCalledBefore, + toBeValidDate ].reduce((acc, matcher) => ({ ...acc, ...matcher }), {}); diff --git a/src/matchers/toBeDate/index.test.js b/src/matchers/toBeDate/index.test.js index 19370a21..67d4bb8e 100644 --- a/src/matchers/toBeDate/index.test.js +++ b/src/matchers/toBeDate/index.test.js @@ -15,19 +15,12 @@ describe('.toBeDate', () => { }); describe('.not.toBeDate', () => { - each([ - [false], - [true], - [0], - [''], - [{}], - [() => {}], - [undefined], - [null], - [NaN] - ]).test('passes when not given a date: %s', given => { - expect(given).not.toBeDate(); - }); + each([[false], [true], [0], [''], [{}], [() => {}], [undefined], [null], [NaN]]).test( + 'passes when not given a date: %s', + given => { + expect(given).not.toBeDate(); + } + ); test('fails when given a date', () => { expect(() => expect(new Date('2018-01-01T13:00:00.000Z')).not.toBeDate()).toThrowErrorMatchingSnapshot(); diff --git a/src/matchers/toBeValidDate/__snapshots__/index.test.js.snap b/src/matchers/toBeValidDate/__snapshots__/index.test.js.snap new file mode 100644 index 00000000..c2dac601 --- /dev/null +++ b/src/matchers/toBeValidDate/__snapshots__/index.test.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`.not.toBeValidDate fails when not given a valid date value 1`] = ` +"expect(received).not.toBeValidDate() + +Expected value to not be a valid date received: + 2017-12-31T18:30:00.000Z" +`; + +exports[`.toBeValidDate fails when given an invalid date 1`] = `"Invalid time value"`; + +exports[`.toBeValidDate fails when not given non-date values 1`] = ` +"expect(received).toBeValidDate() + +Expected value to be a valid date received: + 1" +`; diff --git a/src/matchers/toBeValidDate/index.js b/src/matchers/toBeValidDate/index.js new file mode 100644 index 00000000..01c52566 --- /dev/null +++ b/src/matchers/toBeValidDate/index.js @@ -0,0 +1,26 @@ +import { matcherHint, printReceived } from 'jest-matcher-utils'; + +import predicate from './predicate'; + +const passMessage = received => () => + matcherHint('.not.toBeValidDate', 'received', '') + + '\n\n' + + 'Expected value to not be a valid date received:\n' + + ` ${printReceived(received)}`; + +const failMessage = received => () => + matcherHint('.toBeValidDate', 'received', '') + + '\n\n' + + 'Expected value to be a valid date received:\n' + + ` ${printReceived(received)}`; + +export default { + toBeValidDate: expected => { + const pass = predicate(expected); + if (pass) { + return { pass: true, message: passMessage(expected) }; + } + + return { pass: false, message: failMessage(expected) }; + } +}; diff --git a/src/matchers/toBeValidDate/index.test.js b/src/matchers/toBeValidDate/index.test.js new file mode 100644 index 00000000..a4233c25 --- /dev/null +++ b/src/matchers/toBeValidDate/index.test.js @@ -0,0 +1,41 @@ +import each from 'jest-each'; + +import matcher from './'; + +expect.extend(matcher); + +describe('.toBeValidDate', () => { + test('passes when given a valid date', () => { + expect(new Date()).toBeValidDate(); + }); + + test('fails when given an invalid date', () => { + expect(() => expect(new Date('01/90/2017')).toBeValidDate()).toThrowErrorMatchingSnapshot(); + }); + + test('fails when not given non-date values', () => { + expect(() => expect(1).toBeValidDate()).toThrowErrorMatchingSnapshot(); + }); +}); + +describe('.not.toBeValidDate', () => { + each([ + [new Date('01/90/2018')], + [new Date('32/01/2018')], + [false], + [true], + [0], + [''], + [{}], + [() => {}], + [undefined], + [null], + [NaN] + ]).test('passes when not given a date: %s', given => { + expect(given).not.toBeValidDate(); + }); + + test('fails when not given a valid date value', () => { + expect(() => expect(new Date('01/01/2018')).not.toBeValidDate()).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/src/matchers/toBeValidDate/predicate.js b/src/matchers/toBeValidDate/predicate.js new file mode 100644 index 00000000..f345f26c --- /dev/null +++ b/src/matchers/toBeValidDate/predicate.js @@ -0,0 +1,7 @@ +let is = type => value => Object.prototype.toString.call(value) === `[object ${type}]`; + +let hasDateType = is('Date'); + +let isValidDate = value => hasDateType(value) && !isNaN(value) && !isNaN(value.getTime()); + +export default expected => isValidDate(expected); diff --git a/src/matchers/toBeValidDate/predicate.test.js b/src/matchers/toBeValidDate/predicate.test.js new file mode 100644 index 00000000..e789dd62 --- /dev/null +++ b/src/matchers/toBeValidDate/predicate.test.js @@ -0,0 +1,24 @@ +import each from 'jest-each'; +import predicate from './predicate'; + +describe('toBeDate Predicate', () => { + test('returns true when given a valid date', () => { + expect(predicate(new Date('12/25/2017'))).toBe(true); + }); + + each([ + [new Date('01/90/2018')], + [new Date('32/01/2018')], + [true], + [false], + [''], + [0], + [{}], + [() => {}], + [undefined], + [null], + [NaN] + ]).test('returns false when given: %s', given => { + expect(predicate(given)).toBe(false); + }); +}); diff --git a/types/index.d.ts b/types/index.d.ts index 5ced45f1..89d8cd35 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -86,10 +86,15 @@ declare namespace jest { toBeFalse(): R; /** - * Use .toBeDate when checking if a value is a Date. + * Use `.toBeDate` when checking if a value is a `Date`. */ toBeDate(): R; + /** + * Use `.toBeValidDate` when checking if a value is a `valid Date`. + */ + toBeValidDate(): R; + /** * Use `.toBeFunction` when checking if a value is a `Function`. */ @@ -301,4 +306,3 @@ declare namespace jest { toIncludeMultiple(substring: string[]): R; } } -