refactor: consolidate all JSON Schema 5 rendering code into json-schema-5 plugin (#9798)

This commit is contained in:
Vladimír Gorej
2024-04-10 12:11:51 +02:00
committed by GitHub
parent 46c849b0b3
commit 3b72ee18bc
27 changed files with 56 additions and 52 deletions

View File

@@ -2,7 +2,7 @@
* @prettier
*/
import Model from "../../../../src/core/components/model"
import Model from "../../../../src/core/plugins/json-schema-5/components/model"
describe("getModelName", () => {
const model = new Model()

View File

@@ -0,0 +1,256 @@
import React from "react"
import Immutable, { List } from "immutable"
import { Select, Input, TextArea } from "core/components/layout-utils"
import { mount, render } from "enzyme"
import * as JsonSchemaComponents from "core/plugins/json-schema-5/components/json-schema-components"
const components = {...JsonSchemaComponents, Select, Input, TextArea}
const getComponentStub = (name) => {
if(components[name]) return components[name]
return null
}
describe("<JsonSchemaComponents.JsonSchemaForm/>", function(){
describe("strings", function() {
it("should render the correct options for a string enum parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "string",
enum: ["one", "two"]
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("option").length).toEqual(3)
expect(wrapper.find("option").eq(0).text()).toEqual("--")
expect(wrapper.find("option").eq(1).text()).toEqual("one")
expect(wrapper.find("option").eq(2).text()).toEqual("two")
})
it("should render a string enum as disabled when JsonSchemaForm is disabled", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "string",
enum: ["one", "two"]
}),
disabled: true
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.attr("disabled")).toEqual("disabled")
})
it("should render the correct options for a required string enum parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
required: true,
schema: Immutable.fromJS({
type: "string",
enum: ["one", "two"]
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("select option").length).toEqual(2)
expect(wrapper.find("select option").eq(0).text()).toEqual("one")
expect(wrapper.find("select option").eq(1).text()).toEqual("two")
})
})
describe("booleans", function() {
it("should render the correct options for a boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "boolean"
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("select option").length).toEqual(3)
expect(wrapper.find("select option").eq(0).text()).toEqual("--")
expect(wrapper.find("select option").eq(1).text()).toEqual("true")
expect(wrapper.find("select option").eq(2).text()).toEqual("false")
})
it("should render the correct options for an enum boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "boolean",
enum: ["true"]
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("select option").length).toEqual(2)
expect(wrapper.find("select option").eq(0).text()).toEqual("--")
expect(wrapper.find("select option").eq(1).text()).toEqual("true")
expect(wrapper.find("select option:checked").first().text()).toEqual("--")
})
it("should render the correct options for a required boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "boolean",
required: true
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("select option").length).toEqual(3)
expect(wrapper.find("select option").eq(0).text()).toEqual("--")
expect(wrapper.find("select option").eq(1).text()).toEqual("true")
expect(wrapper.find("select option").eq(2).text()).toEqual("false")
expect(wrapper.find("select option:checked").first().text()).toEqual("--")
})
it("should render the correct options for a required enum boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
required: true,
schema: Immutable.fromJS({
type: "boolean",
enum: ["true"]
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.get(0).name).toEqual("select")
expect(wrapper.find("select option").length).toEqual(1)
expect(wrapper.find("select option").eq(0).text()).toEqual("true")
expect(wrapper.find("select option:checked").first().text()).toEqual("true")
})
})
describe("objects", function() {
it("should render the correct editor for an OAS3 object parameter", function(){
let updateQueue = []
let props = {
getComponent: getComponentStub,
value: `{\n "id": "abc123"\n}`,
onChange: (value) => {
updateQueue.push({ value })
},
keyName: "",
fn: {},
errors: List(),
schema: Immutable.fromJS({
type: "object",
properties: {
id: {
type: "string",
example: "abc123"
}
}
})
}
let wrapper = mount(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
updateQueue.forEach(newProps => wrapper.setProps(newProps))
expect(wrapper.find("textarea").length).toEqual(1)
expect(wrapper.find("textarea").text()).toEqual(`{\n "id": "abc123"\n}`)
})
})
describe("unknown types", function() {
it("should render unknown types as strings", function(){
let props = {
getComponent: getComponentStub,
value: "yo",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "NotARealType"
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.length).toEqual(1)
expect(wrapper.get(0).name).toEqual("input")
// expect(wrapper.find("select input").length).toEqual(1)
// expect(wrapper.find("select option").first().text()).toEqual("true")
})
it("should render unknown types as strings when a format is passed", function(){
let props = {
getComponent: getComponentStub,
value: "yo",
onChange: () => {},
keyName: "",
fn: {},
schema: Immutable.fromJS({
type: "NotARealType",
format: "NotARealFormat"
})
}
let wrapper = render(<JsonSchemaComponents.JsonSchemaForm {...props}/>)
expect(wrapper.length).toEqual(1)
expect(wrapper.get(0).name).toEqual("input")
// expect(wrapper.find("select input").length).toEqual(1)
// expect(wrapper.find("select option").first().text()).toEqual("true")
})
})
})

