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

add checkItemsAsync action #856

Merged
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
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