Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(date): add formatting to date time #20

Merged
merged 1 commit into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "index.d.ts",
"scripts": {
"test": "mocha -r ts-node/register test/**/*.test.ts",
"test:coverage": "nyc npm run test",
"test:coverage": "NODE_OPTIONS='--max-old-space-size=8192' nyc npm run test",
"test:watch": "nodemon -e '*' --watch test --watch src --exec npm run test:coverage",
"commit": "cz",
"feature-tests": "./node_modules/.bin/cucumber-js -p default",
Expand Down Expand Up @@ -66,6 +66,7 @@
},
"devDependencies": {
"@cucumber/cucumber": "^8.0.0-rc.1",
"@date-fns/utc": "^1.2.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/async-lock": "^1.1.3",
"@types/chai": "^4.2.22",
Expand Down Expand Up @@ -95,6 +96,7 @@
},
"dependencies": {
"async-lock": "^1.3.0",
"date-fns": "^3.6.0",
"get-random-values": "^1.2.2",
"lodash": "^4.17.21"
}
Expand Down
54 changes: 49 additions & 5 deletions src/properties.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import merge from 'lodash/merge'
import get from 'lodash/get'
import { formatDate } from 'date-fns/format'
import {
createPropertyValidator,
isType,
Expand Down Expand Up @@ -41,6 +42,8 @@ import {
const EMAIL_REGEX =
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/u

const DEFAULT_JUST_DATE_FORMAT = 'yyyy-MM-dd'

const Property = <
TValue extends Arrayable<FunctionalValue>,
T extends FunctionalModel = FunctionalModel,
Expand Down Expand Up @@ -128,20 +131,61 @@ const Property = <
return r
}

/**
* Config object for a date property
*/
type DatePropertyConfig<T> = PropertyConfig<T> & {
/**
* A date-fns format.
*/
format?: string
isDateOnly?: boolean
}

/**
* Determines if the value is a Date object.
* @param value Any value
*/
const isDate = (value: any): value is Date => {
if (value === null) {
return false
}
return typeof value === 'object' && value.toISOString
}

/**
* A Property for Dates. Both strings and Date objects.
* @param config
* @param additionalMetadata
* @constructor
*/
const DateProperty = <TModifier extends PropertyModifier<Date | string>>(
config: PropertyConfig<TModifier> = {},
config: DatePropertyConfig<TModifier> = {},
additionalMetadata = {}
) =>
Property<TModifier>(
PROPERTY_TYPES.DateProperty,
merge(
{
lazyLoadMethod: (value: Arrayable<FunctionalValue>) => {
if (!value && config?.autoNow) {
return new Date()
if (isDate(value)) {
if (config.format) {
return formatDate(value, config.format)
}
if (config.isDateOnly) {
return formatDate(value, DEFAULT_JUST_DATE_FORMAT)
}
return value.toISOString()
}
if (typeof value === 'string') {
return new Date(value)
if (!value && config?.autoNow) {
const date = new Date()
if (config.format) {
return formatDate(date, config.format)
}
if (config.isDateOnly) {
return formatDate(date, DEFAULT_JUST_DATE_FORMAT)
}
return date.toISOString()
}
return value
},
Expand Down
91 changes: 87 additions & 4 deletions test/src/properties.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assert } from 'chai'
import chai from 'chai'
import sinon from 'sinon'
import { UTCDate } from '@date-fns/utc'
import asPromised from 'chai-as-promised'
import {
UniqueId,
Expand Down Expand Up @@ -1176,7 +1177,7 @@ describe('/src/properties.ts', () => {
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = await instance()
const expected = date
const expected = date.toISOString()
assert.deepEqual(actual, expected)
})
it('should return null, if null config is passed and no value created.', async () => {
Expand All @@ -1192,18 +1193,100 @@ describe('/src/properties.ts', () => {
const expected = null
assert.deepEqual(actual, expected)
})
it('should return a Date object when a string date is passed in', async () => {
it('should return a string when a string date is passed in', async () => {
const proto = DateProperty({})
const date = '2020-01-01T00:00:01Z'
const date = '2020-01-01T00:00:01.000Z'
const instance = proto.createGetter(
date,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = ((await instance()) as Date).toISOString()
const actual = await instance()
const expected = new Date(date).toISOString()
assert.equal(actual, expected)
})
it('should return a string when a date object is passed in', async () => {
const proto = DateProperty({})
const date = new Date('2020-01-01T00:00:01.000Z')
const instance = proto.createGetter(
date,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = typeof (await instance())
const expected = 'string'
assert.equal(actual, expected)
})
it('should return a string when a date object is passed in', async () => {
const proto = DateProperty({})
const date = new Date('2020-01-01T00:00:01.000Z')
const instance = proto.createGetter(
date,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = typeof (await instance())
const expected = 'string'
assert.equal(actual, expected)
})
it('should return the expected string when a date object is passed in and format is provided', async () => {
const proto = DateProperty({
format: 'MMMM',
})
const date = new UTCDate('2020-01-01T00:00:01.000Z')
const instance = proto.createGetter(
date,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = await instance()
const expected = 'January'
assert.equal(actual, expected)
})
it('should return the expected date only string when a date object is passed in and isDateOnly:true', async () => {
const proto = DateProperty({
isDateOnly: true,
})
const date = new UTCDate('2020-01-01T00:00:01.000Z')
const instance = proto.createGetter(
date,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = await instance()
const expected = '2020-01-01'
assert.equal(actual, expected)
})
it('should a date string when autoNow, isDateOnly=true and no value is provided', async () => {
const proto = DateProperty({
isDateOnly: true,
autoNow: true,
})
const instance = proto.createGetter(
null,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = (await instance()) as string
const re = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g
const found = actual.match(re)
assert.isOk(found)
})
it('should formatted string when autoNow, format=yyyy and no value is provided', async () => {
const proto = DateProperty({
format: 'yyyy',
autoNow: true,
})
const instance = proto.createGetter(
null,
{},
{} as unknown as ModelInstance<FunctionalModel>
)
const actual = (await instance()) as string
const re = /[0-9]{4}/g
const found = actual.match(re)
assert.isOk(found)
})
})
describe('#AdvancedModelReferenceProperty()', () => {
it('should not throw an exception when a custom Model is passed in', () => {
Expand Down
Loading