View File

@@ -0,0 +1,122 @@
import React from "react"
import { shallow } from "enzyme"
import ModelExample from "core/plugins/json-schema-5/components/model-example"
import ModelComponent from "core/plugins/json-schema-5/components/model-wrapper"
describe("<ModelExample/>", function(){
let components, props
let exampleSelectedTestInputs = [
{ defaultModelRendering: "model", isExecute: true },
{ defaultModelRendering: "example", isExecute: true },
{ defaultModelRendering: "example", isExecute: false },
{ defaultModelRendering: "othervalue", isExecute: true },
{ defaultModelRendering: "othervalue", isExecute: false }
]
let modelSelectedTestInputs = [
{ defaultModelRendering: "model", isExecute: false }
]
beforeEach(() => {
components = {
ModelWrapper: ModelComponent
}
props = {
getComponent: (c) => {
return components[c]
},
specSelectors: {
isOAS3: () => false
},
schema: {},
example: "{\"example\": \"value\"}",
isExecute: false,
getConfigs: () => ({
defaultModelRendering: "model",
defaultModelExpandDepth: 1
})
}
})
it("renders model and example tabs", function(){
// When
let wrapper = shallow(<ModelExample {...props}/>)
// Then should render tabs
expect(wrapper.find("div > ul.tab").length).toEqual(1)
let tabs = wrapper.find("div > ul.tab").children()
expect(tabs.length).toEqual(2)
tabs.forEach((node) => {
expect(node.length).toEqual(1)
expect(node.name()).toEqual("li")
expect(node.hasClass("tabitem")).toEqual(true)
})
expect(tabs.at(0).text()).toEqual("Example Value")
expect(tabs.at(1).text()).toEqual("Model")
})
exampleSelectedTestInputs.forEach(function(testInputs) {
it("example tab is selected if isExecute = " + testInputs.isExecute + " and defaultModelRendering = " + testInputs.defaultModelRendering, function(){
// When
props.isExecute = testInputs.isExecute
props.getConfigs = () => ({
defaultModelRendering: testInputs.defaultModelRendering,
defaultModelExpandDepth: 1
})
let wrapper = shallow(<ModelExample {...props}/>)
// Then
let tabs = wrapper.find("div > ul.tab").children()
let exampleTab = tabs.at(0)
expect(exampleTab.hasClass("active")).toEqual(true)
let modelTab = tabs.at(1)
expect(modelTab.hasClass("active")).toEqual(false)
expect(wrapper.find("div > div").length).toEqual(1)
expect(wrapper.find("div > div").text()).toEqual(props.example)
})
})
modelSelectedTestInputs.forEach(function(testInputs) {
it("model tab is selected if isExecute = " + testInputs.isExecute + " and defaultModelRendering = " + testInputs.defaultModelRendering, function(){
// When
props.isExecute = testInputs.isExecute
props.getConfigs = () => ({
defaultModelRendering: testInputs.defaultModelRendering,
defaultModelExpandDepth: 1
})
let wrapper = shallow(<ModelExample {...props}/>)
// Then
let tabs = wrapper.find("div > ul.tab").children()
let exampleTab = tabs.at(0)
expect(exampleTab.hasClass("active")).toEqual(false)
let modelTab = tabs.at(1)
expect(modelTab.hasClass("active")).toEqual(true)
expect(wrapper.find("div > div").length).toEqual(1)
expect(wrapper.find("div > div").find(ModelComponent).props().expandDepth).toBe(1)
})
})
it("passes defaultModelExpandDepth to ModelComponent", function(){
// When
let expandDepth = 0
props.isExecute = false
props.getConfigs = () => ({
defaultModelRendering: "model",
defaultModelExpandDepth: expandDepth
})
let wrapper = shallow(<ModelExample {...props}/>)
// Then
expect(wrapper.find("div > div").find(ModelComponent).props().expandDepth).toBe(expandDepth)
})
})

