Skip to content

Commit b65f8e5

Browse files
committed
Factorized errors
1 parent d605a40 commit b65f8e5

File tree

2 files changed

+202
-188
lines changed

2 files changed

+202
-188
lines changed

src/errors.test.ts

Lines changed: 202 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,208 @@
1-
import { superValidate } from '$lib/server';
1+
import { setError, superValidate } from '$lib/server';
22
import { expect, test, describe, assert } from 'vitest';
33
import { z } from 'zod';
44
import { entityData } from '$lib/schemaEntity';
5+
import { flattenErrors } from '$lib/errors';
6+
7+
describe('Errors', async () => {
8+
test('Adding errors with setError', async () => {
9+
const schema = z.object({
10+
scopeId: z.number().int().min(1),
11+
name: z.string().nullable(),
12+
object: z.object({ name: z.string() }).optional(),
13+
arr: z.string().array().optional(),
14+
enumber: z.enum(['test', 'testing']).optional()
15+
});
16+
17+
const output = await superValidate({ scopeId: 3, name: null }, schema);
18+
19+
expect(output.valid).equals(true);
20+
expect(output.errors).toStrictEqual({});
21+
expect(output.data.scopeId).toEqual(3);
22+
expect(output.data.name).toBeNull();
23+
24+
const err = {
25+
scopeId: ['This is an error'],
26+
enumber: ['This should be ok', 'Still ok'],
27+
arr: { 3: ['Array error'] },
28+
object: { name: ['Object error'] }
29+
};
30+
31+
setError(output, 'scopeId', 'This should not be displayed.');
32+
setError(output, 'scopeId', 'This is an error', { overwrite: true });
33+
setError(output, 'object.name', 'Object error');
34+
setError(output, 'arr[3]', 'Array error');
35+
setError(output, 'enumber', 'This should be ok');
36+
setError(output, 'enumber', 'Still ok');
37+
38+
assert(!output.valid);
39+
expect(output.errors).toStrictEqual(err);
40+
41+
// Should fail, since name does not exist.
42+
const output2 = await superValidate({ scopeId: 3 }, schema);
43+
44+
assert(!output2.valid);
45+
expect(output2.errors.name?.length).toEqual(1);
46+
expect(output2.data.scopeId).toEqual(3);
47+
expect(output2.data.name).toBeNull();
48+
});
49+
50+
test('Clearing errors with noErrors', async () => {
51+
const schema = z.object({
52+
scopeId: z.number().int().min(1),
53+
name: z.string().nullable()
54+
});
55+
56+
const output = await superValidate({ scopeId: 0, name: 'abc' }, schema);
57+
58+
assert(!output.valid);
59+
expect(output.posted).toEqual(false);
60+
expect(output.errors.scopeId?.length).toEqual(1);
61+
expect(Object.keys(output.errors).length).toEqual(1);
62+
expect(output.data.scopeId).toEqual(0);
63+
expect(output.data.name).toEqual('abc');
64+
});
65+
66+
test('AllErrors', async () => {
67+
const nestedSchema = z.object({
68+
id: z.number().positive(),
69+
users: z
70+
.object({
71+
name: z.string().min(2).regex(/X/),
72+
posts: z
73+
.object({ subject: z.string().min(1) })
74+
.array()
75+
.min(2)
76+
.optional()
77+
})
78+
.array()
79+
});
80+
81+
const form = await superValidate(
82+
{ users: [{ name: 'A', posts: [{ subject: '' }] }] },
83+
nestedSchema
84+
);
85+
86+
expect(flattenErrors(form.errors)).toStrictEqual([
87+
{ path: 'id', messages: ['Required'] },
88+
{
89+
path: 'users[0].name',
90+
messages: ['String must contain at least 2 character(s)', 'Invalid']
91+
},
92+
{
93+
path: 'users[0].posts[0].subject',
94+
messages: ['String must contain at least 1 character(s)']
95+
},
96+
{
97+
path: 'users[0].posts._errors',
98+
messages: ['Array must contain at least 2 element(s)']
99+
}
100+
]);
101+
});
102+
103+
test('Form-level errors', async () => {
104+
const refined = z
105+
.object({ name: z.string().min(1) })
106+
.refine(() => false, 'Form-level error');
107+
108+
const form0 = await superValidate(null, refined);
109+
110+
assert(form0.valid === false);
111+
expect(form0.errors).toStrictEqual({});
112+
113+
const form = await superValidate({ name: 'Abc' }, refined);
114+
115+
assert(form.valid === false);
116+
expect(form.errors).toStrictEqual({
117+
_errors: ['Form-level error']
118+
});
119+
120+
expect(form.errors._errors).toStrictEqual(['Form-level error']);
121+
122+
const form2 = await superValidate({ name: '' }, refined);
123+
124+
assert(form2.valid === false);
125+
expect(form2.errors).toStrictEqual({
126+
_errors: ['Form-level error'],
127+
name: ['String must contain at least 1 character(s)']
128+
});
129+
130+
expect(form2.errors).toStrictEqual({
131+
_errors: ['Form-level error'],
132+
name: ['String must contain at least 1 character(s)']
133+
});
134+
});
135+
136+
test('Errors with errors === false', async () => {
137+
const refined = z
138+
.object({ name: z.string().min(1) })
139+
.refine(() => false, 'Form-level error');
140+
141+
const form = await superValidate({ name: '' }, refined, {
142+
errors: false
143+
});
144+
145+
assert(form.valid === false);
146+
expect(form.errors).toStrictEqual({});
147+
});
148+
149+
test('Form-level errors only with refine', async () => {
150+
const schema = z
151+
.object({
152+
scoops: z.number().int().min(1).default(1),
153+
flavours: z.string().min(1).array().default(['Mint choc chip'])
154+
})
155+
.refine(
156+
(data) => data.flavours.length < data.scoops,
157+
"Can't order more flavours than scoops!"
158+
);
159+
160+
const data = new FormData();
161+
data.set('scoops', '1');
162+
data.append('flavours', 'Mint choc chip');
163+
data.append('flavours', 'Raspberry ripple');
164+
165+
const form = await superValidate(data, schema);
166+
167+
expect(form).toStrictEqual({
168+
id: '1exthb3',
169+
valid: false,
170+
errors: { _errors: ["Can't order more flavours than scoops!"] },
171+
data: { scoops: 1, flavours: ['Mint choc chip', 'Raspberry ripple'] },
172+
posted: true,
173+
constraints: {
174+
scoops: { min: 1, required: true },
175+
flavours: { minlength: 1, required: true }
176+
}
177+
});
178+
});
179+
180+
test('Array errors', async () => {
181+
const schema = z.object({
182+
name: z.string(),
183+
tags: z.string().min(1).array().min(2)
184+
});
185+
186+
const form = await superValidate({ tags: [''] }, schema);
187+
188+
assert(!form.valid);
189+
expect(form.errors).toStrictEqual({
190+
name: ['Required'],
191+
tags: {
192+
'0': ['String must contain at least 1 character(s)'],
193+
_errors: ['Array must contain at least 2 element(s)']
194+
}
195+
});
196+
197+
const form2 = await superValidate({ tags: ['only one'] }, schema);
198+
199+
assert(!form2.valid);
200+
expect(form2.errors).toStrictEqual({
201+
name: ['Required'],
202+
tags: { _errors: ['Array must contain at least 2 element(s)'] }
203+
});
204+
});
205+
});
5206

6207
describe('Schema errors with arrays and objects', () => {
7208
const schema = z.object({

0 commit comments

Comments
 (0)