diff --git a/src/context.spec.ts b/src/context.spec.ts index 94b74c35..f0081365 100644 --- a/src/context.spec.ts +++ b/src/context.spec.ts @@ -157,7 +157,7 @@ describe('#getData()', () => { context.addFieldInstances(data); - expect(context.getData()).toEqual([data[0]]); + expect(context.getData()).toEqual(data); }); it('does not filter out second undefined when it contains a wildcard', () => { diff --git a/src/context.ts b/src/context.ts index 710fd26b..a80a8b4e 100644 --- a/src/context.ts +++ b/src/context.ts @@ -43,12 +43,12 @@ export class Context { const locations = _.uniqBy(instances, 'location'); // #331 - When multiple locations are involved, all of them must pass the validation. - // If none of the locations contain the field, we at least include one for error reporting. + // checkSchema: If `in` option is not specified, try to check all locations by default. // #458, #531 - Wildcards are an exception though: they may yield 0..* instances with different // paths, so we may want to skip this filtering. if (instances.length > 1 && locations.length > 1 && !group.includes('*')) { const withValue = instances.filter(instance => instance.value !== undefined); - return withValue.length ? withValue : [instances[0]]; + return withValue.length ? withValue : instances; } return instances; diff --git a/src/middlewares/schema.spec.ts b/src/middlewares/schema.spec.ts index 4eaabcf1..9dcc78bc 100644 --- a/src/middlewares/schema.spec.ts +++ b/src/middlewares/schema.spec.ts @@ -183,12 +183,49 @@ describe('on each field', () => { const { context } = await chain.run({ params: { foo: '' } }); expect(context.errors).toHaveLength(1); }); + + it('use default config when specified', async () => { + const chain = checkSchema({ + undef: { + default: { + options: 10, + }, + }, + })[0]; + + const req = { query: { undef: undefined }, body: {}, params: {}, headers: {}, cookies: {} }; + const { context } = await chain.run(req); + + expect(context.getData().every(instance => instance.value === 10)).toBe(true); + expect(req.query.undef).toBe(10); + expect((req as any).body.undef).toBe(10); + expect((req as any).headers.undef).toBe(10); + expect((req as any).params.undef).toBe(10); + expect((req as any).cookies.undef).toBe(10); + }); }); +describe('when ParamSchema location not specified', () => { + it('should validate locations only in which the field can be found', async () => { + const schema = checkSchema({ + foo: { + isInt: true, + toInt: true, + }, + }); + const { context } = await schema[0].run({ + query: { foo: 'foo' }, + params: { foo: 'true' }, + }); + + expect(context.errors).toHaveLength(2); + }); +}); describe('on schema that contains fields with bail methods', () => { - it('stops validation chain with only one error', async () => { + it('stops validation chain with only one error with specified location', async () => { const schema = checkSchema({ foo: { + in: ['params'], exists: { bail: true, }, @@ -200,7 +237,57 @@ describe('on schema that contains fields with bail methods', () => { }, }); - const { context } = await schema[0].run({ params: {} }); + const { context } = await schema[0].run({ query: {} }); + expect(context.errors).toHaveLength(1); + }); + + it('stops validation chain if any validation fails with "exist" validator', async () => { + const allLocations = ['body', 'cookies', 'headers', 'params', 'query']; + const schema = checkSchema({ + foo: { + exists: { + bail: true, + errorMessage: 'exists checking failed', + }, + isLength: { + options: { + min: 5, + }, + }, + }, + }); + + const { context } = await schema[0].run({ query: {} }); + // when location is not specified, checking each location one by one, + // if the expected field does not show up at all in any location, then there will be 5 errors. + // finally bails. + expect(context.errors).toHaveLength(allLocations.length); + // context.errors should not contain any error from next validator -> isLength. + expect(context.errors.every(err => err.msg === 'exists checking failed')).toBe(true); + }); + + it('stops validation chain if any validation fails with sanitizer', async () => { + const schema = checkSchema({ + foo: { + toInt: true, + isInt: { + bail: true, + }, + isLength: { + options: { + min: 5, + }, + }, + }, + }); + + const { context } = await schema[0].run({ query: { foo: 'true' } }); + const { + errors: [firstError], + } = context; + + // simply make sure it's sanitizer-result-caused error. + expect(firstError.value).toBe(NaN); expect(context.errors).toHaveLength(1); }); @@ -225,6 +312,7 @@ describe('on schema that contains fields with bail methods', () => { it('bails with message', async () => { const schema = checkSchema({ foo: { + in: ['params'], exists: { bail: true, errorMessage: 'Value not exists', diff --git a/src/middlewares/validation-chain-builders.spec.ts b/src/middlewares/validation-chain-builders.spec.ts index 5c2114cd..7689f9fb 100644 --- a/src/middlewares/validation-chain-builders.spec.ts +++ b/src/middlewares/validation-chain-builders.spec.ts @@ -48,6 +48,23 @@ describe('buildCheckFunction()', () => { value: 'asd', }); }); + + describe('when at least 1 locations specified', () => { + // for #1010 + it('should apply default value to each specified location if default option is not empty', async () => { + const custom = buildCheckFunction(['cookies', 'headers']); + const chain = custom('bar').default(10); + + await chain.run(req); + + expect(req.cookies).toHaveProperty('bar', 10); + expect(req.headers).toHaveProperty('bar', 10); + + expect(req.body).not.toHaveProperty('bar'); + expect(req.params).not.toHaveProperty('bar'); + expect(req.query).not.toHaveProperty('bar'); + }); + }); }); describe('check()', () => {