View File

@@ -0,0 +1,54 @@
import React from "react"
import { shallow } from "enzyme"
import { fromJS, Map } from "immutable"
import Models from "core/plugins/json-schema-5/components/models"
import ModelCollapse from "core/plugins/json-schema-5/components/model-collapse"
import ModelComponent from "core/plugins/json-schema-5/components/model-wrapper"
describe("<Models/>", function(){
const dummyComponent = () => null
// Given
let components = {
Collapse: ModelCollapse,
ModelWrapper: ModelComponent,
JumpToPath: dummyComponent,
}
let props = {
getComponent: (c) => {
return components[c]
},
specSelectors: {
isOAS3: () => false,
specJson: () => Map(),
definitions: function() {
return fromJS({
def1: {},
def2: {}
})
},
specResolvedSubtree: () => {}
},
layoutSelectors: {
isShown: jest.fn()
},
layoutActions: {},
getConfigs: () => ({
docExpansion: "list",
defaultModelsExpandDepth: 0
})
}
it("passes defaultModelsExpandDepth to ModelWrapper", function(){
// When
let wrapper = shallow(<Models {...props}/>)
// Then should render tabs
expect(wrapper.find("ModelCollapse").length).toEqual(1)
expect(wrapper.find("ModelWrapper").length).toBeGreaterThan(0)
wrapper.find("ModelComponent").forEach((modelWrapper) => {
expect(modelWrapper.props().expandDepth).toBe(0)
})
})
})

View File

@@ -0,0 +1,109 @@
import React from "react"
import { shallow } from "enzyme"
import { List } from "immutable"
import ObjectModel from "core/plugins/json-schema-5/components/object-model"
// import ModelExample from "core/components/model-example"
import Immutable from "immutable"
import Model from "core/plugins/json-schema-5/components/model"
import ModelCollapse from "core/plugins/json-schema-5/components/model-collapse"
import Property from "core/components/property"
// import { inferSchema } from "core/plugins/samples/fn"
describe("<ObjectModel />", function() {
const dummyComponent = () => null
const components = {
"JumpToPath" : dummyComponent,
"Markdown" : dummyComponent,
"Model" : Model,
"ModelCollapse" : ModelCollapse,
"Property" : Property
}
const props = {
getComponent: c => components[c],
getConfigs: () => {
return {
showExtensions: true
}
},
isRef : false,
specPath: List(),
schema: Immutable.fromJS(
{
"properties": {
// Note reverse order: c, b, a
c: {
type: "integer",
name: "c"
},
b: {
type: "boolean",
name: "b"
},
a: {
type: "string",
name: "a"
}
}
}
),
specSelectors: {
isOAS3(){
return false
}
},
className: "for-test"
}
const propsNullable = {
...props,
schema: props.schema.set("nullable", true)
}
const propsMinMaxProperties = {
...props,
schema: props.schema.set("minProperties", 1).set("maxProperties", 5)
}
it("renders a collapsible header", function(){
const wrapper = shallow(<ObjectModel {...props}/>)
const renderedModelCollapse = wrapper.find(ModelCollapse)
expect(renderedModelCollapse.length).toEqual(1)
})
it("renders the object properties in order", function() {
const wrapper = shallow(<ObjectModel {...props}/>)
const renderedModel = wrapper.find(Model)
expect(renderedModel.length).toEqual(3)
expect(renderedModel.get(0).props.schema.get("name")).toEqual("c")
expect(renderedModel.get(1).props.schema.get("name")).toEqual("b")
expect(renderedModel.get(2).props.schema.get("name")).toEqual("a")
})
it("doesn't render `nullable` for model when it absent", function() {
const wrapper = shallow(<ObjectModel {...props}/>)
const renderProperties = wrapper.find(Property)
expect(renderProperties.length).toEqual(0)
})
it("renders `nullable` for model", function() {
const wrapper = shallow(<ObjectModel {...propsNullable}/>)
const renderProperties = wrapper.find(Property)
expect(renderProperties.length).toEqual(1)
expect(renderProperties.get(0).props.propKey).toEqual("nullable")
expect(renderProperties.get(0).props.propVal).toEqual(true)
})
it("doesn't render `minProperties` and `maxProperties` if they are absent", function() {
const wrapper = shallow(<ObjectModel {...props}/>)
const renderProperties = wrapper.find(Property)
expect(renderProperties.length).toEqual(0)
})
it("renders `minProperties` and `maxProperties` if they are defined", function() {
const wrapper = shallow(<ObjectModel {...propsMinMaxProperties}/>)
const renderProperties = wrapper.find(Property)
expect(renderProperties.length).toEqual(2)
expect(renderProperties.get(0).props.propKey).toEqual("minProperties")
expect(renderProperties.get(0).props.propVal).toEqual(1)
expect(renderProperties.get(1).props.propKey).toEqual("maxProperties")
expect(renderProperties.get(1).props.propVal).toEqual(5)
})
})

