Skip to content

Commit

Permalink
Merge pull request #856 from EltonLobo07/feat/add-checkItemsAsync
Browse files Browse the repository at this point in the history
add `checkItemsAsync` action
  • Loading branch information
fabian-hiller authored Oct 10, 2024
2 parents 025e173 + 0e1a15c commit c25643c
Show file tree
Hide file tree
Showing 19 changed files with 710 additions and 58 deletions.
1 change: 1 addition & 0 deletions library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to the library will be documented in this file.

## v1.0.0 (Month DD, YYYY)

- Add `checkItemsAsync` action (pull request #856)
- Change types and implementation to support Standard Schema

## v0.42.1 (September 20, 2024)
Expand Down
28 changes: 8 additions & 20 deletions library/src/actions/checkItems/checkItems.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,29 @@
import { describe, expectTypeOf, test } from 'vitest';
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts';
import {
checkItems,
type CheckItemsAction,
type CheckItemsIssue,
} from './checkItems.ts';
import { checkItems, type CheckItemsAction } from './checkItems.ts';
import type { CheckItemsIssue } from './types.ts';

describe('checkItems', () => {
describe('should return action object', () => {
const requirement = (item: string) => Boolean(item);

test('with undefined message', () => {
type Action = CheckItemsAction<string[], undefined>;
expectTypeOf(checkItems<string[]>(requirement)).toEqualTypeOf<Action>();
expectTypeOf(
checkItems<string[]>((item: string) => Boolean(item))
).toEqualTypeOf<Action>();
expectTypeOf(
checkItems<string[], undefined>(
(item: string) => Boolean(item),
undefined
)
checkItems<string[], undefined>(requirement, undefined)
).toEqualTypeOf<Action>();
});

test('with string message', () => {
expectTypeOf(
checkItems<string[], 'message'>(
(item: string) => Boolean(item),
'message'
)
checkItems<string[], 'message'>(requirement, 'message')
).toEqualTypeOf<CheckItemsAction<string[], 'message'>>();
});

test('with function message', () => {
expectTypeOf(
checkItems<string[], () => string>(
(item: string) => Boolean(item),
() => 'message'
)
checkItems<string[], () => string>(requirement, () => 'message')
).toEqualTypeOf<CheckItemsAction<string[], () => string>>();
});
});
Expand Down
7 changes: 2 additions & 5 deletions library/src/actions/checkItems/checkItems.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import { describe, expect, test } from 'vitest';
import type { StringIssue } from '../../schemas/index.ts';
import type { PartialDataset } from '../../types/dataset.ts';
import { expectNoActionIssue } from '../../vitest/index.ts';
import {
checkItems,
type CheckItemsAction,
type CheckItemsIssue,
} from './checkItems.ts';
import { checkItems, type CheckItemsAction } from './checkItems.ts';
import type { CheckItemsIssue } from './types.ts';

describe('checkItems', () => {
describe('should return action object', () => {
Expand Down
32 changes: 2 additions & 30 deletions library/src/actions/checkItems/checkItems.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,7 @@
import type {
BaseIssue,
BaseValidation,
ErrorMessage,
} from '../../types/index.ts';
import type { BaseValidation, ErrorMessage } from '../../types/index.ts';
import { _addIssue } from '../../utils/index.ts';
import type { ArrayInput, ArrayRequirement } from '../types.ts';

// TODO: Also add `checkItemsAsync` action

/**
* Check items issue type.
*/
export interface CheckItemsIssue<TInput extends ArrayInput>
extends BaseIssue<TInput[number]> {
/**
* The issue kind.
*/
readonly kind: 'validation';
/**
* The issue type.
*/
readonly type: 'check_items';
/**
* The expected input.
*/
readonly expected: null;
/**
* The validation function.
*/
readonly requirement: ArrayRequirement<TInput>;
}
import type { CheckItemsIssue } from './types.ts';

/**
* Check items action type.
Expand Down
54 changes: 54 additions & 0 deletions library/src/actions/checkItems/checkItemsAsync.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expectTypeOf, test } from 'vitest';
import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts';
import {
type CheckItemsActionAsync,
checkItemsAsync,
} from './checkItemsAsync.ts';
import type { CheckItemsIssue } from './types.ts';

describe('checkItemsAsync', () => {
describe('should return action object', () => {
const requirement = async (item: string) => Boolean(item);

test('with undefined message', () => {
type Action = CheckItemsActionAsync<string[], undefined>;
expectTypeOf(
checkItemsAsync<string[]>(requirement)
).toEqualTypeOf<Action>();
expectTypeOf(
checkItemsAsync<string[], undefined>(requirement, undefined)
).toEqualTypeOf<Action>();
});

test('with string message', () => {
expectTypeOf(
checkItemsAsync<string[], 'message'>(requirement, 'message')
).toEqualTypeOf<CheckItemsActionAsync<string[], 'message'>>();
});

test('with function message', () => {
expectTypeOf(
checkItemsAsync<string[], () => string>(requirement, () => 'message')
).toEqualTypeOf<CheckItemsActionAsync<string[], () => string>>();
});
});

describe('should infer correct types', () => {
type Input = ['foo', 123, true];
type Action = CheckItemsActionAsync<Input, undefined>;

test('of input', () => {
expectTypeOf<InferInput<Action>>().toEqualTypeOf<Input>();
});

test('of output', () => {
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<Input>();
});

test('of issue', () => {
expectTypeOf<InferIssue<Action>>().toEqualTypeOf<
CheckItemsIssue<Input>
>();
});
});
});
147 changes: 147 additions & 0 deletions library/src/actions/checkItems/checkItemsAsync.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { describe, expect, test } from 'vitest';
import type { StringIssue } from '../../schemas/index.ts';
import type { PartialDataset } from '../../types/dataset.ts';
import { expectNoActionIssueAsync } from '../../vitest/index.ts';
import {
type CheckItemsActionAsync,
checkItemsAsync,
} from './checkItemsAsync.ts';
import type { CheckItemsIssue } from './types.ts';

describe('checkItemsAsync', () => {
describe('should return action object', () => {
const requirement = async (item: string) => item.startsWith('DE');
const baseAction: Omit<
CheckItemsActionAsync<string[], never>,
'message'
> = {
kind: 'validation',
type: 'check_items',
reference: checkItemsAsync,
expects: null,
requirement,
async: true,
'~validate': expect.any(Function),
};

test('with undefined message', () => {
const action: CheckItemsActionAsync<string[], undefined> = {
...baseAction,
message: undefined,
};
expect(checkItemsAsync<string[]>(requirement)).toStrictEqual(action);
expect(
checkItemsAsync<string[], undefined>(requirement, undefined)
).toStrictEqual(action);
});

test('with string message', () => {
const message = 'message';
expect(
checkItemsAsync<string[], 'message'>(requirement, message)
).toStrictEqual({
...baseAction,
message,
} satisfies CheckItemsActionAsync<string[], 'message'>);
});

test('with function message', () => {
const message = () => 'message';
expect(
checkItemsAsync<string[], typeof message>(requirement, message)
).toStrictEqual({
...baseAction,
message,
} satisfies CheckItemsActionAsync<string[], typeof message>);
});
});

describe('should return dataset without issues', () => {
const action = checkItemsAsync<number[]>(async (item: number) => item > 9);

test('for untyped inputs', async () => {
const issues: [StringIssue] = [
{
kind: 'schema',
type: 'string',
input: null,
expected: 'string',
received: 'null',
message: 'message',
},
];
expect(
await action['~validate']({ typed: false, value: null, issues }, {})
).toStrictEqual({
typed: false,
value: null,
issues,
});
});

test('for empty array', async () => {
await expectNoActionIssueAsync(action, [[]]);
});

test('for valid content', async () => {
await expectNoActionIssueAsync(action, [[10, 11, 12, 13, 99]]);
});
});

describe('should return dataset with issues', () => {
const requirement = async (item: number) => item > 9;
const action = checkItemsAsync<number[], 'message'>(requirement, 'message');

const baseIssue: Omit<CheckItemsIssue<number[]>, 'input' | 'received'> = {
kind: 'validation',
type: 'check_items',
expected: null,
message: 'message',
requirement,
issues: undefined,
lang: undefined,
abortEarly: undefined,
abortPipeEarly: undefined,
};

test('for invalid content', async () => {
const input = [-12, 345, 6, 10];
expect(
await action['~validate']({ typed: true, value: input }, {})
).toStrictEqual({
typed: true,
value: input,
issues: [
{
...baseIssue,
input: input[0],
received: `${input[0]}`,
path: [
{
type: 'array',
origin: 'value',
input,
key: 0,
value: input[0],
},
],
},
{
...baseIssue,
input: input[2],
received: `${input[2]}`,
path: [
{
type: 'array',
origin: 'value',
input,
key: 2,
value: input[2],
},
],
},
],
} satisfies PartialDataset<number[], CheckItemsIssue<number[]>>);
});
});
});
Loading

0 comments on commit c25643c

Please sign in to comment.