-
-
Notifications
You must be signed in to change notification settings - Fork 216
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #789 from jasperteo/feat-nanoid-validation
Added nanoid() validation action
- Loading branch information
Showing
27 changed files
with
421 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './nanoid.ts'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { describe, expectTypeOf, test } from 'vitest'; | ||
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts'; | ||
import { nanoid, type NanoIDAction, type NanoIDIssue } from './nanoid.ts'; | ||
|
||
describe('nanoid', () => { | ||
describe('should return action object', () => { | ||
test('with undefined message', () => { | ||
type Action = NanoIDAction<string, undefined>; | ||
expectTypeOf(nanoid<string>()).toEqualTypeOf<Action>(); | ||
expectTypeOf( | ||
nanoid<string, undefined>(undefined) | ||
).toEqualTypeOf<Action>(); | ||
}); | ||
|
||
test('with string message', () => { | ||
expectTypeOf(nanoid<string, 'message'>('message')).toEqualTypeOf< | ||
NanoIDAction<string, 'message'> | ||
>(); | ||
}); | ||
|
||
test('with function message', () => { | ||
expectTypeOf(nanoid<string, () => string>(() => 'message')).toEqualTypeOf< | ||
NanoIDAction<string, () => string> | ||
>(); | ||
}); | ||
}); | ||
|
||
describe('should infer correct types', () => { | ||
type Action = NanoIDAction<string, undefined>; | ||
|
||
test('of input', () => { | ||
expectTypeOf<InferInput<Action>>().toEqualTypeOf<string>(); | ||
}); | ||
|
||
test('of output', () => { | ||
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<string>(); | ||
}); | ||
|
||
test('of issue', () => { | ||
expectTypeOf<InferIssue<Action>>().toEqualTypeOf<NanoIDIssue<string>>(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { describe, expect, test } from 'vitest'; | ||
import { NANO_ID_REGEX } from '../../regex.ts'; | ||
import { expectActionIssue, expectNoActionIssue } from '../../vitest/index.ts'; | ||
import { nanoid, type NanoIDAction, type NanoIDIssue } from './nanoid.ts'; | ||
|
||
describe('nanoid', () => { | ||
describe('should return action object', () => { | ||
const baseAction: Omit<NanoIDAction<string, never>, 'message'> = { | ||
kind: 'validation', | ||
type: 'nanoid', | ||
reference: nanoid, | ||
expects: null, | ||
requirement: NANO_ID_REGEX, | ||
async: false, | ||
_run: expect.any(Function), | ||
}; | ||
|
||
test('with undefined message', () => { | ||
const action: NanoIDAction<string, undefined> = { | ||
...baseAction, | ||
message: undefined, | ||
}; | ||
expect(nanoid()).toStrictEqual(action); | ||
expect(nanoid(undefined)).toStrictEqual(action); | ||
}); | ||
|
||
test('with string message', () => { | ||
expect(nanoid('message')).toStrictEqual({ | ||
...baseAction, | ||
message: 'message', | ||
} satisfies NanoIDAction<string, string>); | ||
}); | ||
|
||
test('with function message', () => { | ||
const message = () => 'message'; | ||
expect(nanoid(message)).toStrictEqual({ | ||
...baseAction, | ||
message, | ||
} satisfies NanoIDAction<string, typeof message>); | ||
}); | ||
}); | ||
|
||
describe('should return dataset without issues', () => { | ||
const action = nanoid(); | ||
|
||
test('for untyped inputs', () => { | ||
expect(action._run({ typed: false, value: null }, {})).toStrictEqual({ | ||
typed: false, | ||
value: null, | ||
}); | ||
}); | ||
|
||
test('for normal Nano IDs', () => { | ||
expectNoActionIssue(action, [ | ||
'NOi6NWfhDRpgzBYFRR-uE', | ||
'D7j9AWMA6anLPDE2_2uHz', | ||
'g_Se_MXrTmRJpmcp8cN5m', | ||
'Oc0XNYtCgyrX-x2T33z3E', | ||
'gGCr-6yBmZkOTJQ1oLAFr', | ||
]); | ||
}); | ||
|
||
test('for single char', () => { | ||
expectNoActionIssue(action, ['a', 'z', 'A', 'Z', '0', '9', '_', '-']); | ||
}); | ||
|
||
test('for two chars', () => { | ||
expectNoActionIssue(action, ['aa', 'zz', 'AZ', '09', '_-', '9A']); | ||
}); | ||
|
||
test('for long IDs', () => { | ||
expectNoActionIssue(action, [ | ||
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-', | ||
]); | ||
}); | ||
}); | ||
|
||
describe('should return dataset with issues', () => { | ||
const action = nanoid('message'); | ||
const baseIssue: Omit<NanoIDIssue<string>, 'input' | 'received'> = { | ||
kind: 'validation', | ||
type: 'nanoid', | ||
expected: null, | ||
message: 'message', | ||
requirement: NANO_ID_REGEX, | ||
}; | ||
|
||
test('for empty strings', () => { | ||
expectActionIssue(action, baseIssue, ['', ' ', '\n']); | ||
}); | ||
|
||
test('for blank spaces', () => { | ||
expectActionIssue(action, baseIssue, [ | ||
' vM7SGqVFmPS5tw7fII-G', | ||
'BImGM 7USGakXaVhydHgO', | ||
'LBjowKnkbk95kK3IoUV7 ', | ||
]); | ||
}); | ||
|
||
test('for special chars', () => { | ||
expectActionIssue(action, baseIssue, [ | ||
'@1o5BK76uGc-mbqeprAvX', | ||
'#Lcb2qbTsjS98y9Vf-G15', | ||
'$3WZ4tXxsuiDBezXIJKlP', | ||
'%gSjBHLFDO67bE-nbgBRi', | ||
'&2zYmqr0APdImhdxC69t4', | ||
'–gGCr6yBmZkOTJQ1oLAFr', | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { NANO_ID_REGEX } from '../../regex.ts'; | ||
import type { | ||
BaseIssue, | ||
BaseValidation, | ||
Dataset, | ||
ErrorMessage, | ||
} from '../../types/index.ts'; | ||
import { _addIssue } from '../../utils/index.ts'; | ||
|
||
/** | ||
* Nano ID issue type. | ||
*/ | ||
export interface NanoIDIssue<TInput extends string> extends BaseIssue<TInput> { | ||
/** | ||
* The issue kind. | ||
*/ | ||
readonly kind: 'validation'; | ||
/** | ||
* The issue type. | ||
*/ | ||
readonly type: 'nanoid'; | ||
/** | ||
* The expected property. | ||
*/ | ||
readonly expected: null; | ||
/** | ||
* The received property. | ||
*/ | ||
readonly received: string; | ||
/** | ||
* The Nano ID regex. | ||
*/ | ||
readonly requirement: RegExp; | ||
} | ||
|
||
/** | ||
* Nano ID action type. | ||
*/ | ||
export interface NanoIDAction< | ||
TInput extends string, | ||
TMessage extends ErrorMessage<NanoIDIssue<TInput>> | undefined, | ||
> extends BaseValidation<TInput, TInput, NanoIDIssue<TInput>> { | ||
/** | ||
* The action type. | ||
*/ | ||
readonly type: 'nanoid'; | ||
/** | ||
* The action reference. | ||
*/ | ||
readonly reference: typeof nanoid; | ||
/** | ||
* The expected property. | ||
*/ | ||
readonly expects: null; | ||
/** | ||
* The Nano ID regex. | ||
*/ | ||
readonly requirement: RegExp; | ||
/** | ||
* The error message. | ||
*/ | ||
readonly message: TMessage; | ||
} | ||
|
||
/** | ||
* Creates a [Nano ID](https://github.com/ai/nanoid) validation action. | ||
* | ||
* @returns A Nano ID action. | ||
*/ | ||
export function nanoid<TInput extends string>(): NanoIDAction< | ||
TInput, | ||
undefined | ||
>; | ||
|
||
/** | ||
* Creates a [Nano ID](https://github.com/ai/nanoid) validation action. | ||
* | ||
* @param message The error message. | ||
* | ||
* @returns A Nano ID action. | ||
*/ | ||
export function nanoid< | ||
TInput extends string, | ||
const TMessage extends ErrorMessage<NanoIDIssue<TInput>> | undefined, | ||
>(message: TMessage): NanoIDAction<TInput, TMessage>; | ||
|
||
export function nanoid( | ||
message?: ErrorMessage<NanoIDIssue<string>> | ||
): NanoIDAction<string, ErrorMessage<NanoIDIssue<string>> | undefined> { | ||
return { | ||
kind: 'validation', | ||
type: 'nanoid', | ||
reference: nanoid, | ||
async: false, | ||
expects: null, | ||
requirement: NANO_ID_REGEX, | ||
message, | ||
_run(dataset, config) { | ||
if (dataset.typed && !this.requirement.test(dataset.value)) { | ||
_addIssue(this, 'Nano ID', dataset, config); | ||
} | ||
return dataset as Dataset<string, NanoIDIssue<string>>; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.