View File

@@ -0,0 +1,55 @@
import React from "react"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import PrimitiveModel from "core/plugins/json-schema-5/components/primitive-model"
import ModelCollapse from "core/plugins/json-schema-5/components/model-collapse"
describe("<PrimitiveModel/>", function () {
const dummyComponent = () => null
const components = {
Markdown: dummyComponent,
EnumModel: dummyComponent,
Property: dummyComponent,
"ModelCollapse" : ModelCollapse,
}
const props = {
getComponent: c => components[c],
getConfigs: () => ({
showExtensions: false
}),
name: "Name from props",
depth: 1,
schema: fromJS({
type: "string",
title: "Custom model title"
}),
expandDepth: 1
}
it("renders the schema's title", function () {
// When
const wrapper = shallow(<PrimitiveModel {...props} />)
const modelTitleEl = wrapper.find("ModelCollapse").prop("title").props.children.props.children
expect(modelTitleEl).toEqual("Custom model title")
})
it("falls back to the passed-in `name` prop for the title", function () {
// When
props.schema = fromJS({
type: "string"
})
const wrapper = shallow(<PrimitiveModel {...props} />)
const modelTitleEl = wrapper.find("ModelCollapse").prop("title").props.children.props.children
// Then
expect(modelTitleEl).toEqual("Name from props")
})
it("renders a collapsible header", function(){
const wrapper = shallow(<PrimitiveModel {...props}/>)
const renderedModelCollapse = wrapper.find(ModelCollapse)
expect(renderedModelCollapse.length).toEqual(1)
})
})

View File

