Skip to content

Commit

Permalink
💥 Include invalid dates by default (#4490)
Browse files Browse the repository at this point in the history
**💥 Breaking change**

In v3, `fc.date` was not producing "Invalid Date" values by default. One has to specify `noInvalidDate: false` to get some. With this PR we now default the flag to false when not specified: in other words, the new default will be to produce invalid dates except asked explicitly not to.

After that change, if you want not to produce "Invalid Date", you'll have to use:

```js
// in v3, it was only producing valid dates
fc.date()

// equivalent in v4
fc.date({noInvalidDate: true})

//---

// in v3, including invalid dates required users to pass an extra constraint to the arbitrary
fc.date({noInvalidDate: false})

// equivalent in v4
fc.date()
```

You can also prepare yourself to v4 by already toggling: `noInvalidDate: false` on your `fc.date`.
  • Loading branch information
dubzzz authored May 29, 2024
1 parent f27c9c4 commit 110814d
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 30 deletions.
4 changes: 2 additions & 2 deletions packages/fast-check/src/arbitrary/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface DateConstraints {
max?: Date;
/**
* When set to true, no more "Invalid Date" can be generated.
* @defaultValue true
* @defaultValue false
* @remarks Since 3.13.0
*/
noInvalidDate?: boolean;
Expand All @@ -48,7 +48,7 @@ export function date(constraints: DateConstraints = {}): Arbitrary<Date> {
// Date min and max in ECMAScript specification : https://stackoverflow.com/a/11526569/3707828
const intMin = constraints.min !== undefined ? safeGetTime(constraints.min) : -8640000000000000;
const intMax = constraints.max !== undefined ? safeGetTime(constraints.max) : 8640000000000000;
const noInvalidDate = constraints.noInvalidDate === undefined || constraints.noInvalidDate;
const noInvalidDate = constraints.noInvalidDate;
if (safeNumberIsNaN(intMin)) throw new Error('fc.date min must be valid instance of Date');
if (safeNumberIsNaN(intMax)) throw new Error('fc.date max must be valid instance of Date');
if (intMin > intMax) throw new Error('fc.date max must be greater or equal to min');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
describe('timeToDateUnmapper', () => {
it('should be able to revert any mapped date correctly even invalid ones', () => {
fc.assert(
fc.property(fc.date({ noInvalidDate: false }), (d) => {
fc.property(fc.date(), (d) => {
// Arrange / Act
const rev = timeToDateUnmapper(d);
const revRev = timeToDateMapper(rev);
Expand All @@ -22,26 +22,22 @@ describe('timeToDateUnmapper', () => {
});
});

describe('timeToDateUnmapperWithNane', () => {
describe('timeToDateUnmapperWithNaN', () => {
it('should be able to revert any mapped date correctly even invalid once', () => {
fc.assert(
fc.property(
fc.date({ noInvalidDate: false }),
fc.integer({ min: -8640000000000000, max: 8640000000000001 }),
(d, nanValue) => {
// Arrange / Act
const rev = timeToDateUnmapperWithNaN(nanValue)(d);
const revRev = timeToDateMapperWithNaN(nanValue)(rev);
fc.property(fc.date(), fc.integer({ min: -8640000000000000, max: 8640000000000001 }), (d, nanValue) => {
// Arrange / Act
const rev = timeToDateUnmapperWithNaN(nanValue)(d);
const revRev = timeToDateMapperWithNaN(nanValue)(rev);

// Assert
if (d.getTime() === nanValue) {
expect(rev).toBe(nanValue);
expect(revRev.getTime()).toEqual(Number.NaN);
} else {
expect(revRev.getTime()).toEqual(d.getTime());
}
},
),
// Assert
if (d.getTime() === nanValue) {
expect(rev).toBe(nanValue);
expect(revRev.getTime()).toEqual(Number.NaN);
} else {
expect(revRev.getTime()).toEqual(d.getTime());
}
}),
);
});
});
20 changes: 15 additions & 5 deletions packages/fast-check/test/unit/arbitrary/date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('date', () => {
fc.assert(
fc.property(constraintsArb(), (constraints) => {
// Arrange
const withInvalidDates = constraints.noInvalidDate === false;
const withInvalidDates = !constraints.noInvalidDate;
const { instance, map } = fakeArbitrary<number>();
const { instance: mappedInstance } = fakeArbitrary<Date>();
const integer = vi.spyOn(IntegerMock, 'integer');
Expand Down Expand Up @@ -115,7 +115,7 @@ describe('date', () => {
const d = mapper(rangeMin! + (mod % (rangeMax! - rangeMin! + 1))) as Date;

// Assert
if (constraints.noInvalidDate !== false || !Number.isNaN(d.getTime())) {
if (constraints.noInvalidDate || !Number.isNaN(d.getTime())) {
expect(d.getTime()).not.toBe(Number.NaN);
if (constraints.min) expect(d.getTime()).toBeGreaterThanOrEqual(constraints.min.getTime());
if (constraints.max) expect(d.getTime()).toBeLessThanOrEqual(constraints.max.getTime());
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('date (integration)', () => {
const extraParameters: fc.Arbitrary<Extra> = constraintsArb();

const isCorrect = (d: Date, extra: Extra) => {
if (extra.noInvalidDate || extra.noInvalidDate === undefined) {
if (extra.noInvalidDate) {
expect(d.getTime()).not.toBe(Number.NaN);
} else if (Number.isNaN(d.getTime())) {
return;
Expand Down Expand Up @@ -203,7 +203,13 @@ describe('date (integration)', () => {

function constraintsArb() {
return fc
.tuple(fc.date(), fc.date(), fc.boolean(), fc.boolean(), fc.option(fc.boolean(), { nil: undefined }))
.tuple(
fc.date({ noInvalidDate: true }),
fc.date({ noInvalidDate: true }),
fc.boolean(),
fc.boolean(),
fc.option(fc.boolean(), { nil: undefined }),
)
.map(([d1, d2, withMin, withMax, noInvalidDate]) => {
const min = d1 < d2 ? d1 : d2;
const max = d1 < d2 ? d2 : d1;
Expand All @@ -213,7 +219,11 @@ function constraintsArb() {

function invalidRangeConstraintsArb() {
return fc
.tuple(fc.date(), fc.date(), fc.option(fc.boolean(), { nil: undefined }))
.tuple(
fc.date({ noInvalidDate: true }),
fc.date({ noInvalidDate: true }),
fc.option(fc.boolean(), { nil: undefined }),
)
.filter(([d1, d2]) => +d1 !== +d2)
.map(([d1, d2, noInvalidDate]) => {
const min = d1 < d2 ? d1 : d2;
Expand Down
4 changes: 2 additions & 2 deletions website/docs/core-blocks/arbitraries/composites/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ fc.record(
);
// Note: All keys except 'id' will be optional values. id has been marked as required.
// Examples of generated values:
// • {"id":"46045be9-0009-4000-8000-0008ffffffed","name":"Karen","age":11,"birthday":new Date("2100-12-31T23:59:59.996Z")}
// • {"id":"46045be9-0009-4000-8000-0008ffffffed","name":"Karen","age":11,"birthday":new Date("2100-12-31T23:59:59.997Z")}
// • {"id":"fffffffe-0015-4000-95a0-f8e9ffffffe7","name":"Karen","birthday":new Date("1970-01-01T00:00:00.018Z")}
// • {"id":"e2b066ec-000b-4000-bfff-ffe7ccb1828d","name":"Karen","age":17}
// • {"id":"43b7d8e5-d043-42ef-8000-001a00000005","age":16,"birthday":new Date("2004-10-16T22:01:09.416Z")}
// • {"id":"00000007-2008-452e-8000-00133ed36be7","name":"Karen","age":6,"birthday":new Date("2100-12-31T23:59:59.981Z")}
// • {"id":"00000007-2008-452e-8000-00133ed36be7","name":"Karen","age":6,"birthday":new Date("2100-12-31T23:59:59.982Z")}
// • …

fc.record(
Expand Down
15 changes: 12 additions & 3 deletions website/docs/core-blocks/arbitraries/primitives/date.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Generate any possible dates in the specified range. Both the lower bound and upp

- `min?` — default: `new Date(-8640000000000000)`_lower bound of the range (included)_
- `max?` — default: `new Date(8640000000000000)`_upper bound of the range (included)_
- `noInvalidDate?` — default: `true`_when `true` the Date "Invalid Date" will never be defined_
- `noInvalidDate?` — default: `false`_when `true` the Date "Invalid Date" will never be defined_

**Usages:**

Expand All @@ -41,7 +41,7 @@ fc.date({ min: new Date('2000-01-01T00:00:00.000Z') });
// • new Date("2000-01-01T00:00:00.039Z")
// • new Date("2000-01-01T00:00:00.047Z")
// • new Date("2000-01-01T00:00:00.003Z")
// • new Date("+275760-09-12T23:59:59.981Z")
// • new Date("+275760-09-12T23:59:59.982Z")
// • …

fc.date({ max: new Date('2000-01-01T00:00:00.000Z') });
Expand All @@ -58,9 +58,18 @@ fc.date({ min: new Date('2000-01-01T00:00:00.000Z'), max: new Date('2000-12-31T2
// • new Date("2000-05-15T03:02:40.263Z")
// • new Date("2000-10-22T03:00:45.936Z")
// • new Date("2000-02-25T19:00:10.679Z")
// • new Date("2000-12-31T23:59:59.996Z")
// • new Date("2000-12-31T23:59:59.997Z")
// • new Date("2000-01-04T14:12:03.484Z")
// • …

fc.date({ noInvalidDate: true });
// Examples of generated values:
// • new Date("-043663-07-08T11:17:34.486Z")
// • new Date("-169183-12-11T00:28:46.358Z")
// • new Date("1969-12-31T23:59:59.988Z")
// • new Date("1969-12-31T23:59:59.984Z")
// • new Date("-271821-04-20T00:00:00.033Z")
// • …
```

Resources: [API reference](https://fast-check.dev/api-reference/functions/date.html).
Expand Down

0 comments on commit 110814d

Please sign in to comment.