From 0a3384ecf80c7b12f41eaf9d082de894a691fd1b Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Thu, 5 Oct 2023 12:29:15 +0200 Subject: [PATCH] add duration tokens to validation (#749) --- src/schemas/designToken.ts | 2 + src/schemas/durationToken.ts | 12 +++++ src/schemas/durationTokenSchema.test.ts | 58 +++++++++++++++++++++++++ src/schemas/durationValue.ts | 9 ++++ src/schemas/durationValueSchema.test.ts | 25 +++++++++++ src/schemas/schema.test.ts | 6 +++ src/schemas/validTokenType.ts | 1 + 7 files changed, 113 insertions(+) create mode 100644 src/schemas/durationToken.ts create mode 100644 src/schemas/durationTokenSchema.test.ts create mode 100644 src/schemas/durationValue.ts create mode 100644 src/schemas/durationValueSchema.test.ts diff --git a/src/schemas/designToken.ts b/src/schemas/designToken.ts index cc732e03a..a2f435e0f 100644 --- a/src/schemas/designToken.ts +++ b/src/schemas/designToken.ts @@ -10,6 +10,7 @@ import {dimensionToken} from './dimensionToken' import {colorToken} from './colorToken' import {fontFamilyToken} from './fontFamilyToken' import {shadowToken} from './shadowToken' +import {durationToken} from './durationToken' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: TODO: fix this @@ -27,6 +28,7 @@ export const designToken = z.record( typographyToken, viewportRangeToken, numberToken, + durationToken, stringToken, ]), // referenceToken, diff --git a/src/schemas/durationToken.ts b/src/schemas/durationToken.ts new file mode 100644 index 000000000..03e2a9a76 --- /dev/null +++ b/src/schemas/durationToken.ts @@ -0,0 +1,12 @@ +import {z} from 'zod' +import {baseToken} from './baseToken' +import {referenceValue} from './referenceValue' +import {durationValue} from './durationValue' +import {tokenType} from './tokenType' + +export const durationToken = baseToken + .extend({ + $value: z.union([durationValue, referenceValue]), + $type: tokenType('duration'), + }) + .strict() diff --git a/src/schemas/durationTokenSchema.test.ts b/src/schemas/durationTokenSchema.test.ts new file mode 100644 index 000000000..2fcdb87df --- /dev/null +++ b/src/schemas/durationTokenSchema.test.ts @@ -0,0 +1,58 @@ +import {durationToken} from './durationToken' + +describe('Schema: durationToken', () => { + const validToken = { + $value: '1000ms', + $type: 'duration', + $description: '1000 milliseconds', + } + + it('parses valid duration tokens', () => { + expect(durationToken.safeParse(validToken).success).toStrictEqual(true) + }) + + it('fails on invalid properties', () => { + // additional element + expect( + durationToken.safeParse({ + ...validToken, + duration: '1000s', + }).success, + ).toStrictEqual(false) + // missing value + expect( + durationToken.safeParse({ + $type: 'duration', + }).success, + ).toStrictEqual(false) + // missing type + expect( + durationToken.safeParse({ + $value: '1000ms', + }).success, + ).toStrictEqual(false) + }) + + it('fails on wrong type', () => { + // invalid string + expect( + durationToken.safeParse({ + ...validToken, + $type: 'motion', + }).success, + ).toStrictEqual(false) + // undefined + expect( + durationToken.safeParse({ + ...validToken, + $type: undefined, + }).success, + ).toStrictEqual(false) + // no type + expect( + durationToken.safeParse({ + $value: '1000ms', + }).success, + ).toStrictEqual(false) + }) +}) diff --git a/src/schemas/durationValue.ts b/src/schemas/durationValue.ts new file mode 100644 index 000000000..893c85cfd --- /dev/null +++ b/src/schemas/durationValue.ts @@ -0,0 +1,9 @@ +import {z} from 'zod' +import {schemaErrorMessage} from '../utilities/schemaErrorMessage' + +export const durationValue = z.string().refine( + duration => /(^[0-9]+ms$)/.test(duration), + val => ({ + message: schemaErrorMessage(`Invalid duration: "${val}"`, `A duration must be a string with an "ms"`), + }), +) diff --git a/src/schemas/durationValueSchema.test.ts b/src/schemas/durationValueSchema.test.ts new file mode 100644 index 000000000..1a11a2c56 --- /dev/null +++ b/src/schemas/durationValueSchema.test.ts @@ -0,0 +1,25 @@ +import {durationValue} from './durationValue' + +describe('Schema: durationValue', () => { + it('passes on valid duration values', () => { + expect(durationValue.safeParse('0ms').success).toStrictEqual(true) + expect(durationValue.safeParse('100ms').success).toStrictEqual(true) + expect(durationValue.safeParse('20000ms').success).toStrictEqual(true) + }) + + it('fails on invalid duration values', () => { + expect(durationValue.safeParse(-1).success).toStrictEqual(false) + expect(durationValue.safeParse(0).success).toStrictEqual(false) + expect(durationValue.safeParse(1.1).success).toStrictEqual(false) + expect(durationValue.safeParse('0').success).toStrictEqual(false) + expect(durationValue.safeParse('10').success).toStrictEqual(false) + expect(durationValue.safeParse('10s').success).toStrictEqual(false) + expect(durationValue.safeParse('').success).toStrictEqual(false) + }) + + it('fails on non-string values', () => { + expect(durationValue.safeParse(undefined).success).toStrictEqual(false) + expect(durationValue.safeParse(null).success).toStrictEqual(false) + expect(durationValue.safeParse(1).success).toStrictEqual(false) + }) +}) diff --git a/src/schemas/schema.test.ts b/src/schemas/schema.test.ts index 0d6b9ac3b..2ded99889 100644 --- a/src/schemas/schema.test.ts +++ b/src/schemas/schema.test.ts @@ -6,6 +6,12 @@ describe('Schema validation', () => { color: { $value: '#000000', $type: 'color', + $description: 'The color black', + }, + duration: { + $value: '1000ms', + $type: 'duration', + $description: '1000 milliseconds', }, }, } diff --git a/src/schemas/validTokenType.ts b/src/schemas/validTokenType.ts index b09c774b6..fd0f9be98 100644 --- a/src/schemas/validTokenType.ts +++ b/src/schemas/validTokenType.ts @@ -6,6 +6,7 @@ const validTypes = [ 'color', 'typography', 'dimension', + 'duration', 'border', 'duration', 'shadow',