@@ -0,0 +1,86 @@
/**
* @prettier
*/
import React from "react"
import { shallow } from "enzyme"
import { fromJS, List } from "immutable"
import Response from "core/components/response"
import ModelExample from "core/plugins/json-schema-5/components/model-example"
import {
inferSchema,
memoizedSampleFromSchema,
memoizedCreateXMLExample,
} from "core/plugins/json-schema-5-samples/fn/index"
import makeGetSampleSchema from "core/plugins/json-schema-5-samples/fn/get-sample-schema"
import makeGetJsonSampleSchema from "core/plugins/json-schema-5-samples/fn/get-json-sample-schema"
import makeGetYamlSampleSchema from "core/plugins/json-schema-5-samples/fn/get-yaml-sample-schema"
import makeGetXmlSampleSchema from "core/plugins/json-schema-5-samples/fn/get-xml-sample-schema"
describe("<Response />", function () {
const dummyComponent = () => null
const components = {
headers: dummyComponent,
highlightCode: dummyComponent,
modelExample: ModelExample,
Markdown: dummyComponent,
operationLink: dummyComponent,
contentType: dummyComponent,
}
const getSystem = () => ({
getComponent: (c) => components[c],
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() {
return false
},
},
fn: {
inferSchema,
memoizedSampleFromSchema,
memoizedCreateXMLExample,
getJsonSampleSchema: makeGetJsonSampleSchema(getSystem),
getYamlSampleSchema: makeGetYamlSampleSchema(getSystem),
getXmlSampleSchema: makeGetXmlSampleSchema(getSystem),
getSampleSchema: makeGetSampleSchema(getSystem),
},
})
const props = {
...getSystem(),
contentType: "application/json",
className: "for-test",
specPath: List(),
response: fromJS({
schema: {
type: "object",
properties: {
// Note reverse order: c, b, a
c: {
type: "integer",
},
b: {
type: "boolean",
},
a: {
type: "string",
},
},
},
}),
code: "200",
}
it("renders the model-example schema properties in order", function () {
const wrapper = shallow(<Response {...props} />)
const renderedModelExample = wrapper.find(ModelExample)
expect(renderedModelExample.length).toEqual(1)
// Assert the schema's properties have maintained their order
const modelExampleSchemaProperties = renderedModelExample
.props()
.schema.toJS().properties
expect(Object.keys(modelExampleSchemaProperties)).toEqual(["c", "b", "a"])
})
})

View File

@@ -0,0 +1,88 @@
import React from "react"
import { mount } from "enzyme"
import { fromJS } from "immutable"
import SchemesContainer from "core/plugins/json-schema-5/containers/schemes"
import Schemes from "core/plugins/json-schema-5/components/schemes"
import { Col } from "core/components/layout-utils"
describe("<SchemesContainer/>", function(){
const components = {
schemes: Schemes,
Col,
authorizeBtn: () => <span className="mocked-button" id="mocked-button" />
}
const mockedProps = {
specSelectors: {
securityDefinitions() {},
operationScheme() {},
schemes() {}
},
specActions: {
setScheme() {}
},
getComponent: c => components[c]
}
const twoSecurityDefinitions = {
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
}
it("renders Schemes inside SchemesContainer if schemes are provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.operationScheme = function() {return "http"}
props.specSelectors.schemes = function() {return fromJS(["http", "https"])}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(1)
})
it("does not render Schemes inside SchemeWrapper if empty array of schemes is provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.schemes = function() {return fromJS([])}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(0)
})
it("does not render Schemes inside SchemeWrapper if provided schemes are undefined", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.schemes = function() {return undefined}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(0)
})
})

View File

@@ -0,0 +1,69 @@
import React from "react"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import Schemes from "core/plugins/json-schema-5/components/schemes"
describe("<Schemes/>", function(){
it("calls props.specActions.setScheme() when no currentScheme is selected", function(){
let setSchemeSpy = jest.fn()
// Given
let props = {
specActions: {
setScheme: setSchemeSpy
},
schemes: fromJS([
"http",
"https"
]),
currentScheme: undefined,
path: "/test",
method: "get"
}
// When
let wrapper = shallow(<Schemes {...props}/>)
// Then currentScheme should default to first scheme in options list
expect(props.specActions.setScheme).toHaveBeenCalledWith("http", "/test" , "get")
// When the currentScheme is no longer in the list of options
props.schemes = fromJS([
"https"
])
wrapper.setProps(props)
// Then currentScheme should default to first scheme in options list, again
expect(props.specActions.setScheme).toHaveBeenCalledWith("https", "/test", "get")
})
it("doesn't call props.specActions.setScheme() when schemes hasn't changed", function(){
let setSchemeSpy = jest.fn()
// Given
let props = {
specActions: {
setScheme: setSchemeSpy
},
schemes: fromJS([
"http",
"https"
]),
currentScheme: "https"
}
// When
let wrapper = shallow(<Schemes {...props}/>)
// Should be called initially, to set the global state
expect(setSchemeSpy.mock.calls.length).toEqual(1)
// After an update
wrapper.instance().UNSAFE_componentWillReceiveProps(props)
// Should not be called again, since `currentScheme` is in schemes
expect(setSchemeSpy.mock.calls.length).toEqual(1)
})
})