-
-
Notifications
You must be signed in to change notification settings - Fork 189
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 #853 from remonke/grapheme-count-validators
Add `*graphemes` validation actions.
- Loading branch information
Showing
65 changed files
with
2,850 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { describe, expectTypeOf, test } from 'vitest'; | ||
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts'; | ||
import { | ||
graphemes, | ||
type GraphemesAction, | ||
type GraphemesIssue, | ||
} from './graphemes.ts'; | ||
|
||
describe('graphemes', () => { | ||
describe('should return action object', () => { | ||
test('with undefined message', () => { | ||
type Action = GraphemesAction<string, 10, undefined>; | ||
expectTypeOf(graphemes<string, 10>(10)).toEqualTypeOf<Action>(); | ||
expectTypeOf( | ||
graphemes<string, 10, undefined>(10, undefined) | ||
).toEqualTypeOf<Action>(); | ||
}); | ||
|
||
test('with string message', () => { | ||
expectTypeOf( | ||
graphemes<string, 10, 'message'>(10, 'message') | ||
).toEqualTypeOf<GraphemesAction<string, 10, 'message'>>(); | ||
}); | ||
|
||
test('with function message', () => { | ||
expectTypeOf( | ||
graphemes<string, 10, () => string>(10, () => 'message') | ||
).toEqualTypeOf<GraphemesAction<string, 10, () => string>>(); | ||
}); | ||
}); | ||
|
||
describe('should infer correct types', () => { | ||
type Input = 'example string'; | ||
type Action = GraphemesAction<Input, 5, undefined>; | ||
|
||
test('of input', () => { | ||
expectTypeOf<InferInput<Action>>().toEqualTypeOf<Input>(); | ||
}); | ||
|
||
test('of output', () => { | ||
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<Input>(); | ||
}); | ||
|
||
test('of issue', () => { | ||
expectTypeOf<InferIssue<Action>>().toEqualTypeOf< | ||
GraphemesIssue<Input, 5> | ||
>(); | ||
}); | ||
}); | ||
}); |
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,108 @@ | ||
import { describe, expect, test } from 'vitest'; | ||
import type { StringIssue } from '../../schemas/index.ts'; | ||
import { _getGraphemeCount } from '../../utils/index.ts'; | ||
import { expectActionIssue, expectNoActionIssue } from '../../vitest/index.ts'; | ||
import { | ||
graphemes, | ||
type GraphemesAction, | ||
type GraphemesIssue, | ||
} from './graphemes.ts'; | ||
|
||
describe('graphemes', () => { | ||
describe('should return action object', () => { | ||
const baseAction: Omit<GraphemesAction<string, 5, never>, 'message'> = { | ||
kind: 'validation', | ||
type: 'graphemes', | ||
reference: graphemes, | ||
expects: '5', | ||
requirement: 5, | ||
async: false, | ||
'~validate': expect.any(Function), | ||
}; | ||
|
||
test('with undefined message', () => { | ||
const action: GraphemesAction<string, 5, undefined> = { | ||
...baseAction, | ||
message: undefined, | ||
}; | ||
expect(graphemes(5)).toStrictEqual(action); | ||
expect(graphemes(5, undefined)).toStrictEqual(action); | ||
}); | ||
|
||
test('with string message', () => { | ||
expect(graphemes(5, 'message')).toStrictEqual({ | ||
...baseAction, | ||
message: 'message', | ||
} satisfies GraphemesAction<string, 5, string>); | ||
}); | ||
|
||
test('with function message', () => { | ||
const message = () => 'message'; | ||
expect(graphemes(5, message)).toStrictEqual({ | ||
...baseAction, | ||
message, | ||
} satisfies GraphemesAction<string, 5, typeof message>); | ||
}); | ||
}); | ||
|
||
describe('should return dataset without issues', () => { | ||
const action = graphemes(5); | ||
|
||
test('for untyped inputs', () => { | ||
const issues: [StringIssue] = [ | ||
{ | ||
kind: 'schema', | ||
type: 'string', | ||
input: null, | ||
expected: 'string', | ||
received: 'null', | ||
message: 'message', | ||
}, | ||
]; | ||
expect( | ||
action['~validate']({ typed: false, value: null, issues }, {}) | ||
).toStrictEqual({ | ||
typed: false, | ||
value: null, | ||
issues, | ||
}); | ||
}); | ||
|
||
test('for valid strings', () => { | ||
expectNoActionIssue(action, ['12345', '12 45', '1234 ', 'hello']); | ||
}); | ||
|
||
test('for valid emoji', () => { | ||
expectNoActionIssue(action, ['😀👋🏼🧩👩🏻🏫🫥']); | ||
}); | ||
}); | ||
|
||
describe('should return dataset with issues', () => { | ||
const action = graphemes(5, 'message'); | ||
const baseIssue: Omit<GraphemesIssue<string, 5>, 'input' | 'received'> = { | ||
kind: 'validation', | ||
type: 'graphemes', | ||
expected: '5', | ||
message: 'message', | ||
requirement: 5, | ||
}; | ||
|
||
test('for invalid strings', () => { | ||
expectActionIssue( | ||
action, | ||
baseIssue, | ||
['', ' ', '1', '1234', '123 ', '123456', '12 456', '123456789'], | ||
(value) => `${_getGraphemeCount(value)}` | ||
); | ||
}); | ||
|
||
test('for invalid emoji', () => { | ||
expectActionIssue( | ||
action, | ||
baseIssue, | ||
['😀', '😀👋🏼🧩👩🏻🏫', '😀👋🏼🧩👩🏻🏫🫥🫠', '😀👋🏼🧩👩🏻🏫🫥🫠🧑💻👻🥎'], | ||
(value) => `${_getGraphemeCount(value)}` | ||
); | ||
}); | ||
}); | ||
}); |
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,128 @@ | ||
import type { | ||
BaseIssue, | ||
BaseValidation, | ||
ErrorMessage, | ||
} from '../../types/index.ts'; | ||
import { _addIssue, _getGraphemeCount } from '../../utils/index.ts'; | ||
|
||
/** | ||
* Graphemes issue type. | ||
*/ | ||
export interface GraphemesIssue< | ||
TInput extends string, | ||
TRequirement extends number, | ||
> extends BaseIssue<TInput> { | ||
/** | ||
* The issue kind. | ||
*/ | ||
readonly kind: 'validation'; | ||
/** | ||
* The issue type. | ||
*/ | ||
readonly type: 'graphemes'; | ||
/** | ||
* The expected property. | ||
*/ | ||
readonly expected: `${TRequirement}`; | ||
/** | ||
* The received property. | ||
*/ | ||
readonly received: `${number}`; | ||
/** | ||
* The required graphemes. | ||
*/ | ||
readonly requirement: TRequirement; | ||
} | ||
|
||
/** | ||
* Graphemes action type. | ||
*/ | ||
export interface GraphemesAction< | ||
TInput extends string, | ||
TRequirement extends number, | ||
TMessage extends | ||
| ErrorMessage<GraphemesIssue<TInput, TRequirement>> | ||
| undefined, | ||
> extends BaseValidation<TInput, TInput, GraphemesIssue<TInput, TRequirement>> { | ||
/** | ||
* The action type. | ||
*/ | ||
readonly type: 'graphemes'; | ||
/** | ||
* The action reference. | ||
*/ | ||
readonly reference: typeof graphemes; | ||
/** | ||
* The expected property. | ||
*/ | ||
readonly expects: `${TRequirement}`; | ||
/** | ||
* The required graphemes. | ||
*/ | ||
readonly requirement: TRequirement; | ||
/** | ||
* The error message. | ||
*/ | ||
readonly message: TMessage; | ||
} | ||
|
||
/** | ||
* Creates a graphemes validation action. | ||
* | ||
* @param requirement The required graphemes. | ||
* | ||
* @returns A graphemes action. | ||
*/ | ||
export function graphemes< | ||
TInput extends string, | ||
const TRequirement extends number, | ||
>(requirement: TRequirement): GraphemesAction<TInput, TRequirement, undefined>; | ||
|
||
/** | ||
* Creates a graphemes validation action. | ||
* | ||
* @param requirement The required graphemes. | ||
* @param message The error message. | ||
* | ||
* @returns A graphemes action. | ||
*/ | ||
export function graphemes< | ||
TInput extends string, | ||
const TRequirement extends number, | ||
const TMessage extends | ||
| ErrorMessage<GraphemesIssue<TInput, TRequirement>> | ||
| undefined, | ||
>( | ||
requirement: TRequirement, | ||
message: TMessage | ||
): GraphemesAction<TInput, TRequirement, TMessage>; | ||
|
||
export function graphemes( | ||
requirement: number, | ||
message?: ErrorMessage<GraphemesIssue<string, number>> | ||
): GraphemesAction< | ||
string, | ||
number, | ||
ErrorMessage<GraphemesIssue<string, number>> | undefined | ||
> { | ||
return { | ||
kind: 'validation', | ||
type: 'graphemes', | ||
reference: graphemes, | ||
async: false, | ||
expects: `${requirement}`, | ||
requirement, | ||
message, | ||
'~validate'(dataset, config) { | ||
if (dataset.typed) { | ||
const count = _getGraphemeCount(dataset.value); | ||
if (count !== this.requirement) { | ||
_addIssue(this, 'graphemes', dataset, config, { | ||
received: `${count}`, | ||
}); | ||
} | ||
} | ||
return dataset; | ||
}, | ||
}; | ||
} |
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 './graphemes.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
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 './maxGraphemes.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,50 @@ | ||
import { describe, expectTypeOf, test } from 'vitest'; | ||
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts'; | ||
import { | ||
maxGraphemes, | ||
type MaxGraphemesAction, | ||
type MaxGraphemesIssue, | ||
} from './maxGraphemes.ts'; | ||
|
||
describe('maxGraphemes', () => { | ||
describe('should return action object', () => { | ||
test('with undefined message', () => { | ||
type Action = MaxGraphemesAction<string, 10, undefined>; | ||
expectTypeOf(maxGraphemes<string, 10>(10)).toEqualTypeOf<Action>(); | ||
expectTypeOf( | ||
maxGraphemes<string, 10, undefined>(10, undefined) | ||
).toEqualTypeOf<Action>(); | ||
}); | ||
|
||
test('with string message', () => { | ||
expectTypeOf( | ||
maxGraphemes<string, 10, 'message'>(10, 'message') | ||
).toEqualTypeOf<MaxGraphemesAction<string, 10, 'message'>>(); | ||
}); | ||
|
||
test('with function message', () => { | ||
expectTypeOf( | ||
maxGraphemes<string, 10, () => string>(10, () => 'message') | ||
).toEqualTypeOf<MaxGraphemesAction<string, 10, () => string>>(); | ||
}); | ||
}); | ||
|
||
describe('should infer correct types', () => { | ||
type Input = 'example string'; | ||
type Action = MaxGraphemesAction<Input, 10, undefined>; | ||
|
||
test('of input', () => { | ||
expectTypeOf<InferInput<Action>>().toEqualTypeOf<Input>(); | ||
}); | ||
|
||
test('of output', () => { | ||
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<Input>(); | ||
}); | ||
|
||
test('of issue', () => { | ||
expectTypeOf<InferIssue<Action>>().toEqualTypeOf< | ||
MaxGraphemesIssue<Input, 10> | ||
>(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.