import '@testing-library/jest-dom';
import { Provider as MosaicProvider } from '@stoplight/mosaic';
import { render } from '@testing-library/react';
import type { JSONSchema4, JSONSchema7 } from 'json-schema';
import * as React from 'react';
import { describe, expect, it } from 'vitest';
import { SchemaRow } from '../..';
import { buildTree, findNodeWithPath } from './utils';
describe('Property component', () => {
function renderSchema(schema: JSONSchema4 | JSONSchema7, nodePath?: readonly string[]) {
const tree = buildTree(schema);
const node = nodePath ? findNodeWithPath(tree, nodePath) : tree.children[0];
if (node === undefined) {
throw new Error('Schema node not found in tree');
}
const wrapper = render(
,
);
return wrapper;
}
it('should render Types with proper type and subtype', () => {
let schema: JSONSchema4 = {
type: 'array',
items: {
type: 'string',
},
};
let wrapper = renderSchema(schema).queryAllByTestId('property-type')[0];
expect(wrapper).toMatchInlineSnapshot(
`
array[string]
`,
);
schema = {
type: 'object',
additionalProperties: {
type: 'string',
},
};
wrapper = renderSchema(schema).queryAllByTestId('property-type')[0];
expect(wrapper).toMatchInlineSnapshot(
`
array[string]
`,
);
});
it('should handle nullish items', () => {
const schema: JSONSchema4 = {
type: 'array',
items: null as any,
};
const wrapper = renderSchema(schema).queryByTestId('property-type');
expect(wrapper).toMatchInlineSnapshot(
`
array
`,
);
});
it('should handle nullish $ref', () => {
const schema: JSONSchema4 = {
$ref: null as any,
};
const wrapper = renderSchema(schema).queryByTestId('property-type-ref');
expect(wrapper).toMatchInlineSnapshot(
`
$ref
`,
);
});
it('should display true schemas', () => {
const schema: JSONSchema7 = {
type: 'object',
properties: {
foo: true,
},
};
const wrapper = renderSchema(schema, ['properties', 'foo']).queryByTestId('property-type');
expect(wrapper).toMatchInlineSnapshot(
`
any
`,
);
});
it('should display false schemas', () => {
const schema: JSONSchema7 = {
type: 'object',
properties: {
foo: false,
},
};
const wrapper = renderSchema(schema, ['properties', 'foo']).queryByTestId('property-type');
expect(wrapper).toMatchInlineSnapshot(
`
never
`,
);
});
describe('properties names', () => {
it('given an object, should display the names of its properties', () => {
const schema: JSONSchema4 = {
properties: {
foo: {
type: 'string',
},
},
};
const wrapper = renderSchema(schema, ['properties', 'foo']).queryByTestId('schema-row');
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given an object among other types, should still display its properties', () => {
const schema: JSONSchema4 = {
type: ['string', 'object'],
properties: {
foo: {
type: 'array',
items: {
type: 'integer',
},
},
},
};
const wrapper = renderSchema(schema, ['properties', 'foo']).queryByTestId('schema-row');
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given an array of objects, should display names of those properties', () => {
const schema: JSONSchema4 = {
type: 'array',
items: {
properties: {
foo: {
type: 'string',
},
},
},
};
const wrapper = renderSchema(schema, ['items', 'properties', 'foo']).container.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given an array with a combiner inside, should merge it', () => {
const schema: JSONSchema4 = {
type: 'array',
items: {
oneOf: [
{
type: 'string',
},
{
type: 'number',
},
],
},
};
const wrapper = renderSchema(schema).container.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given an array with a mergeable combiner inside, should merge it', () => {
const schema: JSONSchema4 = {
type: 'array',
items: {
oneOf: [
{
properties: {
foo: {},
bar: {},
baz: {},
},
},
],
type: 'object',
},
};
const wrapper = renderSchema(schema).container.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given an array with an allOf inside and enabled allOf merging, should display the name of properties', () => {
const schema: JSONSchema4 = {
type: 'object',
properties: {
'array-all-objects': {
type: 'array',
items: {
allOf: [
{
properties: {
foo: {
type: 'string',
},
},
},
{
properties: {
bar: {
type: 'string',
},
},
},
],
type: 'object',
},
},
},
};
let wrapper = renderSchema(schema, ['properties', 'array-all-objects', 'items', 'properties', 'foo']).container
.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
wrapper = renderSchema(schema, ['properties', 'array-all-objects', 'items', 'properties', 'bar']).container
.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
it('given a ref pointing at complex type, should display property name', () => {
const schema: JSONSchema4 = {
properties: {
foo: {
$ref: '#/properties/bar',
},
bar: {
type: 'object',
},
},
};
const tree = buildTree(schema);
const wrapper = render()
.container.firstChild;
expect(wrapper).toMatchInlineSnapshot(
`
`,
);
});
});
describe('properties titles', () => {
it('given object type, should render title', () => {
const schema: JSONSchema4 = {
title: 'User',
type: 'object',
properties: {
name: {
type: 'string',
},
},
};
const wrapper = renderSchema(schema).queryAllByTestId('property-type')[0];
expect(wrapper).toMatchInlineSnapshot(
`
User
`,
);
});
it('given array type with non-array items, should render title', () => {
const schema: JSONSchema4 = {
type: 'array',
items: {
title: 'User',
type: 'object',
properties: {
name: {
type: 'string',
},
},
},
};
const wrapper = renderSchema(schema).queryAllByTestId('property-type')[0];
expect(wrapper).toMatchInlineSnapshot(
`
array[User]
`,
);
});
it('given array with no items, should render title', () => {
const schema: JSONSchema4 = {
type: 'array',
title: 'User',
};
const wrapper = renderSchema(schema).queryByTestId('property-type');
expect(wrapper).toMatchInlineSnapshot(
`
User
`,
);
});
it('given array with defined items, should not render title', () => {
const schema: JSONSchema4 = {
type: 'array',
items: [
{
title: 'foo',
type: 'string',
},
{
title: 'bar',
type: 'number',
},
],
};
const wrapper = renderSchema(schema).queryAllByTestId('property-type')[0];
expect(wrapper).toMatchInlineSnapshot(
`
array
`,
);
});
});
describe('format', () => {
const schema: JSONSchema4 = require('../../../__fixtures__/formats-schema.json');
it('should render next to a single type', () => {
const wrapper = renderSchema(schema, ['properties', 'id']).queryByText('number');
expect(wrapper).toBeInTheDocument();
});
it.each`
property | text
${'count'} | ${'integer or null'}
${'date-of-birth'} | ${'number or string or array'}
${'array-of-integers'} | ${'array[integer]'}
${'map-of-ids'} | ${'dictionary[string, integer]'}
${'size'} | ${'number or string'}
`('given $property property, should render next to the appropriate type $text', ({ property }) => {
const wrapper = renderSchema(schema, ['properties', property]).container.firstChild;
expect(wrapper).toMatchSnapshot();
});
it('should render even when the type(s) is/are missing', () => {
const wrapper = renderSchema(schema, ['properties', 'notype']).queryByText('');
expect(wrapper).toBeInTheDocument();
});
});
});