Skip to content

Commit

Permalink
fix(isBoolean): stringify on output if stringified: true
Browse files Browse the repository at this point in the history
  • Loading branch information
glebbash committed Oct 1, 2021
1 parent ccf9f92 commit 7b4be00
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 157 deletions.
190 changes: 37 additions & 153 deletions src/decorators/is-boolean.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import { Result } from 'true-myth';

import { input, make, output } from '../../tests/helpers';
import { generateSchemas, input, make, output } from '../../tests/helpers';
import { IsBoolean } from '../nestjs-swagger-dto';

describe('IsBoolean', () => {
describe('single', () => {
describe('normal', () => {
class Test {
@IsBoolean()
booleanField!: boolean;
}

it('generates correct schema', async () => {
expect(await generateSchemas([Test])).toStrictEqual({
Test: {
type: 'object',
properties: {
booleanField: {
type: 'boolean',
default: false,
},
},
required: ['booleanField'],
},
});
});

it('accepts booleans', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
Expand Down Expand Up @@ -50,6 +65,26 @@ describe('IsBoolean', () => {
booleanField!: boolean;
}

it('generates correct schema', async () => {
expect(await generateSchemas([Test])).toStrictEqual({
Test: {
type: 'object',
properties: {
booleanField: {
type: 'boolean',
default: false,
},
},
required: ['booleanField'],
},
});
});

it('transforms to plain', async () => {
const dto = make(Test, { booleanField: true });
expect(output(dto)).toStrictEqual({ booleanField: 'true' });
});

it('accepts boolean strings and booleans', async () => {
expect(await input(Test, { booleanField: 'true' })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
Expand Down Expand Up @@ -86,155 +121,4 @@ describe('IsBoolean', () => {
}
});
});

describe('optional', () => {
class Test {
@IsBoolean({ optional: true })
booleanField?: boolean;
}

it('accepts boolean and undefined', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
);
expect(await input(Test, { booleanField: false })).toStrictEqual(
Result.ok(make(Test, { booleanField: false }))
);
expect(await input(Test, {})).toStrictEqual(Result.ok(make(Test, {})));
});

it('rejects everything else', async () => {
const testValues: unknown[] = [
{ booleanField: 'true' },
{ booleanField: 'false' },
{ booleanField: 'abc' },
{ booleanField: 0 },
{ booleanField: [] },
{ booleanField: {} },
{ booleanField: null },
];

for (const testValue of testValues) {
expect(await input(Test, testValue)).toStrictEqual(
Result.err('booleanField must be a boolean value')
);
}
});
});

describe('default', () => {
class Test {
@IsBoolean({ optional: true, default: false })
booleanField?: boolean;
}

it('returns specified defaults if value is undefined', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
);
expect(await input(Test, { booleanField: false })).toStrictEqual(
Result.ok(make(Test, { booleanField: false }))
);
expect(await input(Test, {})).toStrictEqual(Result.ok(make(Test, { booleanField: false })));
});
});

describe('nullable', () => {
class Test {
@IsBoolean({ nullable: true })
booleanField!: boolean | null;
}

it('accepts boolean and null', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
);
expect(await input(Test, { booleanField: false })).toStrictEqual(
Result.ok(make(Test, { booleanField: false }))
);
expect(await input(Test, { booleanField: null })).toStrictEqual(
Result.ok(make(Test, { booleanField: null }))
);
});

it('rejects everything else', async () => {
const testValues: unknown[] = [
{ booleanField: 'true' },
{ booleanField: 'false' },
{ booleanField: 'abc' },
{ booleanField: 0 },
{ booleanField: [] },
{ booleanField: {} },
{},
];

for (const testValue of testValues) {
expect(await input(Test, testValue)).toStrictEqual(
Result.err('booleanField must be a boolean value')
);
}
});
});

describe('nullable and optional', () => {
class Test {
@IsBoolean({ optional: true, nullable: true })
booleanField?: boolean | null;
}

it('accepts boolean and null and undefined', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.ok(make(Test, { booleanField: true }))
);
expect(await input(Test, { booleanField: false })).toStrictEqual(
Result.ok(make(Test, { booleanField: false }))
);
expect(await input(Test, { booleanField: null })).toStrictEqual(
Result.ok(make(Test, { booleanField: null }))
);
expect(await input(Test, {})).toStrictEqual(Result.ok(make(Test, {})));
});

it('rejects everything else', async () => {
const testValues: unknown[] = [
{ booleanField: 'true' },
{ booleanField: 'false' },
{ booleanField: 'abc' },
{ booleanField: 0 },
{ booleanField: [] },
{ booleanField: {} },
];

for (const testValue of testValues) {
expect(await input(Test, testValue)).toStrictEqual(
Result.err('booleanField must be a boolean value')
);
}
});
});

describe('array', () => {
class Test {
@IsBoolean({ isArray: true })
booleanField!: boolean[];
}

it('accepts boolean arrays', async () => {
expect(await input(Test, { booleanField: [true, false] })).toStrictEqual(
Result.ok(make(Test, { booleanField: [true, false] }))
);
expect(await input(Test, { booleanField: [] })).toStrictEqual(
Result.ok(make(Test, { booleanField: [] }))
);
});

it('rejects everything else', async () => {
expect(await input(Test, { booleanField: true })).toStrictEqual(
Result.err('booleanField must be an array')
);
expect(await input(Test, { booleanField: [1, 2, 3] })).toStrictEqual(
Result.err('each value in booleanField must be a boolean value')
);
});
});
});
15 changes: 11 additions & 4 deletions src/decorators/is-boolean.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Transform } from 'class-transformer';
import { IsBoolean as IsBooleanCV } from 'class-validator';

import { Base, compose, noop } from '../core';
import { Base, compose } from '../core';

export const IsBoolean = ({
stringified,
Expand All @@ -11,7 +11,14 @@ export const IsBoolean = ({
{ type: 'boolean' },
base,
IsBooleanCV({ each: !!base.isArray }),
stringified
? Transform(({ value }) => (value === 'true' ? true : value === 'false' ? false : value))
: noop
...(stringified
? [
Transform(({ value }) => (value === 'true' ? true : value === 'false' ? false : value), {
toClassOnly: true,
}),
Transform(({ value }) => (value === undefined ? value : `${value}`), {
toPlainOnly: true,
}),
]
: [])
);

0 comments on commit 7b4be00

Please sign in to comment.