import '@testing-library/jest-dom'; import { RootNode } from '@stoplight/json-schema-tree'; import { render } from '@testing-library/react'; import { JSONSchema4 } from 'json-schema'; import * as React from 'react'; import { beforeEach, describe, expect, it } from 'vitest'; import { SchemaRow } from '../SchemaRow'; import { buildTree, findNodeWithPath } from '../shared/__tests__/utils'; describe('SchemaRow component', () => { describe('resolving error', () => { let tree: RootNode; let schema: JSONSchema4; beforeEach(() => { schema = { type: 'object', properties: { user: { $ref: '#/properties/foo', }, }, }; tree = buildTree(schema); }); it('given no custom resolver, should render a generic error message', async () => { const wrapper = render(); expect(await wrapper.findByLabelText(`Could not resolve '#/properties/foo'`)).toBeInTheDocument(); }); it('given a custom resolver, should render a message thrown by it', async () => { const message = "I don't know how to resolve it. Sorry"; tree = buildTree(schema, { refResolver: () => { throw new ReferenceError(message); }, }); const wrapper = render(); expect(await wrapper.findByLabelText(message)).toBeInTheDocument(); }); }); describe('resolving permission error', () => { let tree: RootNode; let schema: JSONSchema4; it('given an object schema is marked as internal, a permission denied error message should be shown', async () => { schema = { type: 'object', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); it('given a number schema is marked as internal, a permission denied error messsage should be shown', async () => { schema = { type: 'number', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); it('given an integer schema is marked as internal, a permission denied error messsage should be shown', async () => { schema = { type: 'integer', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); it('given a string schema is marked as internal, a permission denied error messsage should be shown', async () => { schema = { type: 'string', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); it('given a boolean schema is marked as internal, a permission denied error messsage should be shown', async () => { schema = { type: 'boolean', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); it('given an array schema is marked as internal, a permission denied error messsage should be shown', async () => { schema = { title: 'test', type: 'array', items: { type: 'array', 'x-sl-internally-excluded': true, 'x-sl-error-message': 'You do not have permission to view this reference', }, }; tree = buildTree(schema); const wrapper = render(); expect(await wrapper.findByLabelText(`You do not have permission to view this reference`)).toBeInTheDocument(); }); }); describe('required property', () => { let schema: JSONSchema4; function isRequired(schema: JSONSchema4, nodePath: readonly string[], value: boolean) { const tree = buildTree(schema); const schemaNode = findNodeWithPath(tree, nodePath); if (!schemaNode) { throw Error('Node not found, invalid configuration'); } const wrapper = render(); const requiredEl = wrapper.queryByTestId('property-required'); if (value) { expect(requiredEl).toBeInTheDocument(); } else { expect(requiredEl).not.toBeInTheDocument(); } } beforeEach(() => { schema = { type: 'object', properties: { user: { $ref: '#/properties/id', }, id: { type: 'object', required: ['foo'], properties: { foo: { type: 'string', }, bar: { type: 'number', }, }, }, }, }; }); it('should preserve the required validation', () => { isRequired(schema, ['properties', 'id', 'properties', 'foo'], true); }); it('should preserve the optional validation', () => { isRequired(schema, ['properties', 'id', 'properties', 'bar'], false); }); describe('given a referenced object', () => { it('should preserve the required validation', () => { isRequired(schema, ['properties', 'user', 'properties', 'foo'], true); }); it('should preserve the optional validation', () => { isRequired(schema, ['properties', 'user', 'properties', 'bar'], false); }); }); describe('given array with items', () => { beforeEach(() => { schema = { title: 'test', type: 'array', items: { type: 'object', properties: { code: { type: 'number', }, msg: { type: 'string', }, ref: { type: 'string', }, }, required: ['code', 'msg'], }, }; }); it('should preserve the required validation for code item', () => { isRequired(schema, ['items', 'properties', 'code'], true); }); it('should preserve the required validation for msg item', () => { isRequired(schema, ['items', 'properties', 'msg'], true); }); it('should preserve the optional validation', () => { isRequired(schema, ['items', 'properties', 'ref'], false); }); }); describe('given array with arrayish items', () => { beforeEach(() => { schema = { title: 'test', type: 'array', items: [ { type: 'object', properties: { code: { type: 'number', }, msg: { type: 'string', }, ref: { type: 'string', }, }, required: ['code', 'msg'], }, ], }; }); it('should preserve the required validation for code item', () => { isRequired(schema, ['items', '0', 'properties', 'code'], true); }); it('should preserve the required validation for msg item', () => { isRequired(schema, ['items', '0', 'properties', 'msg'], true); }); it('should preserve the optional validation', () => { isRequired(schema, ['items', '0', 'properties', 'ref'], false); }); }); }); describe('schema node with contentMediaType', () => { let schema: JSONSchema4; beforeEach(() => { schema = { type: 'object', properties: { profile_photo: { type: 'string', contentMediaType: 'application/octet-stream', description: "This is user's profile photo", }, }, }; }); it('should render correct type name for binary type', async () => { const tree = buildTree(schema); const schemaNode = findNodeWithPath(tree, ['properties', 'profile_photo']); if (!schemaNode) { throw Error('Node not found, invalid configuration'); } const wrapper = render(); const spanWrapper = await wrapper.findByTestId('property-type'); expect(spanWrapper).toHaveTextContent('string'); }); }); });