import {expect, test, describe, beforeEach} from 'bun:test' import {yup} from '../src/index' import * as parsing from '../src/parsing' import {Ok} from '../src/helpers' beforeEach(() => (process.env = {})) describe('Object', () => { test('basic', async () => { const scheme = yup.object({}) const res = await parsing.object(scheme) expect(res).toEqual({ok: true, data: {}}) }) describe('empty', () => { test('Optional', async () => { const res = await parsing.object(yup.object({thing: yup.number()})) // @ts-expect-error empty is undefined, duh ?? expect(res).toEqual({ok: true, data: {}}) }) test('Required missing', async () => { const res = await parsing.object(yup.object({thing: yup.number().required()})) expect(res.ok).not.toBeTrue() }) test('default', async () => { const res = await parsing.object(yup.object({thing: yup.number().default(42)})) expect(res).toEqual({ok: true, data: {thing: 42}}) }) }) describe('Nested objects', () => { test('One level', async () => { process.env = { NAME: 'test', PORT: '12', TS: '2025-06-08 23:51:12' } const scheme = yup.object({name: yup.string(), port: yup.number(), ts: yup.date()}) const res = await parsing.object(scheme) expect(res).toEqual({ok: true, data: {name: 'test', port: 12, ts: new Date('2025-06-08 23:51:12')}}) }) test('Two levels', async () => { process.env = { DB_PORT: '12', DB_HOST: 'localhost', THING_A: 'Hello!', THING_B: '212', NAME: 'Pascal', AGE: '27' } const expected = { name: 'Pascal', age: 27, db: { port: 12, host: 'localhost' }, thing: { a: 'Hello!', b: 212 } } const scheme = yup.object({ name: yup.string().required(), age: yup.number().required(), db: yup .object({ port: yup.number().required(), host: yup.string().required() }) .required(), thing: yup .object({ a: yup.string().required(), b: yup.number().required() }) .required() }) const res = await parsing.object(scheme) expect(res).toEqual({ok: true, data: expected}) }) test('Three levels', async () => { process.env = { DB_HOST: 'localhost', THING_A: 'Hello!', AGE: '27', THING_THANG_A: '2025-06-06 23:56:12' } const expected = { age: 27, db: { host: 'localhost' }, thing: { a: 'Hello!', thang: { a: new Date('2025-06-06 23:56:12') } } } const scheme = yup.object({ age: yup.number(), db: yup.object({ host: yup.string() }), thing: yup.object({ a: yup.string(), thang: yup.object({ a: yup.date() }) }) }) const res = await parsing.object(scheme) expect(res).toEqual({ok: true, data: expected}) }) }) describe('Specific parsing', () => { async function testing(env_value: string, scheme: yup.AnySchema, should_be_successful: boolean, thing?: T) { process.env['THING'] = env_value const my_scheme = yup.object({thing: scheme}) const res: Ok<{thing: T}> = await parsing.object(my_scheme) // @ts-expect-error annoying typing issue, but it works, so, yk if (should_be_successful) expect(res).toEqual({ok: true, data: {thing}}) else expect(res.ok).not.toBeTrue() } describe('array', () => { test('basic', () => testing('1,2,3', yup.array(yup.number()), true, [1, 2, 3])) test('basic with whitespaces', () => testing(' 1 , 2 , 3 ', yup.array(yup.number()), true, [1, 2, 3])) test('Empty', () => testing('', yup.array(yup.number()), true, [])) test('Empty fails with minimum length', () => testing('', yup.array(yup.number()).min(3), false)) test('With minimum elements', () => testing('1,2,3', yup.array(yup.number()).min(3), true, [1, 2, 3])) }) describe('tuple', () => { test('basic', () => testing('1,2,3', yup.tuple([yup.number(), yup.number(), yup.number()]), true, [1, 2, 3])) test('Missing value', () => testing('1,2', yup.tuple([yup.number(), yup.number(), yup.number()]), false)) test('Too much values', () => testing('1,2,3,4', yup.tuple([yup.number(), yup.number(), yup.number()]), false)) }) }) }) describe('generic', () => { async function testing( value: string | string[], scheme: yup.AnySchema, should_be_successful: boolean, data?: unknown ) { const res = await parsing.generic(value, scheme) if (should_be_successful) expect(res).toEqual({ok: true, data}) else expect(res.ok).not.toBeTrue() } describe('Integer', () => { test('basic', () => testing('12', yup.number(), true, 12)) test('string', () => testing('coucou', yup.number(), false)) test('min-max OK', () => testing('12', yup.number().min(11).max(13), true, 12)) test('min-max KO', () => testing('10', yup.number().min(11).max(13), false, 10)) test('min-max KO 2', () => testing('10', yup.number().min(11).max(13), false, 14)) }) describe('String', () => { test('basic', () => testing('coucou', yup.string(), true, 'coucou')) test('Length min-max', () => testing('coucou', yup.string().max(7), true, 'coucou')) test('KO: Length min-max', () => testing('coucou', yup.string().max(5), false)) }) describe('boolean', () => { test('basic true', () => testing('true', yup.boolean(), true, true)) test('basic false', () => testing('false', yup.boolean(), true, false)) test('basic 1', () => testing('1', yup.boolean(), true, true)) test('basic 0', () => testing('0', yup.boolean(), true, false)) test('junk', () => testing('yeet', yup.boolean(), false)) }) describe('Date', () => { test('basic', () => testing('2025-06-08 22:40:17', yup.date(), true, new Date('2025-06-08 22:40:17'))) }) })