Skip to content
Open
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
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,13 @@ function buildArrayTypeCondition (type, accessor) {
return buildArrayTypeCondition(subType, accessor)
})
condition = `(${conditions.join(' || ')})`
} else {
// The item schema has no `type` keyword, so it does not constrain the
// type of the item at all. That is the case for `{}`, `const`, `enum`,
// `oneOf`/`anyOf`/`allOf`, `if`/`then`/`else` and boolean schemas.
// Without this branch the condition would be `undefined` and every
// value would be rejected.
condition = 'true'
}
}
return condition
Expand Down
74 changes: 74 additions & 0 deletions test/array.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,80 @@ test('array items is a schema and additionalItems is false', (t) => {
t.assert.equal(validate({ foo: ['foo', 'bar'] }), true)
})

test('tuple items without a type keyword accept any value', (t) => {
t.plan(12)

const itemSchemas = {
'empty schema': [{}, 42],
'boolean schema': [true, { a: 1 }],
const: [{ const: 'foo' }, 'foo'],
enum: [{ enum: ['a', 'b'] }, 'b'],
anyOf: [{ anyOf: [{ type: 'string' }, { type: 'number' }] }, 'foo'],
oneOf: [{ oneOf: [{ type: 'string' }, { type: 'number' }] }, 42]
}

const ajv = new Ajv({ allErrors: true, strict: false })

for (const [name, [itemSchema, value]] of Object.entries(itemSchemas)) {
const schema = {
type: 'array',
items: [itemSchema]
}

const stringify = build(schema)
const output = stringify([value])

t.assert.equal(output, JSON.stringify([value]), name)
t.assert.equal(ajv.validate(schema, JSON.parse(output)), true, name)
}
})

test('mixed tuple without a type keyword, with $id and nested in an object', (t) => {
t.plan(2)

const schema = {
$id: 'tuple-without-type',
type: 'array',
items: [
{ anyOf: [{ type: 'string' }, { type: 'number' }] },
{}
]
}

const stringify = build(schema)

t.assert.equal(stringify(['foo', { bar: 42 }]), '["foo",{"bar":42}]')

const nestedStringify = build({
type: 'object',
properties: {
tuple: {
type: 'array',
items: [{ anyOf: [{ type: 'string' }, { type: 'number' }] }]
}
}
})

t.assert.equal(nestedStringify({ tuple: [42] }), '{"tuple":[42]}')
})

test('tuple items with a type keyword still reject mismatching values', (t) => {
t.plan(2)

const schema = {
type: 'array',
items: [
{ type: 'string' },
{ anyOf: [{ type: 'string' }, { type: 'number' }] }
]
}

const stringify = build(schema)

t.assert.equal(stringify(['foo', 42]), '["foo",42]')
t.assert.throws(() => stringify([1, 42]), new Error('Item at 0 does not match schema definition.'))
})

test('array items is a list of schema and additionalItems is a schema', (t) => {
t.plan(1)

Expand Down