@sanity/sdk
Version:
478 lines (406 loc) • 13.7 kB
text/typescript
import {describe, expect, test} from 'vitest'
import {defineIntent, type Intent, type IntentFilter} from './defineIntent'
describe('defineIntent', () => {
test('should return a valid intent object when all required fields are provided', () => {
const intent: Intent = {
id: 'viewHat',
action: 'view',
title: 'View a hat',
description: 'This lets you view a hat',
filters: [
{
projectId: 'some-project',
dataset: 'a-dataset',
types: ['hat'],
},
],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
expect(result).toBe(intent)
})
test('should throw error when filters array is empty', () => {
const intent: Intent = {
id: 'globalIntent',
action: 'create',
title: 'Global Intent',
description: 'An intent with no filters',
filters: [],
}
expect(() => defineIntent(intent)).toThrow(
"Intent must have at least one filter. If you want to match everything, use {types: ['*']}",
)
})
test('should work with wildcard filter to match everything', () => {
const intent: Intent = {
id: 'globalIntent',
action: 'create',
title: 'Global Intent',
description: 'An intent that matches everything',
filters: [
{
types: ['*'],
},
],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
expect(result.filters[0].types).toEqual(['*'])
})
test('should work with partial filters', () => {
const intent: Intent = {
id: 'partialFilter',
action: 'edit',
title: 'Partial Filter Intent',
description: 'An intent with partial filter criteria',
filters: [
{
projectId: 'some-project',
types: ['*'], // Add required types
},
{
types: ['document'],
// No projectId or dataset specified
},
],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
})
test('should throw error when id is missing', () => {
const intent = {
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have an id')
})
test('should throw error when id is empty string', () => {
const intent: Intent = {
id: '',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [],
}
expect(() => defineIntent(intent)).toThrow('Intent must have an id')
})
test('should throw error when action is missing', () => {
const intent = {
id: 'test',
title: 'Test Intent',
description: 'Test description',
filters: [],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have an action')
})
test('should throw error when action is empty string', () => {
const intent = {
id: 'test',
action: '',
title: 'Test Intent',
description: 'Test description',
filters: [],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have an action')
})
test('should throw error when title is missing', () => {
const intent = {
id: 'test',
action: 'view',
description: 'Test description',
filters: [],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have a title')
})
test('should throw error when title is empty string', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: '',
description: 'Test description',
filters: [],
}
expect(() => defineIntent(intent)).toThrow('Intent must have a title')
})
test('should throw error when filters are missing', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have a filters array')
})
test('should throw error when filters are not an array', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: 'not an array',
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Intent must have a filters array')
})
test('should work with complex filter combinations', () => {
const intent: Intent = {
id: 'complexIntent',
action: 'edit',
title: 'Complex Intent',
description: 'An intent with multiple complex filters',
filters: [
{
projectId: 'project-1',
dataset: 'production',
types: ['article', 'blogPost'],
},
{
projectId: 'project-2',
dataset: 'staging',
types: ['product'],
},
{
// Filter with only types
types: ['global-document'],
},
],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
expect(result.filters).toHaveLength(3)
expect(result.filters[0].types).toEqual(['article', 'blogPost'])
expect(result.filters[2].projectId).toBeUndefined()
expect(result.filters[2].dataset).toBeUndefined()
})
// Filter validation tests
test('should throw error for empty filter object', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{}],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow(
"Filter at index 0 must have a types property. Use ['*'] to match all document types.",
)
})
test('should throw error for non-object filter', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: ['not an object'],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Filter at index 0 must be an object')
})
test('should throw error for non-string projectId', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: 123, types: ['*']}],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Filter at index 0: projectId must be a string')
})
test('should throw error for empty projectId', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: '', types: ['*']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: projectId cannot be empty')
})
test('should throw error for whitespace-only projectId', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: ' ', types: ['*']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: projectId cannot be empty')
})
test('should throw error for non-string dataset', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: 'test', dataset: 123, types: ['*']}],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Filter at index 0: dataset must be a string')
})
test('should throw error for empty dataset', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{dataset: '', types: ['*']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: dataset cannot be empty')
})
test('should throw error when dataset is specified without projectId', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{dataset: 'production', types: ['*']}],
}
expect(() => defineIntent(intent)).toThrow(
'Filter at index 0: dataset cannot be specified without projectId',
)
})
test('should throw error when dataset is specified with empty projectId', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: '', dataset: 'production', types: ['*']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: projectId cannot be empty')
})
test('should work when dataset is specified with valid projectId', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{projectId: 'my-project', dataset: 'production', types: ['*']}],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
})
test('should throw error for non-array types', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: 'not-an-array'}],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Filter at index 0: types must be an array')
})
test('should throw error for empty types array', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: []}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: types array cannot be empty')
})
test('should throw error for non-string type in types array', () => {
const intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: ['valid', 123, 'also-valid']}],
} as unknown as Intent
expect(() => defineIntent(intent)).toThrow('Filter at index 0: types[1] must be a string')
})
test('should throw error for empty string in types array', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: ['valid', '', 'also-valid']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: types[1] cannot be empty')
})
test('should throw error for whitespace-only string in types array', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: ['valid', ' ', 'also-valid']}],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 0: types[1] cannot be empty')
})
test('should throw error when wildcard is mixed with other types', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: ['*', 'document']}],
}
expect(() => defineIntent(intent)).toThrow(
"Filter at index 0: when using wildcard '*', it must be the only type in the array",
)
})
test('should throw error when wildcard appears with other types in different order', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [{types: ['document', 'article', '*']}],
}
expect(() => defineIntent(intent)).toThrow(
"Filter at index 0: when using wildcard '*', it must be the only type in the array",
)
})
test('should work with valid individual filter properties', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [
{projectId: 'my-project', types: ['*']},
{projectId: 'my-project', dataset: 'production', types: ['*']},
{types: ['document']},
{types: ['*']}, // Valid wildcard usage
],
}
const result = defineIntent(intent)
expect(result).toEqual(intent)
})
test('should provide correct filter index in error messages for multiple filters', () => {
const intent: Intent = {
id: 'test',
action: 'view',
title: 'Test Intent',
description: 'Test description',
filters: [
{projectId: 'valid-project', types: ['*']},
{projectId: '', types: ['*']},
],
}
expect(() => defineIntent(intent)).toThrow('Filter at index 1: projectId cannot be empty')
})
})
describe('IntentFilter interface', () => {
test('should require types property', () => {
// This is more of a TypeScript compile-time test
// but we can verify the structure is as expected
const filter1: IntentFilter = {types: ['*']} // types is now required
const filter2: IntentFilter = {projectId: 'test', types: ['*']} // types is now required
const filter3: IntentFilter = {dataset: 'test', types: ['*']} // types is now required
const filter4: IntentFilter = {types: ['test']}
const filter5: IntentFilter = {
projectId: 'test',
dataset: 'test',
types: ['test'],
}
// These should all be valid filter objects
expect(filter1).toBeDefined()
expect(filter2).toBeDefined()
expect(filter3).toBeDefined()
expect(filter4).toBeDefined()
expect(filter5).toBeDefined()
})
})