This commit is contained in:
2024-05-24 15:27:07 +03:00
parent 17df2ce6a9
commit fc1da2c238
643 changed files with 110185 additions and 231 deletions

View File

View File

@ -0,0 +1,21 @@
import React from "react"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
describe("UI-3199: Sanitized Markdown causing code examples to be double escaped", function(){
it("should single-escape quotes", function(){
let str = "" +
"This is a test: \n\n" +
" {\"abc\": \"def\"}\n"
let props = {
source: str
}
let el = render(<Markdown {...props}/>)
expect(el.find("code").first().text()).toEqual("{\"abc\": \"def\"}\n")
expect(el.find("code").first().html()).toEqual("{\"abc\": \"def\"}\n")
})
})

View File

@ -0,0 +1,35 @@
import React from "react"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
describe("UI-3279: Empty Markdown inputs causing bare `undefined` in output", function(){
it("should return no text for `null` as source input", function(){
let props = {
source: null
}
let el = render(<Markdown {...props}/>)
expect(el.text()).toEqual("")
})
it("should return no text for `undefined` as source input", function(){
let props = {
source: undefined
}
let el = render(<Markdown {...props}/>)
expect(el.text()).toEqual("")
})
it("should return no text for empty string as source input", function(){
let props = {
source: ""
}
let el = render(<Markdown {...props}/>)
expect(el.text()).toEqual("")
})
})

View File

@ -0,0 +1,78 @@
import React from "react"
import { List, fromJS } from "immutable"
import { render } from "enzyme"
import ParameterRow from "components/parameter-row"
describe("bug #4557: default parameter values", function(){
it("should apply a Swagger 2.0 default value", function(){
const paramValue = fromJS({
description: "a pet",
type: "string",
default: "MyDefaultValue"
})
let props = {
getComponent: ()=> "div",
specSelectors: {
security(){},
parameterWithMetaByIdentity(){ return paramValue },
isOAS3(){ return false },
isSwagger2(){ return true }
},
fn: {},
operation: {get: ()=>{}},
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => {},
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props}/>)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "MyDefaultValue", false)
})
it("should apply an OpenAPI 3.0 default value", function(){
const paramValue = fromJS({
description: "a pet",
schema: {
type: "string",
default: "MyDefaultValue"
}
})
let props = {
getComponent: ()=> "div",
specSelectors: {
security(){},
parameterWithMetaByIdentity(){ return paramValue },
isOAS3(){ return true },
isSwagger2() { return false }
},
oas3Selectors: {
activeExamplesMember: () => null
},
fn: {},
operation: {get: ()=>{}},
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => {},
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props}/>)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "MyDefaultValue", false)
})
})

View File

@ -0,0 +1,62 @@
import React from "react"
import { mount } from "enzyme"
import FilterContainer from "containers/filter"
import { Col } from "components/layout-utils"
describe("<FilterContainer/>", function(){
const mockedProps = {
specSelectors: {
loadingStatus() {}
},
layoutSelectors: {
currentFilter() {}
},
getComponent: () => {return Col}
}
it("renders FilterContainer if filter is provided", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return true}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(1)
})
it("does not render FilterContainer if filter is null", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return null}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(0)
})
it("does not render FilterContainer if filter is false", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return false}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(0)
})
})

View File

@ -0,0 +1,67 @@
import React from "react"
import { mount } from "enzyme"
import { fromJS } from "immutable"
import InfoContainer from "containers/info"
describe("<InfoContainer/>", function () {
const components = {
info: () => <span className="mocked-info"/>
}
const mockedProps = {
specSelectors: {
info () {},
url () {},
basePath () {},
host () {},
externalDocs () {},
},
oas3Selectors: {
selectedServer () {},
},
getComponent: c => components[c]
}
it("renders Info inside InfoContainer if info is provided", function () {
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.info = function () {return fromJS(["info1", "info2"])}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(1)
})
it("does not render Info inside InfoContainer if no info is provided", function () {
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.info = function () {return fromJS([])}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(0)
})
it("does not render Info inside InfoContainer if info is undefined", function () {
// Given
let props = {...mockedProps}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(0)
})
})

View File

@ -0,0 +1,257 @@
import React from "react"
import Immutable, { List } from "immutable"
import { Select, Input, TextArea } from "components/layout-utils"
import { mount, render } from "enzyme"
import * as JsonSchemaComponents from "core/json-schema-components"
import { JsonSchemaForm } from "core/json-schema-components"
const components = {...JsonSchemaComponents, Select, Input, TextArea}
const getComponentStub = (name) => {
if(components[name]) return components[name]
return null
}
describe("<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(<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(<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(<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(<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(<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(<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(<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(<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(<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(<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,101 @@
import React from "react"
import { fromJSOrdered } from "core/utils"
import { shallow } from "enzyme"
import Curl from "components/curl"
import LiveResponse from "components/live-response"
import ResponseBody from "components/response-body"
describe("<LiveResponse/>", function(){
let request = fromJSOrdered({
credentials: "same-origin",
headers: {
accept: "application/xml"
},
url: "http://petstore.swagger.io/v2/pet/1"
})
let mutatedRequest = fromJSOrdered({
credentials: "same-origin",
headers: {
accept: "application/xml",
mutated: "header"
},
url: "http://mutated.petstore.swagger.io/v2/pet/1"
})
let requests = {
request: request,
mutatedRequest: mutatedRequest
}
const tests = [
{ showMutatedRequest: true, expected: { request: "mutatedRequest", requestForCalls: 0, mutatedRequestForCalls: 1 } },
{ showMutatedRequest: false, expected: { request: "request", requestForCalls: 1, mutatedRequestForCalls: 0 } }
]
tests.forEach(function(test) {
it("passes " + test.expected.request + " to Curl when showMutatedRequest = " + test.showMutatedRequest, function() {
// Given
let response = fromJSOrdered({
status: 200,
url: "http://petstore.swagger.io/v2/pet/1",
headers: {
"content-type": "application/xml"
},
text: "<response/>",
duration: 50
})
let mutatedRequestForSpy = jest.fn().mockImplementation(function(mutatedRequest) { return mutatedRequest })
let requestForSpy = jest.fn().mockImplementation(function(request) { return request })
let components = {
curl: Curl,
responseBody: ResponseBody
}
let props = {
response: response,
specSelectors: {
mutatedRequestFor: mutatedRequestForSpy,
requestFor: requestForSpy,
},
pathMethod: [ "/one", "get" ],
getComponent: (c) => {
return components[c]
},
displayRequestDuration: true,
getConfigs: () => ({ showMutatedRequest: test.showMutatedRequest })
}
// When
let wrapper = shallow(<LiveResponse {...props}/>)
// Then
expect(mutatedRequestForSpy.calls.length).toEqual(test.expected.mutatedRequestForCalls)
expect(requestForSpy.calls.length).toEqual(test.expected.requestForCalls)
const curl = wrapper.find(Curl)
expect(curl.length).toEqual(1)
expect(curl.props().request).toBe(requests[test.expected.request])
const expectedUrl = requests[test.expected.request].get("url")
expect(wrapper.find("div.request-url pre.microlight").text()).toEqual(expectedUrl)
let duration = wrapper.find("Duration")
expect(duration.length).toEqual(1)
expect(duration.props().duration).toEqual(50)
expect(duration.html())
.toEqual("<div><h5>Request duration</h5><pre class=\"microlight\">50 ms</pre></div>")
let responseHeaders = wrapper.find("Headers")
expect(duration.length).toEqual(1)
expect(responseHeaders.props().headers.length).toEqual(1)
expect(responseHeaders.props().headers[0].key).toEqual("content-type")
expect(responseHeaders.html())
.toEqual("<div><h5>Response headers</h5><pre class=\"microlight\"><span class=\"headerline\"> content-type: application/xml </span></pre></div>")
})
})
})

View File

@ -0,0 +1,110 @@
import React from "react"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.jsx"
describe("Markdown component", function () {
describe("Swagger 2.0", function () {
it("allows elements with class, style and data-* attribs", function () {
const getConfigs = () => ({ useUnsafeMarkdown: true })
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
const el = render(<Markdown source={str} getConfigs={getConfigs} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><span data-attr="value" style="border-width: 1px" class="method">ONE</span></p>\n</div>`)
})
it("strips class, style and data-* attribs from elements", function () {
const getConfigs = () => ({ useUnsafeMarkdown: false })
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
const el = render(<Markdown source={str} getConfigs={getConfigs} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><span>ONE</span></p>\n</div>`)
})
it("allows td elements with colspan attrib", function () {
const str = `<table><tr><td>ABC</td></tr></table>`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><table><tbody><tr><td>ABC</td></tr></tbody></table></div>`)
})
it("allows image elements", function () {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="http://image.source"></p>\n</div>`)
})
it("allows image elements with https scheme", function () {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="https://image.source"></p>\n</div>`)
})
it("allows image elements with data scheme", function () {
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p>` + str + `</p>\n</div>`)
})
it("allows heading elements", function () {
const str = `
# h1
## h2
### h3
#### h4
##### h5
###### h6`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6>\n</div>`)
})
it("allows links", function () {
const str = `[Link](https://example.com/)`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><a rel="noopener noreferrer" target="_blank" href="https://example.com/">Link</a></p>\n</div>`)
})
})
describe("OAS 3", function () {
it("allows elements with class, style and data-* attribs", function () {
const getConfigs = () => ({ useUnsafeMarkdown: true })
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
const el = render(<OAS3Markdown source={str} getConfigs={getConfigs} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p><span data-attr="value" style="border-width: 1px" class="method">ONE</span></p></div>`)
})
it("strips class, style and data-* attribs from elements", function () {
const getConfigs = () => ({ useUnsafeMarkdown: false })
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
const el = render(<OAS3Markdown source={str} getConfigs={getConfigs} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p><span>ONE</span></p></div>`)
})
it("allows image elements", function () {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p><img title="Image title" alt="Image alt text" src="http://image.source"></p></div>`)
})
it("allows image elements with https scheme", function () {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p><img title="Image title" alt="Image alt text" src="https://image.source"></p></div>`)
})
it("allows image elements with data scheme", function () {
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p>` + str + `</p></div>`)
})
it("allows heading elements", function () {
const str = `
# h1
## h2
### h3
#### h4
##### h5
###### h6`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6></div>`)
})
})
})

View File

@ -0,0 +1,122 @@
import React from "react"
import { shallow } from "enzyme"
import ModelExample from "components/model-example"
import ModelComponent from "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 "components/models"
import ModelCollapse from "components/model-collapse"
import ModelComponent from "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 "components/object-model"
// import ModelExample from "components/model-example"
import Immutable from "immutable"
import Model from "components/model"
import ModelCollapse from "components/model-collapse"
import Property from "components/property"
// import { inferSchema } from "corePlugins/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,76 @@
import React from "react"
import { mount } from "enzyme"
import OnlineValidatorBadge from "components/online-validator-badge"
describe("<OnlineValidatorBadge/>", function () {
it("should render a validator link and image correctly for the default validator", function () {
// When
const props = {
getConfigs: () => ({}),
getComponent: () => null,
specSelectors: {
url: () => "swagger.json"
}
}
const wrapper = mount(
<OnlineValidatorBadge {...props} />
)
// Then
expect(wrapper.find("a").props().href).toEqual(
"https://validator.swagger.io/validator/debug?url=swagger.json"
)
expect(wrapper.find("ValidatorImage").length).toEqual(1)
expect(wrapper.find("ValidatorImage").props().src).toEqual(
"https://validator.swagger.io/validator?url=swagger.json"
)
})
it("should encode a definition URL correctly", function () {
// When
const props = {
getConfigs: () => ({}),
getComponent: () => null,
specSelectors: {
url: () => "http://google.com/swagger.json"
}
}
const wrapper = mount(
<OnlineValidatorBadge {...props} />
)
// Then
expect(wrapper.find("a").props().href).toEqual(
"https://validator.swagger.io/validator/debug?url=http%3A%2F%2Fgoogle.com%2Fswagger.json"
)
expect(wrapper.find("ValidatorImage").length).toEqual(1)
expect(wrapper.find("ValidatorImage").props().src).toEqual(
"https://validator.swagger.io/validator?url=http%3A%2F%2Fgoogle.com%2Fswagger.json"
)
})
it.skip("should resolve a definition URL against the browser's location", function () {
// TODO: mock `window`
// When
const props = {
getConfigs: () => ({}),
getComponent: () => null,
specSelectors: {
url: () => "http://google.com/swagger.json"
}
}
const wrapper = mount(
<OnlineValidatorBadge {...props} />
)
// Then
expect(wrapper.find("a").props().href).toEqual(
"https://validator.swagger.io/validator/debug?url=http%3A%2F%2Fgoogle.com%2Fswagger.json"
)
expect(wrapper.find("ValidatorImage").length).toEqual(1)
expect(wrapper.find("ValidatorImage").props().src).toEqual(
"https://validator.swagger.io/validator?url=http%3A%2F%2Fgoogle.com%2Fswagger.json"
)
})
// should resolve a definition URL against the browser's location
})

View File

@ -0,0 +1,53 @@
import React from "react"
import { shallow } from "enzyme"
import OperationTag from "components/operation-tag"
import Im from "immutable"
import { Link } from "components/layout-utils"
describe("<OperationTag/>", function(){
it("render externalDocs URL for swagger v2", function(){
const dummyComponent = () => null
const components = {
Collapse: () => dummyComponent,
Markdown: () => dummyComponent,
DeepLink: () => dummyComponent,
Link
}
let props = {
tagObj: Im.fromJS({
tagDetails: {
externalDocs: {
description: "Find out more",
url: "http://swagger.io"
}
}
}),
tag: "testtag",
getConfigs: () => ({}),
getComponent: c => components[c],
layoutSelectors: {
currentFilter() {
return null
},
isShown() {
return true
},
show() {
return true
}
}
}
let wrapper = shallow(<OperationTag {...props}/>)
const opblockTag = wrapper.find(".opblock-tag")
expect(opblockTag.length).toEqual(1)
expect(opblockTag.getElement().type).toEqual("h3")
const renderedLink = wrapper.find("Link")
expect(renderedLink.length).toEqual(1)
expect(renderedLink.props().href).toEqual("http://swagger.io")
})
})

View File

@ -0,0 +1,30 @@
import React from "react"
import { shallow } from "enzyme"
import Operation from "components/operation"
describe("<Operation/>", function(){
it.skip("blanket tests", function(){
let props = {
operation: {get: ()=>{}},
getComponent: ()=> "div",
specSelectors: { security(){} },
path: "/one",
method: "get",
shown: true,
showOpId: "",
showOpIdPrefix: "",
toggleCollapse: jest.fn()
}
let wrapper = shallow(<Operation {...props}/>)
expect(wrapper.find(".opblock").length).toEqual(1)
expect(wrapper.find(".opblock-summary-method").text()).toEqual("GET")
expect(wrapper.find(".opblock-summary-path").text().trim()).toEqual("/one")
expect(wrapper.find("[isOpened]").prop("isOpened")).toEqual(true)
wrapper.find(".opblock-summary").simulate("click")
expect(props.toggleCollapse).toHaveBeenCalled()
})
})

View File

@ -0,0 +1,126 @@
import React from "react"
import { render } from "enzyme"
import { fromJS } from "immutable"
import DeepLink from "components/deep-link"
import Operations from "components/operations"
import {Collapse} from "components/layout-utils"
const components = {
Collapse,
DeepLink,
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />,
OperationTag: "div",
}
describe("<Operations/>", function(){
it("should render a Swagger2 `get` method, but not a `trace` or `foo` method", function(){
let props = {
fn: {},
specActions: {},
layoutActions: {},
getComponent: (name)=> {
return components[name] || null
},
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() { return false },
url() { return "https://petstore.swagger.io/v2/swagger.json" },
taggedOperations() {
return fromJS({
"default": {
"operations": [
{
"path": "/pets/{id}",
"method": "get"
},
{
"path": "/pets/{id}",
"method": "trace"
},
{
"path": "/pets/{id}",
"method": "foo"
},
]
}
})
},
},
layoutSelectors: {
currentFilter() {
return null
},
isShown() {
return true
},
show() {
return true
}
}
}
let wrapper = render(<Operations {...props}/>)
expect(wrapper.find("span.mocked-op").length).toEqual(1)
expect(wrapper.find("span.mocked-op").eq(0).attr("id")).toEqual("/pets/{id}-get")
})
it("should render an OAS3 `get` and `trace` method, but not a `foo` method", function(){
let props = {
fn: {},
specActions: {},
layoutActions: {},
getComponent: (name)=> {
return components[name] || null
},
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() { return true },
url() { return "https://petstore.swagger.io/v2/swagger.json" },
taggedOperations() {
return fromJS({
"default": {
"operations": [
{
"path": "/pets/{id}",
"method": "get"
},
{
"path": "/pets/{id}",
"method": "trace"
},
{
"path": "/pets/{id}",
"method": "foo"
},
]
}
})
},
},
layoutSelectors: {
currentFilter() {
return null
},
isShown() {
return true
},
show() {
return true
}
}
}
let wrapper = render(<Operations {...props}/>)
expect(wrapper.find("span.mocked-op").length).toEqual(2)
expect(wrapper.find("span.mocked-op").eq(0).attr("id")).toEqual("/pets/{id}-get")
expect(wrapper.find("span.mocked-op").eq(1).attr("id")).toEqual("/pets/{id}-trace")
})
})

View File

@ -0,0 +1,257 @@
import React from "react"
import { List, fromJS } from "immutable"
import { render } from "enzyme"
import ParameterRow from "components/parameter-row"
describe("<ParameterRow/>", () => {
const createProps = ({ param, isOAS3 }) => ({
getComponent: () => "div",
specSelectors: {
parameterWithMetaByIdentity: () => param,
isOAS3: () => isOAS3,
isSwagger2: () => !isOAS3
},
oas3Selectors: { activeExamplesMember: () => {} },
param,
rawParam: param,
pathMethod: [],
getConfigs: () => ({})
})
it("Can render Swagger 2 parameter type with format", () => {
const param = fromJS({
name: "petUuid",
in: "path",
description: "UUID that identifies a pet",
type: "string",
format: "uuid"
})
const props = createProps({ param, isOAS3: false })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("string($uuid)")
})
it("Can render Swagger 2 parameter type without format", () => {
const param = fromJS({
name: "petId",
in: "path",
description: "ID that identifies a pet",
type: "string"
})
const props = createProps({ param, isOAS3: false })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("string")
})
it("Can render Swagger 2 parameter type boolean without format", () => {
const param = fromJS({
name: "hasId",
in: "path",
description: "boolean value to indicate if the pet has an id",
type: "boolean"
})
const props = createProps({ param, isOAS3: false })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("boolean")
})
it("Can render OAS3 parameter type with format", () => {
const param = fromJS({
name: "petUuid",
in: "path",
description: "UUID that identifies a pet",
schema: {
type: "string",
format: "uuid"
}
})
const props = createProps({ param, isOAS3: true })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("string($uuid)")
})
it("Can render OAS3 parameter type without format", () => {
const param = fromJS({
name: "petId",
in: "path",
description: "ID that identifies a pet",
schema: {
type: "string"
}
})
const props = createProps({ param, isOAS3: true })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("string")
})
it("Can render OAS3 parameter type boolean without format", () => {
const param = fromJS({
name: "hasId",
in: "path",
description: "boolean value to indicate if the pet has an id",
schema: {
type: "boolean"
}
})
const props = createProps({ param, isOAS3: true })
const wrapper = render(<ParameterRow {...props}/>)
expect(wrapper.find(".parameter__type").length).toEqual(1)
expect(wrapper.find(".parameter__type").text()).toEqual("boolean")
})
})
describe("bug #5573: zero default and example values", function () {
it("should apply a Swagger 2.0 default value of zero", function () {
const paramValue = fromJS({
description: "a pet",
type: "integer",
default: 0
})
let props = {
getComponent: () => "div",
specSelectors: {
security() { },
parameterWithMetaByIdentity() { return paramValue },
isOAS3() { return false },
isSwagger2() { return true }
},
fn: {},
operation: { get: () => { } },
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => { },
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props} />)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "0", false)
})
it("should apply a Swagger 2.0 example value of zero", function () {
const paramValue = fromJS({
description: "a pet",
type: "integer",
schema: {
example: 0
}
})
let props = {
getComponent: () => "div",
specSelectors: {
security() { },
parameterWithMetaByIdentity() { return paramValue },
isOAS3() { return false },
isSwagger2() { return true }
},
fn: {},
operation: { get: () => { } },
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => { },
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props} />)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "0", false)
})
it("should apply an OpenAPI 3.0 default value of zero", function () {
const paramValue = fromJS({
description: "a pet",
schema: {
type: "integer",
default: 0
}
})
let props = {
getComponent: () => "div",
specSelectors: {
security() { },
parameterWithMetaByIdentity() { return paramValue },
isOAS3() { return true },
isSwagger2() { return false }
},
oas3Selectors: {
activeExamplesMember: () => null
},
fn: {},
operation: { get: () => { } },
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => { },
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props} />)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "0", false)
})
it("should apply an OpenAPI 3.0 example value of zero", function () {
const paramValue = fromJS({
description: "a pet",
schema: {
type: "integer",
example: 0
}
})
let props = {
getComponent: () => "div",
specSelectors: {
security() { },
parameterWithMetaByIdentity() { return paramValue },
isOAS3() { return true },
isSwagger2() { return false }
},
oas3Selectors: {
activeExamplesMember: () => null
},
fn: {},
operation: { get: () => { } },
onChange: jest.fn(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => { },
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props} />)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "0", false)
})
})

View File

@ -0,0 +1,55 @@
import React from "react"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import PrimitiveModel from "components/primitive-model"
import ModelCollapse from "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,42 @@
import React from "react"
import { shallow } from "enzyme"
import ResponseBody from "components/response-body"
describe("<ResponseBody />", function () {
const highlightCodeComponent = () => null
const components = {
highlightCode: highlightCodeComponent
}
const props = {
getComponent: c => components[c],
}
it("renders ResponseBody as 'application/json'", function () {
props.contentType = "application/json"
props.content = "{\"key\": \"a test value\"}"
const wrapper = shallow(<ResponseBody {...props} />)
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
})
it("renders ResponseBody as 'text/html'", function () {
props.contentType = "application/json"
props.content = "<b>Result</b>"
const wrapper = shallow(<ResponseBody {...props} />)
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
})
it("renders ResponseBody as 'image/svg'", function () {
props.contentType = "image/svg"
const wrapper = shallow(<ResponseBody {...props} />)
console.warn(wrapper.debug())
expect(wrapper.find("highlightCodeComponent").length).toEqual(0)
})
it("should render a copyable highlightCodeComponent for text types", function () {
props.contentType = "text/plain"
props.content = "test text"
const wrapper = shallow(<ResponseBody {...props} />)
console.warn(wrapper.debug())
expect(wrapper.find("highlightCodeComponent[canCopy]").length).toEqual(1)
})
})

View File

@ -0,0 +1,63 @@
import React from "react"
import { shallow } from "enzyme"
import { fromJS, List } from "immutable"
import Response from "components/response"
import ModelExample from "components/model-example"
import { inferSchema } from "corePlugins/samples/fn"
describe("<Response />", function () {
const dummyComponent = () => null
const components = {
headers: dummyComponent,
highlightCode: dummyComponent,
modelExample: ModelExample,
Markdown: dummyComponent,
operationLink: dummyComponent,
contentType: dummyComponent
}
const props = {
getComponent: c => components[c],
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() {
return false
}
},
fn: {
inferSchema
},
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 "containers/schemes"
import Schemes from "components/schemes"
import { Col } from "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 "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)
})
})

View File

@ -0,0 +1,67 @@
import React from "react"
import { shallow } from "enzyme"
import VersionPragmaFilter from "components/version-pragma-filter"
describe("<VersionPragmaFilter/>", function(){
it("renders children for a Swagger 2 definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={true} isOAS3={false}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders children for an OpenAPI 3 definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={false} isOAS3={true}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders children when a bypass prop is set", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter bypass>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders the correct message for an ambiguous-version definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={true} isOAS3={true}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div.version-pragma__message--ambiguous").length).toEqual(1)
expect(wrapper.find("div.version-pragma__message--missing").length).toEqual(0)
})
it("renders the correct message for a missing-version definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={false} isOAS3={false}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div.version-pragma__message--missing").length).toEqual(1)
expect(wrapper.find("div.version-pragma__message--ambiguous").length).toEqual(0)
})
})

View File

@ -0,0 +1,389 @@
import Im from "immutable"
import { requestSnippetGenerator_curl_bash as curl } from "core/plugins/request-snippets/fn.js"
import win from "core/window"
describe("curlify", function () {
it("prints a curl statement with custom content-type", function () {
const body = JSON.stringify({
id: 0,
name: "doggie",
status: "available"
}, null, 2)
let req = {
url: "http://example.com",
method: "POST",
body,
headers: {
Accept: "application/json",
"content-type": "application/json"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual(`curl -X 'POST' \\\n 'http://example.com' \\\n -H 'Accept: application/json' \\\n -H 'content-type: application/json' \\\n -d '${body}'`)
})
it("does add a empty data param if no request body given", function () {
let req = {
url: "http://example.com",
method: "POST",
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -d ''")
})
it("does not change the case of header in curl", function () {
let req = {
url: "http://example.com",
method: "POST",
headers: {
"conTenT Type": "application/Moar"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'conTenT Type: application/Moar' \\\n -d ''")
})
it("prints a curl statement with an array of query params", function () {
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "GET"
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'GET' \\\n 'http://swaggerhub.com/v1/one?name=john|smith'")
})
it("prints a curl statement with an array of query params and auth", function () {
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "GET",
headers: {
authorization: "Basic Zm9vOmJhcg=="
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'GET' \\\n 'http://swaggerhub.com/v1/one?name=john|smith' \\\n -H 'authorization: Basic Zm9vOmJhcg=='")
})
it("prints a curl statement with html", function () {
const body = {
description: "<b>Test</b>"
}
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "GET",
headers: {
accept: "application/json"
},
body
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual(`curl -X 'GET' \\\n 'http://swaggerhub.com/v1/one?name=john|smith' \\\n -H 'accept: application/json' \\\n -d '${JSON.stringify(body, null, 2)}'`)
})
it("handles post body with html", function () {
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "POST",
headers: {
accept: "application/json"
},
body: {
description: "<b>Test</b>"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual(`curl -X 'POST' \\
'http://swaggerhub.com/v1/one?name=john|smith' \\
-H 'accept: application/json' \\
-d '{
"description": "<b>Test</b>"
}'`)
})
it("handles post body with special chars", function () {
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "POST",
body: {
description: "@prefix nif:<http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> .\n" +
"@prefix itsrdf: <http://www.w3.org/2005/11/its/rdf#> ."
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://swaggerhub.com/v1/one?name=john|smith' \\\n -d '{\n \"description\": \"@prefix nif:<http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> .\\n@prefix itsrdf: <http://www.w3.org/2005/11/its/rdf#> .\"\n}'")
})
it("handles delete form with parameters", function () {
let req = {
url: "http://example.com",
method: "DELETE",
headers: {
accept: "application/x-www-form-urlencoded"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'DELETE' \\\n 'http://example.com' \\\n -H 'accept: application/x-www-form-urlencoded'")
})
it("should print a curl with formData", function () {
let req = {
url: "http://example.com",
method: "POST",
headers: { "content-type": "multipart/form-data" },
body: {
id: "123",
name: "Sahar"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: multipart/form-data' \\\n -F 'id=123' \\\n -F 'name=Sahar'")
})
it("should print a curl with formData that extracts array representation with hashIdx", function () {
// Note: hashIdx = `_**[]${counter}`
// Usage of hashIdx is an internal SwaggerUI method to convert formData array into something curlify can handle
const req = {
url: "http://example.com",
method: "POST",
headers: { "content-type": "multipart/form-data" },
body: {
id: "123",
"fruits[]_**[]1": "apple",
"fruits[]_**[]2": "banana",
"fruits[]_**[]3": "grape"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: multipart/form-data' \\\n -F 'id=123' \\\n -F 'fruits[]=apple' \\\n -F 'fruits[]=banana' \\\n -F 'fruits[]=grape'")
})
it("should print a curl with formData and file", function () {
let file = new win.File([""], "file.txt", { type: "text/plain" })
// file.name = "file.txt"
// file.type = "text/plain"
let req = {
url: "http://example.com",
method: "POST",
headers: { "content-type": "multipart/form-data" },
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: multipart/form-data' \\\n -F 'id=123' \\\n -F 'file=@file.txt;type=text/plain'")
})
it("should print a curl without form data type if type is unknown", function () {
let file = new win.File([""], "file.txt", { type: "" })
// file.name = "file.txt"
// file.type = ""
let req = {
url: "http://example.com",
method: "POST",
headers: { "content-type": "multipart/form-data" },
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: multipart/form-data' \\\n -F 'id=123' \\\n -F 'file=@file.txt'")
})
it("should print a curl with data-binary if body is instance of File and it is not a multipart form data request", function () {
let file = new win.File([""], "file.txt", { type: "" })
let req = {
url: "http://example.com",
method: "POST",
headers: { "content-type": "application/octet-stream" },
body: file
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: application/octet-stream' \\\n --data-binary '@file.txt'")
})
it("prints a curl post statement from an object", function () {
let req = {
url: "http://example.com",
method: "POST",
headers: {
accept: "application/json"
},
body: {
id: 10101
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'accept: application/json' \\\n -d '{\n \"id\": 10101\n}'")
})
it("prints a curl post statement from a string containing a single quote", function () {
let req = {
url: "http://example.com",
method: "POST",
headers: {
accept: "application/json"
},
body: "{\"id\":\"foo'bar\"}"
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'accept: application/json' \\\n -d '{\"id\":\"foo'\\''bar\"}'")
})
describe("given multiple entries with file", function () {
describe("and with leading custom header", function () {
it("should print a proper curl -F", function () {
let file = new win.File([""], "file.txt", { type: "text/plain" })
// file.name = "file.txt"
// file.type = "text/plain"
let req = {
url: "http://example.com",
method: "POST",
headers: {
"x-custom-name": "multipart/form-data",
"content-type": "multipart/form-data"
},
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'x-custom-name: multipart/form-data' \\\n -H 'content-type: multipart/form-data' \\\n -F 'id=123' \\\n -F 'file=@file.txt;type=text/plain'")
})
})
describe("and with trailing custom header; e.g. from requestInterceptor appending req.headers", function () {
it("should print a proper curl -F", function () {
let file = new win.File([""], "file.txt", { type: "text/plain" })
// file.name = "file.txt"
// file.type = "text/plain"
let req = {
url: "http://example.com",
method: "POST",
headers: {
"content-type": "multipart/form-data",
"x-custom-name": "any-value"
},
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'content-type: multipart/form-data' \\\n -H 'x-custom-name: any-value' \\\n -F 'id=123' \\\n -F 'file=@file.txt;type=text/plain'")
})
})
})
describe("POST when header value is 'multipart/form-data' but header name is not 'content-type'", function () {
it("should print a proper curl as -d <data>, when file type is provided", function () {
let file = new win.File([""], "file.txt", { type: "text/plain" })
// file.name = "file.txt"
// file.type = "text/plain"
let req = {
url: "http://example.com",
method: "POST",
headers: { "x-custom-name": "multipart/form-data" },
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'x-custom-name: multipart/form-data' \\\n -d '{\n \"id\": \"123\",\n \"file\": {\n \"name\": \"file.txt\",\n \"type\": \"text/plain\"\n }\n}'")
})
it("should print a proper curl as -d <data>, no file type provided", function () {
let file = new win.File([""], "file.txt")
// file.name = "file.txt"
// file.type = "text/plain"
let req = {
url: "http://example.com",
method: "POST",
headers: { "x-custom-name": "multipart/form-data" },
body: {
id: "123",
file
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X 'POST' \\\n 'http://example.com' \\\n -H 'x-custom-name: multipart/form-data' \\\n -d '{\n \"id\": \"123\",\n \"file\": {\n \"name\": \"file.txt\"\n }\n}'")
})
})
it("should include curlOptions from the request in the curl command", function () {
let req = {
url: "http://example.com",
method: "GET",
headers: { "X-DOLLAR": "token/123$" },
curlOptions: ["-g"]
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -g -X 'GET' \\\n 'http://example.com' \\\n -H 'X-DOLLAR: token/123$'")
})
it("should include multiple curlOptions from the request in the curl command", function () {
let req = {
url: "http://example.com",
method: "GET",
headers: { "X-DOLLAR": "token/123$" },
curlOptions: ["-g", "--limit-rate 20k"]
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -g --limit-rate 20k -X 'GET' \\\n 'http://example.com' \\\n -H 'X-DOLLAR: token/123$'")
})
})

View File

@ -0,0 +1,39 @@
/**
* @prettier
*/
import Model from "../../../../src/core/components/model"
describe("getModelName", () => {
const model = new Model()
it("should decode JSON Pointer and URI encoding for OpenAPI v3 refs", () => {
const actual = model.getModelName("#/components/schemas/a~1b%2Bc")
const expected = "a/b+c"
expect(actual).toStrictEqual(expected)
})
it("should decode JSON Pointer and URI encoding for Swagger v2 refs", () => {
const actual = model.getModelName(
"#/definitions/custom%3A%3Anamespace%3A%3APerson"
)
const expected = "custom::namespace::Person"
expect(actual).toStrictEqual(expected)
})
it("should decode multiple json-pointer values", () => {
const actual = model.getModelName("#/components/schemas/~1~1~0~0")
const expected = "//~~"
expect(actual).toStrictEqual(expected)
})
it("should support invalid URI encoding", () => {
const actual = model.getModelName("#/components/schemas/%25%d")
const expected = "%25%d"
expect(actual).toStrictEqual(expected)
})
})

View File

@ -0,0 +1,147 @@
/**
* @prettier
*/
import { fromJS } from "immutable"
import getParameterSchema from "../../../../src/helpers/get-parameter-schema"
describe("getParameterSchema", () => {
it("should return an empty Map when given no parameters", () => {
const result = getParameterSchema()
expect(result.schema.toJS()).toEqual({})
expect(result.parameterContentMediaType).toEqual(null)
})
it("should return an empty Map when given an empty Map", () => {
const result = getParameterSchema(fromJS({}))
expect(result.schema.toJS()).toEqual({})
expect(result.parameterContentMediaType).toEqual(null)
})
it("should return a schema for a Swagger 2.0 query parameter", () => {
const result = getParameterSchema(
fromJS({
name: "id",
in: "query",
description: "ID of the object to fetch",
required: false,
type: "array",
items: {
type: "string",
},
collectionFormat: "multi",
})
)
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})
it("should return a schema for a Swagger 2.0 body parameter", () => {
const result = getParameterSchema(
fromJS({
name: "user",
in: "body",
description: "user to add to the system",
required: true,
schema: {
type: "array",
items: {
type: "string",
},
},
})
)
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})
it("should return a schema for an OpenAPI 3.0 query parameter", () => {
const result = getParameterSchema(
fromJS({
name: "id",
in: "query",
description: "ID of the object to fetch",
required: false,
schema: {
type: "array",
items: {
type: "string",
},
},
style: "form",
explode: true,
}),
{
isOAS3: true,
}
)
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})
it("should return a schema for an OpenAPI 3.0 query parameter with `content`", () => {
const result = getParameterSchema(
fromJS({
in: "query",
name: "coordinates",
content: {
"application/json": {
schema: {
type: "object",
required: ["lat", "long"],
properties: {
lat: {
type: "number",
},
long: {
type: "number",
},
},
},
"should-ignore/the-second-media-type": {
type: "string",
default: "this shouldn't be returned",
},
},
},
}),
{
isOAS3: true,
}
)
expect(result.schema.toJS()).toEqual({
type: "object",
required: ["lat", "long"],
properties: {
lat: {
type: "number",
},
long: {
type: "number",
},
},
})
expect(result.parameterContentMediaType).toEqual(`application/json`)
})
})

View File

@ -0,0 +1,201 @@
import Im from "immutable"
import oauth2Authorize from "core/oauth2-authorize"
import * as utils from "core/utils"
describe("oauth2", () => {
let mockSchema = {
flow: "accessCode",
authorizationUrl: "https://testAuthorizationUrl"
}
let authConfig = {
auth: { schema: { get: (key)=> mockSchema[key] }, scopes: ["scope1", "scope2"] },
authActions: {
authPopup: jest.fn()
},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {}
}
let authConfig2 = {
auth: { schema: { get: (key)=> mockSchema[key] }, scopes: Im.List(["scope2","scope3"]) },
authActions: {
authPopup: jest.fn()
},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {}
}
let authConfig3 = {
auth: { schema: { get: (key)=> mockSchema[key] }, scopes: Im.List(["scope4","scope5"]) },
authActions: {
authPopup: jest.fn()
},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {}
}
describe("authorize redirect", () => {
it("should build authorize url", () => {
oauth2Authorize(authConfig)
expect(authConfig.authActions.authPopup.mock.calls.length).toEqual(1)
expect(authConfig.authActions.authPopup.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?response_type=code&redirect_uri=&scope=scope1%20scope2&state=")
authConfig.authActions.authPopup.mockReset()
})
it("should build authorize url relative", function () {
let relativeMockSchema = {
flow: "accessCode",
authorizationUrl: "/testAuthorizationUrl"
}
let relativeAuthConfig = {
auth: { schema: { get: (key) => relativeMockSchema[key] } },
authActions: {
authPopup: jest.fn()
},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {},
currentServer: "https://currentserver"
}
oauth2Authorize(relativeAuthConfig)
expect(relativeAuthConfig.authActions.authPopup.mock.calls.length).toEqual(1)
expect(relativeAuthConfig.authActions.authPopup.mock.calls[0][0]).toMatch("https://currentserver/testAuthorizationUrl?response_type=code&redirect_uri=&state=")
relativeAuthConfig.authActions.authPopup.mockReset()
})
it("should append query parameters to authorizeUrl with query parameters", () => {
mockSchema.authorizationUrl = "https://testAuthorizationUrl?param=1"
oauth2Authorize(authConfig)
expect(authConfig.authActions.authPopup.mock.calls.length).toEqual(1)
expect(authConfig.authActions.authPopup.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?param=1&response_type=code&redirect_uri=&scope=scope1%20scope2&state=")
authConfig.authActions.authPopup.mockReset()
})
it("should send code_challenge when using authorizationCode flow with usePkceWithAuthorizationCodeGrant enabled", () => {
mockSchema.flow = "authorizationCode"
const expectedCodeVerifier = "mock_code_verifier"
const expectedCodeChallenge = "mock_code_challenge"
const generateCodeVerifierSpy = jest.spyOn(utils, "generateCodeVerifier").mockImplementation(() => expectedCodeVerifier)
const createCodeChallengeSpy = jest.spyOn(utils, "createCodeChallenge").mockImplementation(() => expectedCodeChallenge)
authConfig.authConfigs.usePkceWithAuthorizationCodeGrant = true
oauth2Authorize(authConfig)
expect(authConfig.authActions.authPopup.mock.calls.length).toEqual(1)
const actualUrl = new URLSearchParams(authConfig.authActions.authPopup.mock.calls[0][0])
expect(actualUrl.get("code_challenge")).toBe(expectedCodeChallenge)
expect(actualUrl.get("code_challenge_method")).toBe("S256")
expect(createCodeChallengeSpy.mock.calls.length).toEqual(1)
expect(createCodeChallengeSpy.mock.calls[0][0]).toBe(expectedCodeVerifier)
// The code_verifier should be stored to be able to send in
// on the TokenUrl call
expect(authConfig.auth.codeVerifier).toBe(expectedCodeVerifier)
// Restore spies
authConfig.authActions.authPopup.mockReset()
generateCodeVerifierSpy.mockReset()
createCodeChallengeSpy.mockReset()
})
it("should send code_challenge when using accessCode flow with usePkceWithAuthorizationCodeGrant enabled", () => {
mockSchema.flow = "accessCode"
const expectedCodeVerifier = "mock_code_verifier"
const expectedCodeChallenge = "mock_code_challenge"
const generateCodeVerifierSpy = jest.spyOn(utils, "generateCodeVerifier").mockImplementation(() => expectedCodeVerifier)
const createCodeChallengeSpy = jest.spyOn(utils, "createCodeChallenge").mockImplementation(() => expectedCodeChallenge)
authConfig.authConfigs.useBasicAuthenticationWithAccessCodeGrant = true
authConfig.authConfigs.usePkceWithAuthorizationCodeGrant = true
oauth2Authorize(authConfig)
expect(authConfig.authActions.authPopup.mock.calls.length).toEqual(1)
const actualUrl = new URLSearchParams(authConfig.authActions.authPopup.mock.calls[0][0])
expect(actualUrl.get("code_challenge")).toBe(expectedCodeChallenge)
expect(actualUrl.get("code_challenge_method")).toBe("S256")
expect(createCodeChallengeSpy.mock.calls.length).toEqual(1)
expect(createCodeChallengeSpy.mock.calls[0][0]).toBe(expectedCodeVerifier)
// The code_verifier should be stored to be able to send in
// on the TokenUrl call
expect(authConfig.auth.codeVerifier).toBe(expectedCodeVerifier)
// Restore spies
authConfig.authActions.authPopup.mockReset()
generateCodeVerifierSpy.mockReset()
createCodeChallengeSpy.mockReset()
})
it("should send code_challenge when using authorization_code flow with usePkceWithAuthorizationCodeGrant enabled", () => {
mockSchema.flow = "authorization_code"
const expectedCodeVerifier = "mock_code_verifier"
const expectedCodeChallenge = "mock_code_challenge"
const generateCodeVerifierSpy = jest.spyOn(utils, "generateCodeVerifier").mockImplementation(() => expectedCodeVerifier)
const createCodeChallengeSpy = jest.spyOn(utils, "createCodeChallenge").mockImplementation(() => expectedCodeChallenge)
authConfig.authConfigs.usePkceWithAuthorizationCodeGrant = true
oauth2Authorize(authConfig)
expect(authConfig.authActions.authPopup.mock.calls.length).toEqual(1)
const actualUrl = new URLSearchParams(authConfig.authActions.authPopup.mock.calls[0][0])
expect(actualUrl.get("code_challenge")).toBe(expectedCodeChallenge)
expect(actualUrl.get("code_challenge_method")).toBe("S256")
expect(createCodeChallengeSpy.mock.calls.length).toEqual(1)
expect(createCodeChallengeSpy.mock.calls[0][0]).toBe(expectedCodeVerifier)
// The code_verifier should be stored to be able to send in
// on the TokenUrl call
expect(authConfig.auth.codeVerifier).toBe(expectedCodeVerifier)
// Restore spies
authConfig.authActions.authPopup.mockReset()
generateCodeVerifierSpy.mockReset()
createCodeChallengeSpy.mockReset()
})
it("should add list of scopes to authorizeUrl", () => {
mockSchema.authorizationUrl = "https://testAuthorizationUrl?param=1"
oauth2Authorize(authConfig2)
expect(authConfig2.authActions.authPopup.mock.calls.length).toEqual(1)
expect(authConfig2.authActions.authPopup.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?param=1&response_type=code&redirect_uri=&scope=scope2%20scope3&state=")
authConfig2.authActions.authPopup.mockReset()
})
it("should build authorize url for authorization_code flow", () => {
mockSchema.authorizationUrl = "https://testAuthorizationUrl"
mockSchema.flow = "authorization_code"
oauth2Authorize(authConfig3)
expect(authConfig3.authActions.authPopup.mock.calls.length).toEqual(1)
expect(authConfig3.authActions.authPopup.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?response_type=code&redirect_uri=&scope=scope4%20scope5&state=")
authConfig3.authActions.authPopup.mockReset()
})
})
})

View File

@ -0,0 +1,346 @@
import { Map } from "immutable"
import win from "core/window"
import {
authorizeRequest,
authorizeAccessCodeWithFormParams,
authorizeWithPersistOption,
authorizeOauth2WithPersistOption,
logoutWithPersistOption,
persistAuthorizationIfNeeded
} from "corePlugins/auth/actions"
import {authorizeAccessCodeWithBasicAuthentication, authPopup} from "../../../../../src/core/plugins/auth/actions"
describe("auth plugin - actions", () => {
describe("authorizeRequest", () => {
[
[
{
oas3: true,
server: "https://host/resource",
effectiveServer: "https://host/resource",
scheme: "http",
host: null,
url: "http://specs/file",
},
"https://host/authorize"
],
[
{
oas3: true,
server: "https://{selected_host}/resource",
effectiveServer: "https://host/resource",
scheme: "http",
host: null,
url: "http://specs/file",
},
"https://host/authorize"
],
[
{
oas3: false,
server: null,
effectiveServer: null,
scheme: "https",
host: undefined,
url: "https://specs/file",
},
"https://specs/authorize"
],
[
{
oas3: false,
server: null,
effectiveServer: null,
scheme: "https",
host: "host",
url: "http://specs/file",
},
"http://specs/authorize"
],
].forEach(([{ oas3, server, effectiveServer, scheme, host, url }, expectedFetchUrl]) => {
it("should resolve authorization endpoint against the server URL", () => {
// Given
const data = {
url: "/authorize"
}
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({})
},
errActions: {
newAuthErr: () => ({})
},
oas3Selectors: {
selectedServer: () => server,
serverEffectiveValue: () => effectiveServer || server
},
specSelectors: {
isOAS3: () => oas3,
operationScheme: () => scheme,
host: () => host,
url: () => url
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.mock.calls.length).toEqual(1)
expect(system.fn.fetch.mock.calls[0][0]).toEqual(expect.objectContaining({ url: expectedFetchUrl }))
})
})
it("should add additionalQueryStringParams to Swagger 2.0 authorization and token URLs", () => {
// Given
const data = {
url: "/authorize?q=1"
}
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({
additionalQueryStringParams: {
myCustomParam: "abc123"
}
})
},
errActions: {
newAuthErr: () => ({})
},
specSelectors: {
isOAS3: () => false,
operationScheme: () => "https",
host: () => "http://google.com",
url: () => "http://google.com/swagger.json"
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.mock.calls.length).toEqual(1)
expect(system.fn.fetch.mock.calls[0][0].url)
.toEqual("http://google.com/authorize?q=1&myCustomParam=abc123")
})
it("should add additionalQueryStringParams to OpenAPI 3.0 authorization and token URLs", () => {
// Given
const data = {
url: "/authorize?q=1"
}
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
errActions: {
newAuthErr: () => ({})
},
getConfigs: () => ({}),
authSelectors: {
getConfigs: () => ({
additionalQueryStringParams: {
myCustomParam: "abc123"
}
})
},
oas3Selectors: {
selectedServer: () => "http://google.com",
serverEffectiveValue: () => "http://google.com"
},
specSelectors: {
isOAS3: () => true,
}
}
// When
authorizeRequest(data)(system)
// Then
expect(system.fn.fetch.mock.calls.length).toEqual(1)
expect(system.fn.fetch.mock.calls[0][0].url)
.toEqual("http://google.com/authorize?q=1&myCustomParam=abc123")
})
})
describe("tokenRequest", function () {
it("should send the code verifier when set", () => {
const testCodeVerifierForAuthorizationCodeFlows = (flowAction) => {
const data = {
auth: {
schema: {
get: () => "http://tokenUrl",
},
codeVerifier: "mock_code_verifier",
},
redirectUrl: "http://google.com",
}
const authActions = {
authorizeRequest: jest.fn(),
}
flowAction(data)({ authActions })
expect(authActions.authorizeRequest.mock.calls.length).toEqual(1)
const actualArgument = authActions.authorizeRequest.mock.calls[0][0]
expect(actualArgument.body).toContain("code_verifier=" + data.auth.codeVerifier)
expect(actualArgument.body).toContain("grant_type=authorization_code")
}
testCodeVerifierForAuthorizationCodeFlows(authorizeAccessCodeWithFormParams)
testCodeVerifierForAuthorizationCodeFlows(authorizeAccessCodeWithBasicAuthentication)
})
})
describe("persistAuthorization", () => {
describe("wrapped functions with persist option", () => {
it("should wrap `authorize` action and persist data if needed", () => {
// Given
const data = {
"api_key": {}
}
const system = {
getConfigs: () => ({}),
authActions: {
authorize: jest.fn(() => { }),
persistAuthorizationIfNeeded: jest.fn(() => { })
}
}
// When
authorizeWithPersistOption(data)(system)
// Then
expect(system.authActions.authorize).toHaveBeenCalled()
expect(system.authActions.authorize).toHaveBeenCalledWith(data)
expect(system.authActions.persistAuthorizationIfNeeded).toHaveBeenCalled()
})
it("should wrap `oauth2Authorize` action and persist data if needed", () => {
// Given
const data = {
"api_key": {}
}
const system = {
getConfigs: () => ({}),
authActions: {
authorizeOauth2: jest.fn(() => { }),
persistAuthorizationIfNeeded: jest.fn(() => { })
}
}
// When
authorizeOauth2WithPersistOption(data)(system)
// Then
expect(system.authActions.authorizeOauth2).toHaveBeenCalled()
expect(system.authActions.authorizeOauth2).toHaveBeenCalledWith(data)
expect(system.authActions.persistAuthorizationIfNeeded).toHaveBeenCalled()
})
it("should wrap `logout` action and persist data if needed", () => {
// Given
const data = {
"api_key": {}
}
const system = {
getConfigs: () => ({}),
authActions: {
logout: jest.fn(() => { }),
persistAuthorizationIfNeeded: jest.fn(() => { })
}
}
// When
logoutWithPersistOption(data)(system)
// Then
expect(system.authActions.logout).toHaveBeenCalled()
expect(system.authActions.logout).toHaveBeenCalledWith(data)
expect(system.authActions.persistAuthorizationIfNeeded).toHaveBeenCalled()
})
})
describe("persistAuthorizationIfNeeded", () => {
beforeEach(() => {
localStorage.clear()
})
it("should skip if `persistAuthorization` is turned off", () => {
// Given
const system = {
getConfigs: () => ({
persistAuthorization: false
}),
authSelectors: {
authorized: jest.fn(() => { })
}
}
// When
persistAuthorizationIfNeeded()(system)
// Then
expect(system.authSelectors.authorized).not.toHaveBeenCalled()
})
it("should persist authorization data to localStorage", () => {
// Given
const data = {
"api_key": {}
}
const system = {
getConfigs: () => ({
persistAuthorization: true
}),
errActions: {
newAuthErr: () => ({})
},
authSelectors: {
authorized: jest.fn(() => Map(data))
}
}
jest.spyOn(Object.getPrototypeOf(window.localStorage), "setItem")
// When
persistAuthorizationIfNeeded()(system)
expect(localStorage.setItem).toHaveBeenCalled()
expect(localStorage.setItem).toHaveBeenCalledWith("authorized", JSON.stringify(data))
})
})
describe("authPopup", () => {
beforeEach(() => {
win.open = jest.fn()
})
it("should call win.open with url", () => {
const windowOpenSpy = jest.spyOn(win, "open")
authPopup("http://swagger.ui", {})()
expect(windowOpenSpy.mock.calls.length).toEqual(1)
windowOpenSpy.mockReset()
})
})
})
})

View File

@ -0,0 +1,153 @@
import { fromJS } from "immutable"
import { preauthorizeBasic, preauthorizeApiKey } from "corePlugins/auth"
import { authorize } from "corePlugins/auth/actions"
const S2_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => false,
specJson: () => {
return fromJS({
swagger: "2.0",
securityDefinitions: {
"APIKeyHeader": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
},
"basicAuth": {
"type": "basic"
}
}
})
}
}
}
const OAI3_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => true,
specJson: () => {
return fromJS({
openapi: "3.0.0",
components: {
securitySchemes: {
basicAuth: {
type: "http",
scheme: "basic"
},
APIKeyHeader: {
type: "apiKey",
in: "header",
name: "X-API-Key"
}
}
}
})
}
}
}
describe("auth plugin - preauthorizers", () => {
describe("preauthorizeBasic", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "http",
scheme: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
})
describe("preauthorizeApiKey", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
name: "X-API-Key",
"in": "header"
},
value: "Asdf1234"
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
"in": "header",
name: "X-API-Key"
},
value: "Asdf1234"
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
})
})

View File

@ -0,0 +1,228 @@
import { fromJS } from "immutable"
import { definitionsToAuthorize, definitionsForRequirements } from "corePlugins/auth/selectors"
describe("auth plugin - selectors", () => {
describe("definitionsToAuthorize", () => {
it("should return securityDefinitions as a List", () => {
const securityDefinitions = {
"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"
}
}
const system = {
specSelectors: {
securityDefinitions() {
return fromJS(securityDefinitions)
}
}
}
const res = definitionsToAuthorize({})(system)
expect(res.toJS()).toEqual([
{
"petstore_auth": securityDefinitions["petstore_auth"]
},
{
"api_key": securityDefinitions["api_key"]
},
])
})
it("should fail gracefully with bad data", () => {
const securityDefinitions = null
const system = {
specSelectors: {
securityDefinitions() {
return fromJS(securityDefinitions)
}
}
}
const res = definitionsToAuthorize({})(system)
expect(res.toJS()).toEqual([])
})
})
describe("definitionsForRequirements", () => {
it("should return applicable securityDefinitions as a List", () => {
const securityDefinitions = {
"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"
}
}
const system = {
authSelectors: {
definitionsToAuthorize() {
return fromJS([
{
"petstore_auth": securityDefinitions["petstore_auth"]
},
{
"api_key": securityDefinitions["api_key"]
},
])
}
}
}
const securities = fromJS([
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
])
const res = definitionsForRequirements({}, securities)(system)
expect(res.toJS()).toEqual([
{
"petstore_auth": securityDefinitions["petstore_auth"]
}
])
})
it("should fail gracefully with bad data", () => {
const securityDefinitions = null
const system = {
authSelectors: {
definitionsToAuthorize() {
return null
}
}
}
const securities = null
const res = definitionsForRequirements({}, securities)(system)
expect(res.toJS()).toEqual([])
})
})
it("should return only security definitions used by the endpoint", () => {
const securityDefinitions = {
"used": {
"type": "http",
"scheme": "basic",
},
"unused": {
"type": "http",
"scheme": "basic",
}
}
const system = {
authSelectors: {
definitionsToAuthorize() {
return fromJS([
{
"used": securityDefinitions["used"]
},
{
"unused": securityDefinitions["unused"]
},
])
}
}
}
const securities = fromJS([
{
"used": [],
"undefined": [],
}
])
const res = definitionsForRequirements({}, securities)(system)
expect(res.toJS()).toEqual([
{
"used": securityDefinitions["used"]
}
])
})
it("should return only oauth scopes used by the endpoint", () => {
const securityDefinitions = {
"oauth2": {
"type": "oauth2",
"flow": "clientCredentials",
"tokenUrl": "https://api.testserver.com/oauth2/token/",
"scopes": {
"used": "foo",
"unused": "bar"
}
},
"other": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
}
const system = {
authSelectors: {
definitionsToAuthorize() {
return fromJS([
{
"oauth2": securityDefinitions["oauth2"],
"other": securityDefinitions["other"],
},
])
}
}
}
const securities = fromJS([
{
"oauth2": ["used", "undefined"],
"other": [],
}
])
let expectedOauth2Definitions = {...securityDefinitions["oauth2"]}
expectedOauth2Definitions["scopes"] = {"used": "foo"}
const res = definitionsForRequirements({}, securities)(system)
expect(res.toJS()).toEqual([
{
"oauth2": expectedOauth2Definitions,
"other": securityDefinitions["other"]
}
])
})
})

View File

@ -0,0 +1,40 @@
import { execute } from "corePlugins/auth/spec-wrap-actions"
describe("spec plugin - actions", function(){
describe("execute", function(){
xit("should add `securities` to the oriAction call", function(){
// Given
const system = {
authSelectors: {
authorized: jest.fn().mockImplementation(() => ({
some: "security"
}))
}
}
const oriExecute = jest.fn()
// When
let executeFn = execute(oriExecute, system)
executeFn({})
// Then
expect(oriExecute.mock.calls.length).toEqual(1)
expect(oriExecute.mock.calls[0][0]).toEqual({
extras: {
security: {
some: "security"
}
},
method: undefined,
path: undefined,
operation: undefined
})
})
})
})

View File

@ -0,0 +1,65 @@
import { downloadConfig } from "corePlugins/configs/spec-actions"
import { loaded } from "corePlugins/configs/actions"
describe("configs plugin - actions", () => {
describe("downloadConfig", () => {
it("should call the system fetch helper with a provided request", () => {
const fetchSpy = jest.fn(async () => {})
const system = {
fn: {
fetch: fetchSpy
}
}
const req = {
url: "http://swagger.io/one",
requestInterceptor: a => a,
responseInterceptor: a => a,
}
downloadConfig(req)(system)
expect(fetchSpy).toHaveBeenCalledWith(req)
})
})
describe("loaded hook", () => {
describe("authorization data restoration", () => {
beforeEach(() => {
localStorage.clear()
})
it("retrieve `authorized` value from `localStorage`", () => {
const system = {
getConfigs: () => ({
persistAuthorization: true
}),
authActions: {
}
}
jest.spyOn(Object.getPrototypeOf(window.localStorage), "getItem")
loaded()(system)
expect(localStorage.getItem).toHaveBeenCalled()
expect(localStorage.getItem).toHaveBeenCalledWith("authorized")
})
it("restore authorization data when a value exists", () => {
const system = {
getConfigs: () => ({
persistAuthorization: true
}),
authActions: {
restoreAuthorization: jest.fn(() => {})
}
}
const mockData = {"api_key": {}}
localStorage.setItem("authorized", JSON.stringify(mockData))
loaded()(system)
expect(system.authActions.restoreAuthorization).toHaveBeenCalled()
expect(system.authActions.restoreAuthorization).toHaveBeenCalledWith({
authorized: mockData
})
})
})
})
})

View File

@ -0,0 +1,54 @@
import { Map, List } from "immutable"
import { transform } from "corePlugins/err/error-transformers/transformers/not-of-type"
describe("err plugin - tranformers - not of type", () => {
it("should transform a singular not of type(s) error without an inline path", () => {
let ori = List([
Map({
path: "info.version",
message: "is not of a type(s) string"
})
])
let res = transform(ori).toJS()
expect(res).toEqual([{
path: "info.version",
message: "should be a string"
}])
})
it("should transform a plural (2) not of type(s) error without an inline path", () => {
let ori = List([
Map({
path: "info.version",
message: "is not of a type(s) string,array"
})
])
let res = transform(ori).toJS()
expect(res).toEqual([{
path: "info.version",
message: "should be a string or array"
}])
})
it("should transform a plural (3+) not of type(s) error without an inline path", () => {
let ori = List([
Map({
path: "info.version",
message: "is not of a type(s) string,array,number"
})
])
let res = transform(ori).toJS()
expect(res).toEqual([{
path: "info.version",
message: "should be a string, array, or number"
}])
})
})

View File

@ -0,0 +1,131 @@
/* eslint-disable no-useless-escape */
import { fromJS } from "immutable"
import { transform } from "corePlugins/err/error-transformers/transformers/parameter-oneof"
describe.skip("err plugin - tranformers - parameter oneof", () => {
describe("parameter.in misuse transformation to fixed value error", () => {
it("should return a helpful error for invalid 'in' values", () => {
const jsSpec = {
paths: {
"/CoolPath/": {
get: {
parameters: [
{
name: "id",
in: "heder"
}
]
}
}
}
}
const jsonSchemaError = {
"level": "error",
"path": "paths.\/CoolPath\/.get.parameters[0]",
"message": "is not exactly one from <#\/definitions\/parameter>,<#\/definitions\/jsonReference>",
"source": "schema",
"type": "spec"
}
let res = transform(fromJS([jsonSchemaError]), { jsSpec })
expect(res.toJS()).toEqual([{
path: "paths./CoolPath/.get.parameters[0].in",
message: "Wrong value for the \"in\" keyword. Expected one of: path, query, header, body, formData.",
level: "error",
source: "schema",
type: "spec"
}])
})
})
describe("parameter.collectionFormat misuse transformation to fixed value error", () => {
it("should return a helpful error for invalid 'collectionFormat' values", () => {
const jsSpec = {
paths: {
"/CoolPath/": {
get: {
parameters: [
{
name: "id",
in: "query",
collectionFormat: "asdf"
}
]
}
}
}
}
const jsonSchemaError = {
"level": "error",
"path": "paths.\/CoolPath\/.get.parameters[0]",
"message": "is not exactly one from <#\/definitions\/parameter>,<#\/definitions\/jsonReference>",
"source": "schema",
"type": "spec"
}
let res = transform(fromJS([jsonSchemaError]), { jsSpec })
expect(res.toJS()).toEqual([{
path: "paths./CoolPath/.get.parameters[0].collectionFormat",
message: "Wrong value for the \"collectionFormat\" keyword. Expected one of: csv, ssv, tsv, pipes, multi.",
level: "error",
source: "schema",
type: "spec"
}])
})
})
describe("Integrations", () => {
it("should return the correct errors when both 'in' and 'collectionFormat' are incorrect", () => {
const jsSpec = {
paths: {
"/CoolPath/": {
get: {
parameters: [
{
name: "id",
in: "blah",
collectionFormat: "asdf"
}
]
}
}
}
}
const jsonSchemaError = {
"level": "error",
"path": "paths.\/CoolPath\/.get.parameters[0]",
"message": "is not exactly one from <#\/definitions\/parameter>,<#\/definitions\/jsonReference>",
"source": "schema",
"type": "spec"
}
let res = transform(fromJS([jsonSchemaError]), { jsSpec })
expect(res.toJS()).toEqual([
{
path: "paths./CoolPath/.get.parameters[0].in",
message: "Wrong value for the \"in\" keyword. Expected one of: path, query, header, body, formData.",
level: "error",
source: "schema",
type: "spec"
},
{
path: "paths./CoolPath/.get.parameters[0].collectionFormat",
message: "Wrong value for the \"collectionFormat\" keyword. Expected one of: csv, ssv, tsv, pipes, multi.",
level: "error",
source: "schema",
type: "spec"
}
])
})
})
})

View File

@ -0,0 +1,24 @@
import { Map } from "immutable"
import opsFilter from "corePlugins/filter/opsFilter"
describe("opsFilter", function() {
const taggedOps = Map([["pet"], ["store"], ["user"]])
it("should filter taggedOps by tag name", function () {
const filtered = opsFilter(taggedOps, "sto")
expect(filtered.size).toEqual(1)
})
it("should return all taggedOps when search phrase is empty", function () {
const filtered = opsFilter(taggedOps, "")
expect(filtered.size).toEqual(taggedOps.size)
})
it("should return empty result when there is no match", function () {
const filtered = opsFilter(taggedOps, "NoMatch")
expect(filtered.size).toEqual(0)
})
})

View File

@ -0,0 +1,67 @@
import { fromJS } from "immutable"
import { isOAS3, isSwagger2 } from "corePlugins/oas3/helpers"
const isOAS3Shorthand = (version) => isOAS3(fromJS({
openapi: version
}))
const isSwagger2Shorthand = (version) => isSwagger2(fromJS({
swagger: version
}))
describe("isOAS3", function () {
it("should recognize valid OAS3 version values", function () {
expect(isOAS3Shorthand("3.0.0")).toEqual(true)
expect(isOAS3Shorthand("3.0.1")).toEqual(true)
expect(isOAS3Shorthand("3.0.11111")).toEqual(true)
expect(isOAS3Shorthand("3.0.0-rc0")).toEqual(true)
})
it("should fail for invalid OAS3 version values", function () {
expect(isOAS3Shorthand("3.0")).toEqual(false)
expect(isOAS3Shorthand("3.0.")).toEqual(false)
expect(isOAS3Shorthand("2.0")).toEqual(false)
})
it("should gracefully fail for non-string values", function () {
expect(isOAS3Shorthand(3.0)).toEqual(false)
expect(isOAS3Shorthand(3)).toEqual(false)
expect(isOAS3Shorthand({})).toEqual(false)
expect(isOAS3Shorthand(null)).toEqual(false)
})
it("should gracefully fail when `openapi` field is missing", function () {
expect(isOAS3(fromJS({
openApi: "3.0.0"
}))).toEqual(false)
expect(isOAS3Shorthand(null)).toEqual(false)
})
})
describe("isSwagger2", function () {
it("should recognize valid Swagger 2.0 version values", function () {
expect(isSwagger2Shorthand("2.0")).toEqual(true)
expect(isSwagger2Shorthand("2.0-rc0")).toEqual(true)
})
it("should fail for invalid Swagger 2.0 version values", function () {
expect(isSwagger2Shorthand("3.0")).toEqual(false)
expect(isSwagger2Shorthand("3.0.")).toEqual(false)
expect(isSwagger2Shorthand("2.1")).toEqual(false)
expect(isSwagger2Shorthand("1.2")).toEqual(false)
expect(isSwagger2Shorthand("2")).toEqual(false)
})
it("should gracefully fail for non-string values", function () {
expect(isSwagger2Shorthand(2.0)).toEqual(false)
expect(isSwagger2Shorthand(2)).toEqual(false)
expect(isSwagger2Shorthand({})).toEqual(false)
expect(isSwagger2Shorthand(null)).toEqual(false)
})
it("should gracefully fail when `swagger` field is missing", function () {
expect(isSwagger2(fromJS({
Swagger: "2.0"
}))).toEqual(false)
})
})

View File

@ -0,0 +1,582 @@
import { fromJS } from "immutable"
import reducer from "corePlugins/oas3/reducers"
describe("oas3 plugin - reducer", function () {
describe("SET_REQUEST_BODY_VALIDATE_ERROR", () => {
const setRequestBodyValidateError = reducer["oas3_set_request_body_validate_error"]
describe("missingBodyValue exists, e.g. application/json", () => {
it("should set errors", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: "",
requestContentType: "application/json"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: true,
missingRequiredKeys: []
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: "",
requestContentType: "application/json",
errors: ["Required field is not provided"]
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("missingRequiredKeys exists with length, e.g. application/x-www-form-urleconded", () => {
it("should set nested errors", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
missingRequiredKeys: ["name"]
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
errors: ["Required field is not provided"]
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
it("should overwrite nested errors, for keys listed in missingRequiredKeys", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
errors: ["some fake error"]
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
missingRequiredKeys: ["name"]
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
errors: ["Required field is not provided"]
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
it("should not overwrite nested errors, for keys not listed in missingRequiredKeys", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
errors: ["random error should not be overwritten"]
},
name: {
value: "",
errors: ["some fake error"]
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
missingRequiredKeys: ["name"]
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
errors: ["random error should not be overwritten"]
},
name: {
value: "",
errors: ["Required field is not provided"]
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
it("should set multiple nested errors", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
missingRequiredKeys: ["id", "name"]
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "",
errors: ["Required field is not provided"]
},
name: {
value: "",
errors: ["Required field is not provided"]
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("missingRequiredKeys is empty list", () => {
it("should not set any errors, and return state unchanged", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
missingRequiredKeys: []
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("other unexpected payload, e.g. no missingBodyValue or missingRequiredKeys", () => {
it("should not throw error if receiving unexpected validationError format. return state unchanged", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = setRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
validationErrors: {
missingBodyValue: null,
// missingRequiredKeys: ["none provided"]
},
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
name: {
value: "",
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
})
describe("CLEAR_REQUEST_BODY_VALIDATE_ERROR", function() {
const clearRequestBodyValidateError = reducer["oas3_clear_request_body_validate_error"]
describe("bodyValue is String, e.g. application/json", () => {
it("should clear errors", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: "{}",
requestContentType: "application/json"
}
}
}
})
const result = clearRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: "{}",
requestContentType: "application/json",
errors: []
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("bodyValue is Map with entries, e.g. application/x-www-form-urleconded", () => {
it("should clear nested errors, and apply empty error list to all entries", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
errors: ["some random error"]
},
name: {
value: "doggie",
errors: ["Required field is not provided"]
},
status: {
value: "available"
}
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = clearRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
errors: [],
},
name: {
value: "doggie",
errors: [],
},
status: {
value: "available",
errors: [],
},
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("bodyValue is empty Map", () => {
it("should return state unchanged", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = clearRequestBodyValidateError(state, {
payload: {
path: "/pet",
method: "post",
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {
},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
})
describe("CLEAR_REQUEST_BODY_VALUE", function () {
const clearRequestBodyValue = reducer["oas3_clear_request_body_value"]
describe("when requestBodyValue is a String", () => {
it("should clear requestBodyValue with empty String", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: "some random string",
requestContentType: "application/json"
}
}
}
})
const result = clearRequestBodyValue(state, {
payload: {
pathMethod: ["/pet", "post"],
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: "",
requestContentType: "application/json",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("when requestBodyValue is a Map", () => {
it("should clear requestBodyValue with empty Map", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = clearRequestBodyValue(state, {
payload: {
pathMethod: ["/pet", "post"],
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
})
})

View File

@ -0,0 +1,75 @@
import React from "react"
import { mount } from "enzyme"
import { fromJS } from "immutable"
import ServersContainer from "core/plugins/oas3/components/servers-container"
import Servers from "core/plugins/oas3/components/servers"
import { Col } from "components/layout-utils"
describe("<ServersContainer/>", function(){
const components = {
Servers,
Col
}
const mockedProps = {
specSelectors: {
servers() {}
},
oas3Selectors: {
selectedServer() {},
serverVariableValue() {},
serverEffectiveValue() {}
},
oas3Actions: {
setSelectedServer() {},
setServerVariableValue() {}
},
getComponent: c => components[c]
}
it("renders Servers inside ServersContainer if servers are provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.servers = function() {return fromJS([{url: "http://server1.com"}])}
props.oas3Selectors = {...mockedProps.oas3Selectors}
props.oas3Selectors.selectedServer = function() {return "http://server1.com"}
// When
let wrapper = mount(<ServersContainer {...props}/>)
// Then
const renderedServers = wrapper.find(Servers)
expect(renderedServers.length).toEqual(1)
})
it("does not render Servers inside ServersContainer if servers are empty", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.servers = function() {return fromJS([])}
// When
let wrapper = mount(<ServersContainer {...props}/>)
// Then
const renderedServers = wrapper.find(Servers)
expect(renderedServers.length).toEqual(0)
})
it("does not render Servers inside ServersContainer if servers are undefined", function(){
// Given
let props = {...mockedProps}
// When
let wrapper = mount(<ServersContainer {...props}/>)
// Then
const renderedServers = wrapper.find(Servers)
expect(renderedServers.length).toEqual(0)
})
})

View File

@ -0,0 +1,358 @@
import { fromJS, OrderedMap } from "immutable"
import {
selectedServer,
serverVariableValue,
serverVariables,
serverEffectiveValue
} from "corePlugins/oas3/selectors"
import reducers from "corePlugins/oas3/reducers"
import {
setSelectedServer,
setServerVariableValue,
} from "corePlugins/oas3/actions"
describe("OAS3 plugin - state", function() {
describe("action + reducer + selector integration", function() {
describe("selectedServer", function() {
it("should set and get a global selectedServer", function() {
const state = new OrderedMap()
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setSelectedServer("http://google.com")
// Collect the new state
const newState = reducers["oas3_set_servers"](state, action)
// Get the value with the selector
const res = selectedServer(newState)(system)
expect(res).toEqual("http://google.com")
})
it("should set and get a namespaced selectedServer", function() {
const state = fromJS({
selectedServer: "http://yahoo.com"
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setSelectedServer("http://google.com", "myOperation")
// Collect the new state
const newState = reducers["oas3_set_servers"](state, action)
// Get the value with the selector
const res = selectedServer(newState, "myOperation")(system)
// Get the global selected server
const globalRes = selectedServer(newState)(system)
expect(res).toEqual("http://google.com")
expect(globalRes).toEqual("http://yahoo.com")
})
})
describe("serverVariableValue", function() {
it("should set and get a global serverVariableValue", function() {
const state = new OrderedMap()
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setServerVariableValue({
server: "google.com",
key: "foo",
val: "bar"
})
// Collect the new state
const newState = reducers["oas3_set_server_variable_value"](state, action)
// Get the value with the selector
const res = serverVariableValue(newState, "google.com", "foo")(system)
expect(res).toEqual("bar")
})
it("should set and get a namespaced serverVariableValue", function() {
const state = fromJS({
serverVariableValues: {
"google.com": {
foo: "123"
}
}
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setServerVariableValue({
namespace: "myOperation",
server: "google.com",
key: "foo",
val: "bar"
})
// Collect the new state
const newState = reducers["oas3_set_server_variable_value"](state, action)
// Get the value with the selector
const res = serverVariableValue(newState, {
namespace: "myOperation",
server: "google.com"
}, "foo")(system)
// Get the global value, to cross-check
const globalRes = serverVariableValue(newState, {
server: "google.com"
}, "foo")(system)
expect(res).toEqual("bar")
expect(globalRes).toEqual("123")
})
})
describe("serverVariables", function() {
it("should set and get global serverVariables", function() {
const state = new OrderedMap()
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setServerVariableValue({
server: "google.com",
key: "foo",
val: "bar"
})
// Collect the new state
const newState = reducers["oas3_set_server_variable_value"](state, action)
// Get the value with the selector
const res = serverVariables(newState, "google.com", "foo")(system)
expect(res.toJS()).toEqual({
foo: "bar"
})
})
it("should set and get namespaced serverVariables", function() {
const state = fromJS({
serverVariableValues: {
"google.com": {
foo: "123"
}
}
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setServerVariableValue({
namespace: "myOperation",
server: "google.com",
key: "foo",
val: "bar"
})
// Collect the new state
const newState = reducers["oas3_set_server_variable_value"](state, action)
// Get the value with the selector
const res = serverVariables(newState, {
namespace: "myOperation",
server: "google.com"
}, "foo")(system)
// Get the global value, to cross-check
const globalRes = serverVariables(newState, {
server: "google.com"
}, "foo")(system)
expect(res.toJS()).toEqual({
foo: "bar"
})
expect(globalRes.toJS()).toEqual({
foo: "123"
})
})
})
describe("serverEffectiveValue", function() {
it("should set variable values and compute a URL for a namespaced server", function() {
const state = fromJS({
serverVariableValues: {
"google.com/{foo}": {
foo: "123"
}
}
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Create the action
const action = setServerVariableValue({
namespace: "myOperation",
server: "google.com/{foo}",
key: "foo",
val: "bar"
})
// Collect the new state
const newState = reducers["oas3_set_server_variable_value"](state, action)
// Get the value with the selector
const res = serverEffectiveValue(newState, {
namespace: "myOperation",
server: "google.com/{foo}"
})(system)
// Get the global value, to cross-check
const globalRes = serverEffectiveValue(newState, {
server: "google.com/{foo}"
})(system)
expect(res).toEqual("google.com/bar")
expect(globalRes).toEqual("google.com/123")
})
})
})
describe("selectors", function() {
describe("serverEffectiveValue", function() {
it("should compute global serverEffectiveValues", function() {
const state = fromJS({
serverVariableValues: {
"google.com/{foo}/{bar}": {
foo: "123",
bar: "456"
}
}
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Get the value with the selector
const res = serverEffectiveValue(state, "google.com/{foo}/{bar}")(system)
expect(res).toEqual("google.com/123/456")
})
it("should handle multiple variable instances", function() {
const state = fromJS({
serverVariableValues: {
"google.com/{foo}/{foo}/{bar}": {
foo: "123",
bar: "456"
}
}
})
const system = {
// needed to handle `onlyOAS3` wrapper
getSystem() {
return {
specSelectors: {
specJson: () => {
return fromJS({ openapi: "3.0.0" })
}
}
}
}
}
// Get the value with the selector
const res = serverEffectiveValue(state, "google.com/{foo}/{foo}/{bar}")(system)
expect(res).toEqual("google.com/123/123/456")
})
})
})
})

View File

@ -0,0 +1,240 @@
import { fromJS, Map } from "immutable"
import {
definitionsToAuthorize
} from "corePlugins/oas3/auth-extensions/wrap-selectors"
describe("oas3 plugin - auth extensions - wrapSelectors", function(){
describe("execute", function(){
it("should add `securities` to the oriAction call", function(){
// Given
const system = {
getSystem: () => system,
getState: () => new Map(),
specSelectors: {
specJson: () => fromJS({
openapi: "3.0.0"
}),
securityDefinitions: () => {
return fromJS({
"oauth2AuthorizationCode": {
"type": "oauth2",
"description": "Some Oauth2 endpoint",
"flows": {
"authorizationCode": {
"authorizationUrl": "http://google.com/",
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
}
}
},
"oauth2Multiflow": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
},
"password": {
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
},
"authorizationCode": {
"authorizationUrl": "http://google.com/",
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
}
}
},
"oidc": {
"type": "openIdConnect",
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
"openIdConnectData": {
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"scopes_supported": [
"openid",
"email",
"profile"
],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
]
}
},
"oidcNoGrant": {
"type": "openIdConnect",
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
"openIdConnectData": {
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"scopes_supported": [
"openid",
"email",
"profile"
]
},
},
})
}
}
}
// When
let res = definitionsToAuthorize(() => null, system)()
// Then
expect(res.toJS()).toEqual([
{
oauth2AuthorizationCode: {
flow: "authorizationCode",
authorizationUrl: "http://google.com/",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2",
description: "Some Oauth2 endpoint"
}
},
{
oauth2Multiflow: {
flow: "clientCredentials",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oauth2Multiflow: {
flow: "password",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oauth2Multiflow: {
flow: "authorizationCode",
authorizationUrl: "http://google.com/",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oidc: {
flow: "authorization_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "refresh_token",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "urn:ietf:params:oauth:grant-type:device_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "urn:ietf:params:oauth:grant-type:jwt-bearer",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
// grant_types_supported
// OPTIONAL. JSON array containing a list of the OAuth 2.0 Grant Type values that
// this OP supports. Dynamic OpenID Providers MUST support the authorization_code
// and implicit Grant Type values and MAY support other Grant Types. If omitted,
// the default value is ["authorization_code", "implicit"].
oidcNoGrant: {
flow: "authorization_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidcNoGrant: {
flow: "implicit",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
])
})
})
})

View File

@ -0,0 +1,98 @@
import { fromJS } from "immutable"
import {
definitions
} from "corePlugins/oas3/spec-extensions/wrap-selectors"
describe("oas3 plugin - spec extensions - wrapSelectors", function(){
describe("definitions", function(){
it("should return definitions by default", function () {
// Given
const spec = fromJS({
openapi: "3.0.0",
components: {
schemas: {
a: {
type: "string"
},
b: {
type: "string"
}
}
}
})
const system = {
getSystem: () => system,
specSelectors: {
specJson: () => spec,
}
}
// When
let res = definitions(() => null, system)(fromJS({
json: spec
}))
// Then
expect(res.toJS()).toEqual({
a: {
type: "string"
},
b: {
type: "string"
}
})
})
it("should return an empty Map when missing definitions", function () {
// Given
const spec = fromJS({
openapi: "3.0.0"
})
const system = {
getSystem: () => system,
specSelectors: {
specJson: () => spec,
}
}
// When
let res = definitions(() => null, system)(fromJS({
json: spec
}))
// Then
expect(res.toJS()).toEqual({})
})
it("should return an empty Map when given non-object definitions", function () {
// Given
const spec = fromJS({
openapi: "3.0.0",
components: {
schemas: "..."
}
})
const system = {
getSystem: () => system,
specSelectors: {
specJson: () => spec,
}
}
// When
let res = definitions(() => null, system)(fromJS({
json: spec
}))
// Then
expect(res.toJS()).toEqual({})
})
})
})

View File

@ -0,0 +1,253 @@
import React from "react"
import { mount } from "enzyme"
import sinon from "sinon"
import { Provider } from "react-redux"
import noop from "lodash/noop"
import System from "core/system"
import ViewPlugin from "core/plugins/view"
import SafeRenderPlugin from "core/plugins/safe-render"
describe("safe-render", function() {
const DisableComponentDidCatchPlugin = () => ({
fn: {
componentDidCatch: noop,
}
})
it("should catch errors thrown inside of React Component class render method", function() {
class BrokenComponent extends React.Component {
render() {
return null
}
}
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent")
const wrapper = mount(<SafeBrokenComponent />)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of PureComponent class render method", function() {
class BrokenComponent extends React.PureComponent {
render() {
return null
}
}
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent")
const wrapper = mount(<SafeBrokenComponent />)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of function component", function() {
const BrokenComponent = () => null
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent")
const wrapper = mount(<SafeBrokenComponent />)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of container created from React Component class", function() {
class BrokenComponent extends React.Component {
render() {
return null
}
}
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent", true)
const wrapper = mount(
<Provider store={system.getStore()}>
<SafeBrokenComponent />
</Provider>
)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of container created from function component", function() {
const BrokenComponent = () => null
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent", true)
const wrapper = mount(
<Provider store={system.getStore()}>
<SafeBrokenComponent />
</Provider>
)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should render custom Fallback component", function() {
const BrokenComponent = () => null
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const FallbackPlugin = () => ({
components: {
Fallback: () => "fallback component",
},
})
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
FallbackPlugin,
DisableComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent")
const wrapper = mount(<SafeBrokenComponent />)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(wrapper.text()).toEqual("fallback component")
})
it("should call custom componentDidCatch hook", function() {
const BrokenComponent = () => null
const componentDidCatch = sinon.spy()
const BrokenComponentPlugin = () => {
return {
components: {
BrokenComponent,
}
}
}
const ComponentDidCatchPlugin = () => ({
fn: {
componentDidCatch,
},
})
const system = new System({
plugins: [
ViewPlugin,
BrokenComponentPlugin,
SafeRenderPlugin({
fullOverride: true,
componentList: ["BrokenComponent"],
}),
ComponentDidCatchPlugin,
]
})
const SafeBrokenComponent = system.getSystem().getComponent("BrokenComponent")
const wrapper = mount(<SafeBrokenComponent />)
wrapper.find(BrokenComponent).simulateError(new Error("error"))
expect(componentDidCatch.calledOnce).toBe(true)
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
import { fromJS } from "immutable"
import { execute, executeRequest, changeParamByIdentity, updateEmptyParamInclusion } from "corePlugins/spec/actions"
describe("spec plugin - actions", function(){
describe("execute", function(){
xit("should collect a full request and call fn.executeRequest", function(){
// Given
const system = {
fn: {
fetch: 1
},
specActions: {
executeRequest: jest.fn()
},
specSelectors: {
spec: () => fromJS({spec: 1}),
parameterValues: () => fromJS({values: 2}),
contentTypeValues: () => fromJS({requestContentType: "one", responseContentType: "two"})
}
}
// When
let executeFn = execute({ path: "/one", method: "get"})
executeFn(system)
// Then
expect(system.specActions.executeRequest.calls[0][0]).toEqual({
fetch: 1,
method: "get",
pathName: "/one",
parameters: {
values: 2
},
requestContentType: "one",
responseContentType: "two",
spec: {
spec: 1
}
})
})
xit("should allow passing _extra_ properties to executeRequest", function(){
// Given
const system = {
fn: {},
specActions: {
executeRequest: jest.fn()
},
specSelectors: {
spec: () => fromJS({}),
parameterValues: () => fromJS({}),
contentTypeValues: () => fromJS({})
}
}
// When
let executeFn = execute({ hi: "hello" })
executeFn(system)
// Then
expect(system.specActions.executeRequest.calls[0][0]).toContain({hi: "hello"})
})
})
describe("executeRequest", function(){
xit("should call fn.execute with arg ", function(){
const system = {
fn: {
execute: jest.fn().mockImplementation(() => Promise.resolve({}))
},
specActions: {
setResponse: jest.fn()
}
}
// When
let executeFn = executeRequest({one: 1})
let res = executeFn(system)
// Then
expect(res).toBeInstanceOf(Promise)
expect(system.fn.execute.mock.calls.length).toEqual(1)
expect(system.fn.execute.mock.calls[0][0]).toEqual({
one: 1
})
})
it("should pass requestInterceptor/responseInterceptor to fn.execute", async () => {
// Given
let configs = {
requestInterceptor: jest.fn(),
responseInterceptor: jest.fn()
}
const system = {
fn: {
buildRequest: jest.fn(),
execute: jest.fn().mockImplementation(() => Promise.resolve({}))
},
specActions: {
executeRequest: jest.fn(),
setMutatedRequest: jest.fn(),
setRequest: jest.fn(),
setResponse: jest.fn()
},
specSelectors: {
spec: () => fromJS({}),
parameterValues: () => fromJS({}),
contentTypeValues: () => fromJS({}),
url: () => fromJS({}),
isOAS3: () => false
},
getConfigs: () => configs
}
// When
let executeFn = executeRequest({
pathName: "/one",
method: "GET",
operation: fromJS({operationId: "getOne"})
})
await executeFn(system)
// Then
expect(system.fn.execute.mock.calls.length).toEqual(1)
expect(Object.keys(system.fn.execute.mock.calls[0][0])).toContain("requestInterceptor")
expect(system.fn.execute.mock.calls[0][0]).toEqual(expect.objectContaining({
responseInterceptor: configs.responseInterceptor
}))
expect(system.specActions.setMutatedRequest.mock.calls.length).toEqual(0)
expect(system.specActions.setRequest.mock.calls.length).toEqual(1)
let wrappedRequestInterceptor = system.fn.execute.mock.calls[0][0].requestInterceptor
await wrappedRequestInterceptor(system.fn.execute.mock.calls[0][0])
expect(configs.requestInterceptor.mock.calls.length).toEqual(1)
expect(system.specActions.setMutatedRequest.mock.calls.length).toEqual(1)
expect(system.specActions.setRequest.mock.calls.length).toEqual(1)
})
})
xit("should call specActions.setResponse, when fn.execute resolves", function(){
const response = {serverResponse: true}
const system = {
fn: {
execute: jest.fn().mockImplementation(() => Promise.resolve(response))
},
specActions: {
setResponse: jest.fn()
},
errActions: {
newSpecErr: jest.fn()
}
}
// When
let executeFn = executeRequest({
pathName: "/one",
method: "GET"
})
let executePromise = executeFn(system)
// Then
return executePromise.then( () => {
expect(system.specActions.setResponse.calls.length).toEqual(1)
expect(system.specActions.setResponse.calls[0].arguments).toEqual([
"/one",
"GET",
response
])
})
})
describe.skip("requestResolvedSubtree", () => {
it("should return a promise ", function() {
})
})
it.skip("should call errActions.newErr, if the fn.execute rejects", function(){
})
describe("changeParamByIdentity", function () {
it("should map its arguments to a payload", function () {
const pathMethod = ["/one", "get"]
const param = fromJS({
name: "body",
in: "body"
})
const value = "my value"
const isXml = false
const result = changeParamByIdentity(pathMethod, param, value, isXml)
expect(result).toEqual({
type: "spec_update_param",
payload: {
path: pathMethod,
param,
value,
isXml
}
})
})
})
describe("updateEmptyParamInclusion", function () {
it("should map its arguments to a payload", function () {
const pathMethod = ["/one", "get"]
const result = updateEmptyParamInclusion(pathMethod, "param", "query", true)
expect(result).toEqual({
type: "spec_update_empty_param_inclusion",
payload: {
pathMethod,
paramName: "param",
paramIn: "query",
includeEmptyValue: true
}
})
})
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,202 @@
import { fromJS } from "immutable"
import reducer from "corePlugins/spec/reducers"
describe("spec plugin - reducer", function(){
describe("update operation meta value", function() {
it("should update the operation metadata at the specified key", () => {
const updateOperationValue = reducer["spec_update_operation_meta_value"]
const state = fromJS({
resolved: {
"paths": {
"/pet": {
"post": {
"description": "my operation"
}
}
}
}
})
let result = updateOperationValue(state, {
payload: {
path: ["/pet", "post"],
value: "application/json",
key: "consumes_value"
}
})
let expectedResult = {
resolved: {
"paths": {
"/pet": {
"post": {
"description": "my operation"
}
}
}
},
meta: {
paths: {
"/pet": {
post: {
"consumes_value": "application/json"
}
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
it("shouldn't throw an error if we try to update the consumes_value of a null operation", () => {
const updateOperationValue = reducer["spec_update_operation_meta_value"]
const state = fromJS({
resolved: {
"paths": {
"/pet": {
"post": null
}
}
}
})
let result = updateOperationValue(state, {
payload: {
path: ["/pet", "post"],
value: "application/json",
key: "consumes_value"
}
})
expect(result.toJS()).toEqual(state.toJS())
})
})
describe("set response value", function() {
it("should combine the response and error objects", () => {
const setResponse = reducer["spec_set_response"]
const path = "/pet/post"
const method = "POST"
const state = fromJS({})
const result = setResponse(state, {
payload: {
path: path,
method: method,
res: {
error: true,
err: {
message: "Not Found",
name: "Error",
response: {
data: "response data",
headers: {
key: "value"
},
ok: false,
status: 404,
statusText: "Not Found"
},
status: 404,
statusCode: 404
}
}
}
})
let expectedResult = {
error: true,
message: "Not Found",
name: "Error",
data: "response data",
headers: {
key: "value"
},
ok: false,
status: 404,
statusCode: 404,
statusText: "Not Found"
}
const response = result.getIn(["responses", path, method]).toJS()
expect(response).toEqual(expectedResult)
})
})
describe("SPEC_UPDATE_PARAM", function() {
it("should store parameter values by {in}.{name}", () => {
const updateParam = reducer["spec_update_param"]
const path = "/pet/post"
const method = "POST"
const state = fromJS({})
const result = updateParam(state, {
payload: {
path: [path, method],
paramName: "myBody",
paramIn: "body",
value: `{ "a": 123 }`,
isXml: false
}
})
const response = result.getIn(["meta", "paths", path, method, "parameters", "body.myBody", "value"])
expect(response).toEqual(`{ "a": 123 }`)
})
it("should store parameter values by identity", () => {
const updateParam = reducer["spec_update_param"]
const path = "/pet/post"
const method = "POST"
const param = fromJS({
name: "myBody",
in: "body",
schema: {
type: "string"
}
})
const state = fromJS({})
const result = updateParam(state, {
payload: {
param,
path: [path, method],
value: `{ "a": 123 }`,
isXml: false
}
})
const value = result.getIn(["meta", "paths", path, method, "parameters", `body.myBody.hash-${param.hashCode()}`, "value"])
expect(value).toEqual(`{ "a": 123 }`)
})
})
describe("SPEC_UPDATE_EMPTY_PARAM_INCLUSION", function() {
it("should store parameter values by {in}.{name}", () => {
const updateParam = reducer["spec_update_empty_param_inclusion"]
const path = "/pet/post"
const method = "POST"
const state = fromJS({})
const result = updateParam(state, {
payload: {
pathMethod: [path, method],
paramName: "param",
paramIn: "query",
includeEmptyValue: true
}
})
const response = result.getIn(["meta", "paths", path, method, "parameter_inclusions", "query.param"])
expect(response).toEqual(true)
})
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
import { loaded } from "corePlugins/swagger-js/configs-wrap-actions"
describe("swagger-js plugin - withCredentials", () => {
it("should have no effect by default", () => {
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({})
}
const oriExecute = jest.fn()
const loadedFn = loaded(oriExecute, system)
loadedFn()
expect(oriExecute.mock.calls.length).toBe(1)
expect(system.fn.fetch.withCredentials).toBe(undefined)
})
it("should allow setting flag to true via config", () => {
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({
withCredentials: true
})
}
const oriExecute = jest.fn()
const loadedFn = loaded(oriExecute, system)
loadedFn()
expect(oriExecute.mock.calls.length).toBe(1)
expect(system.fn.fetch.withCredentials).toBe(true)
})
it("should allow setting flag to false via config", () => {
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({
withCredentials: false
})
}
const oriExecute = jest.fn()
const loadedFn = loaded(oriExecute, system)
loadedFn()
expect(oriExecute.mock.calls.length).toBe(1)
expect(system.fn.fetch.withCredentials).toBe(false)
})
it("should allow setting flag to true via config as string", () => {
// for query string config
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({
withCredentials: "true"
})
}
const oriExecute = jest.fn()
const loadedFn = loaded(oriExecute, system)
loadedFn()
expect(oriExecute.mock.calls.length).toBe(1)
expect(system.fn.fetch.withCredentials).toBe(true)
})
it("should allow setting flag to false via config as string", () => {
// for query string config
const system = {
fn: {
fetch: jest.fn().mockImplementation(() => Promise.resolve())
},
getConfigs: () => ({
withCredentials: "false"
})
}
const oriExecute = jest.fn()
const loadedFn = loaded(oriExecute, system)
loadedFn()
expect(oriExecute.mock.calls.length).toBe(1)
expect(system.fn.fetch.withCredentials).toBe(false)
})
})

View File

@ -0,0 +1,915 @@
import React, { PureComponent } from "react"
import { fromJS } from "immutable"
import { render, mount } from "enzyme"
import { Provider } from "react-redux"
import System from "core/system"
import ViewPlugin from "core/plugins/view/index.js"
import filterPlugin from "core/plugins/filter/index.js"
describe("bound system", function(){
describe("wrapActions", function(){
it("should replace an action", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
josh: {
actions: {
simple: () => {
return { type: "simple" }
}
},
wrapActions: {
simple: () => () => {
return { type: "newSimple" }
}
}
}
}
}
})
// When
let action = system.getSystem().joshActions.simple(1)
expect(action).toEqual({
type: "newSimple"
})
})
it("should expose the original action, and the system as args", function(){
// Given
const simple = () => ({type: "simple" })
const system = new System({
plugins: {
statePlugins: {
josh: {
actions: { simple },
wrapActions: {
simple: (oriAction, system) => (actionArg) => {
return {
type: "newSimple",
oriActionResult: oriAction(),
system: system.getSystem(),
actionArg
}
}
}
}
}
}
})
// When
let action = system.getSystem().joshActions.simple(1)
expect(action).toEqual({
type: "newSimple",
oriActionResult: { type: "simple" },
system: system.getSystem(),
actionArg: 1
})
})
it("should support multiple wraps of the same action", function(){
const system = new System({
plugins: [
{
statePlugins: {
kyle: {
actions: {
simple: () => {
return {
type: "simple",
}
}
}
}
}
},
{
statePlugins: {
kyle: {
wrapActions: {
simple: (ori) => () => {
return {
...ori(),
firstWrap: true
}
}
}
}
}
},
{
statePlugins: {
kyle: {
wrapActions: {
simple: (ori) => () => {
return {
...ori(),
secondWrap: true
}
}
}
}
}
}
]
})
// When
let action = system.getSystem().kyleActions.simple(1)
expect(action).toEqual({
type: "simple",
firstWrap: true,
secondWrap: true,
})
})
it("should execute wrapActions in the order they appear ( via plugins )", function(){
const system = new System({
plugins: [
{
statePlugins: {
kyle: {
actions: {
simple: () => {
return {
type: "one",
}
}
}
}
}
},
{
statePlugins: {
kyle: {
wrapActions: {
simple: (ori) => () => {
const obj = ori()
obj.type += "-two"
return obj
}
}
}
}
},
{
statePlugins: {
kyle: {
wrapActions: {
simple: (ori) => () => {
const obj = ori()
obj.type += "-three"
return obj
}
}
}
}
}
]
})
// When
let action = system.getSystem().kyleActions.simple(1)
expect(action.type).toEqual("one-two-three")
})
it("should have a the latest system", function(){
// Given
const system = new System({
plugins: [
{
statePlugins: {
kyle: {
actions: {
simple: () => {
return {
type: "one",
}
}
},
wrapActions: {
simple: (ori, {joshActions}) => () => {
return joshActions.hello()
}
}
}
}
},
]
})
// When
const kyleActions = system.getSystem().kyleActions
system.register({
statePlugins: {
josh: {
actions: {
hello(){ return {type: "hello" } }
}
}
}
})
const action = kyleActions.simple()
expect(action).toEqual({ type: "hello"})
})
it.skip("should be able to create async actions", function(){
const system = new System({
plugins: [
{
statePlugins: {
kyle: {
actions: {
simple: () => {
return {
type: "one",
}
}
}
}
}
},
{
statePlugins: {
kyle: {
wrapActions: {
// eslint-disable-next-line no-unused-vars
simple: (ori) => (arg) => (sys) => {
return { type: "called" }
}
}
}
}
},
]
})
// When
let action = system.getSystem().kyleActions.simple(1)
expect(action.type).toEqual("called")
})
})
describe("fn", function() {
it("should return helper functions", function () {
// Given
const system = new System({
plugins: [
filterPlugin
]
})
// When
const fn = system.getSystem().fn.opsFilter
expect(typeof fn).toEqual("function")
})
})
describe("selectors", function(){
it("should have the first arg be the nested state, and all other args to follow", function(){
// Given
const system = new System({
state: {
josh: {
one: 1
}
},
plugins: {
statePlugins: {
josh: {
selectors: {
simple: (state, arg1) => {
return { state, arg1 }
}
}
}
}
}
})
// When
let res = system.getSystem().joshSelectors.simple(1)
expect(res).toEqual({
state: fromJS({
one: 1
}),
arg1: 1
})
})
describe("when selector returns a function", function(){
it("should pass the system to that function", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
josh: {
selectors: {
advanced: () => (mySystem) => {
// Then
expect(mySystem).toEqual(system.getSystem())
return "hi"
}
}
}
}
}
})
// When
let res = system.getSystem().joshSelectors.advanced(1)
expect(res).toEqual("hi")
})
})
describe("wrapSelectors", () => {
it("should wrap a selector and provide a reference to the original", function(){
// Given
const system = new System({
plugins: [
{
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "original"
}
}
}
}
},
{
statePlugins: {
doge: {
wrapSelectors: {
wow: (ori) => (system) => {
// Then
return ori() + " wrapper"
}
}
}
}
}
]
})
// When
let res = system.getSystem().dogeSelectors.wow(1)
expect(res).toEqual("original wrapper")
})
it("should provide a live reference to the system to a wrapper", function(done){
// Given
const mySystem = new System({
plugins: [
{
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "original"
}
}
}
}
},
{
statePlugins: {
doge: {
wrapSelectors: {
wow: (ori, system) => () => {
// Then
expect(mySystem.getSystem()).toEqual(system.getSystem())
done()
return ori() + " wrapper"
}
}
}
}
}
]
})
mySystem.getSystem().dogeSelectors.wow(1)
})
it("should provide the state as the first argument to the inner function", function(done){
// Given
const mySystem = new System({
state: {
doge: {
abc: "123"
}
},
plugins: [
{
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "original"
}
}
}
}
},
{
statePlugins: {
doge: {
wrapSelectors: {
wow: (ori, system) => (dogeState) => {
// Then
expect(dogeState.toJS().abc).toEqual("123")
done()
return ori() + " wrapper"
}
}
}
}
}
]
})
mySystem.getSystem().dogeSelectors.wow(1)
})
})
})
describe("getComponent", function() {
it("returns a component from the system", function() {
const system = new System({
plugins: [
ViewPlugin,
{
components: {
test: ({ name }) => <div>{name} component</div>
}
}
]
})
// When
let Component = system.getSystem().getComponent("test")
const renderedComponent = render(<Component name="Test" />)
expect(renderedComponent.text()).toEqual("Test component")
})
it("allows container components to provide their own `mapStateToProps` function", function() {
// Given
class ContainerComponent extends PureComponent {
mapStateToProps(nextState, props) {
return {
"fromMapState": "This came from mapStateToProps"
}
}
static defaultProps = {
"fromMapState" : ""
}
render() {
const { exampleSelectors, fromMapState, fromOwnProps } = this.props
return (
<div>{ fromMapState } {exampleSelectors.foo()} {fromOwnProps}</div>
)
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
ContainerComponent
}
},
{
statePlugins: {
example: {
selectors: {
foo() { return "and this came from the system" }
}
}
}
}
]
})
// When
let Component = system.getSystem().getComponent("ContainerComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component fromOwnProps="and this came from my own props" />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
})
it("gives the system and own props as props to a container's `mapStateToProps` function", function() {
// Given
class ContainerComponent extends PureComponent {
mapStateToProps(nextState, props) {
const { exampleSelectors, fromMapState, fromOwnProps } = props
return {
"fromMapState": `This came from mapStateToProps ${exampleSelectors.foo()} ${fromOwnProps}`
}
}
static defaultProps = {
"fromMapState" : ""
}
render() {
const { fromMapState } = this.props
return (
<div>{ fromMapState }</div>
)
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
ContainerComponent
}
},
{
statePlugins: {
example: {
selectors: {
foo() { return "and this came from the system" }
}
}
}
}
]
})
// When
let Component = system.getSystem().getComponent("ContainerComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component fromOwnProps="and this came from my own props" />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
})
})
describe("afterLoad", function() {
it("should call a plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
const system = new System({
plugins: [
{
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
]
})
// When
let res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should call a preset plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: [
[MyPlugin]
]
})
// When
let res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should call a function preset plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: [
() => {
return [MyPlugin]
}
]
})
// When
let res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
it("should call a registered plugin's `afterLoad` method after the plugin is loaded", function() {
// Given
const MyPlugin = {
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: []
})
system.register([MyPlugin])
// When
let res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
})
describe("rootInjects", function() {
it("should attach a rootInject function as an instance method", function() {
// This is the same thing as the `afterLoad` tests, but is here for posterity
// Given
const system = new System({
plugins: [
{
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
]
})
// When
let res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
})
describe("error catching", function() {
it("should encapsulate thrown errors in an afterLoad method", function() {
// Given
const ThrowyPlugin = {
afterLoad(system) {
throw new Error("afterLoad BREAKS STUFF!")
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
const system = new System({
plugins: []
})
// When
expect(() => {
system.register([ThrowyPlugin])
// let resSystem = system.getSystem()
}).not.toThrow()
})
it("should encapsulate thrown errors in an action creator", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func() {
throw new Error("this action creator THROWS!")
}
}
}
}
}
})
expect(() => {
// TODO: fix existing action error catcher that creates THROWN ERR actions
system.getSystem().throwActions.func()
}).not.toThrow()
})
it("should encapsulate thrown errors in a reducer", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func: () => {
return {
type: "THROW_FUNC",
payload: "BOOM!"
}
}
},
reducers: {
"THROW_FUNC": (state, action) => {
throw new Error("this reducer EXPLODES!")
}
}
}
}
}
})
expect(() => {
system.getSystem().throwActions.func()
}).not.toThrow()
})
it("should encapsulate thrown errors in a selector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => {
throw new Error("this selector THROWS!")
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).not.toThrow()
})
it("should encapsulate thrown errors in a complex selector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => system => {
throw new Error("this selector THROWS!")
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).not.toThrow()
})
it("should encapsulate thrown errors in a wrapAction", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
actions: {
func: () => {
return {
type: "THROW_FUNC",
payload: "this original action does NOT throw"
}
}
},
wrapActions: {
func: (ori) => (...args) => {
throw new Error("this wrapAction UNRAVELS EVERYTHING!")
}
}
}
}
}
})
expect(system.getSystem().throwActions.func).not.toThrow()
})
it("should encapsulate thrown errors in a wrapSelector", function(){
// Given
const system = new System({
plugins: {
statePlugins: {
throw: {
selectors: {
func: (state, arg1) => {
return 123
}
},
wrapSelectors: {
func: (ori) => (...props) => {
return ori(...props)
}
}
}
}
}
})
expect(system.getSystem().throwSelectors.func).not.toThrow()
})
})
})

View File

@ -0,0 +1,307 @@
import React from "react"
import { render } from "enzyme"
import System from "core/system"
describe("wrapComponents", () => {
describe("should wrap a component and provide a reference to the original", () => {
it("with stateless components", function () {
// Given
const system = new System({
plugins: [
{
components: {
wow: ({ name }) => <div>{name} component</div>
}
},
{
wrapComponents: {
wow: (OriginalComponent) => (props) => {
return <container>
<OriginalComponent {...props}></OriginalComponent>
<OriginalComponent name="Wrapped"></OriginalComponent>
</container>
}
}
}
]
})
// When
let Component = system.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container")
const children = wrapper.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Normal component")
expect(children.eq(1).text()).toEqual("Wrapped component")
})
it("with React classes", function () {
class MyComponent extends React.Component {
render() {
return <div>{this.props.name} component</div>
}
}
// Given
const system = new System({
plugins: [
{
components: {
wow: MyComponent
}
},
{
wrapComponents: {
wow: (OriginalComponent) => {
return class WrapperComponent extends React.Component {
render() {
return <container>
<OriginalComponent {...this.props}></OriginalComponent>
<OriginalComponent name="Wrapped"></OriginalComponent>
</container>
}
}
}
}
}
]
})
// When
let Component = system.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container")
const children = wrapper.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Normal component")
expect(children.eq(1).text()).toEqual("Wrapped component")
})
})
it("should provide a reference to the system to the wrapper", function () {
// Given
const mySystem = new System({
plugins: [
{
// Make a selector
statePlugins: {
doge: {
selectors: {
wow: () => () => {
return "WOW much data"
}
}
}
}
},
{
// Create a component
components: {
wow: () => <div>Original component</div>
}
},
{
// Wrap the component and use the system
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container>
<OriginalComponent {...props}></OriginalComponent>
<div>{system.dogeSelectors.wow()}</div>
</container>
}
}
}
]
})
// Then
let Component = mySystem.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container")
const children = wrapper.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Original component")
expect(children.eq(1).text()).toEqual("WOW much data")
})
it("should wrap correctly when registering more plugins", function () {
// Given
const mySystem = new System({
plugins: [
() => {
return {
statePlugins: {
doge: {
selectors: {
wow: () => () => {
return "WOW much data"
}
}
}
},
components: {
wow: () => <div>Original component</div>
}
}
}
]
})
mySystem.register([
function () {
return {
// Wrap the component and use the system
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container>
<OriginalComponent {...props}></OriginalComponent>
<div>{system.dogeSelectors.wow()}</div>
</container>
}
}
}
}
])
// Then
let Component = mySystem.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container")
const children = wrapper.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Original component")
expect(children.eq(1).text()).toEqual("WOW much data")
})
it("should wrap correctly when registering multiple plugins targeting the same component", function () {
// Given
const mySystem = new System({
pluginsOptions: {
pluginLoadType: "chain"
},
plugins: [
() => {
return {
components: {
wow: () => <div>Original component</div>
}
}
}
]
})
mySystem.register([
() => {
return {
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container1>
<OriginalComponent {...props}></OriginalComponent>
<div>Injected after</div>
</container1>
}
}
}
},
() => {
return {
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container2>
<div>Injected before</div>
<OriginalComponent {...props}></OriginalComponent>
</container2>
}
}
}
}
])
// Then
let Component = mySystem.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container2")
const children2 = wrapper.children()
expect(children2.length).toEqual(2)
expect(children2[0].name).toEqual("div")
expect(children2.eq(0).text()).toEqual("Injected before")
expect(children2[1].name).toEqual("container1")
const children1 = children2.children()
expect(children1.length).toEqual(2)
expect(children1.eq(0).text()).toEqual("Original component")
expect(children1[0].name).toEqual("div")
expect(children1.eq(1).text()).toEqual("Injected after")
})
it("should wrap correctly when building a system twice", function () {
// Given
const pluginOne = {
statePlugins: {
doge: {
selectors: {
wow: () => () => {
return "WOW much data"
}
}
}
},
components: {
wow: () => <div>Original component</div>
}
}
const pluginTwo = {
// Wrap the component and use the system
wrapComponents: {
wow: (OriginalComponent, system) => (props) => {
return <container>
<OriginalComponent {...props}></OriginalComponent>
<div>{system.dogeSelectors.wow()}</div>
</container>
}
}
}
const bothPlugins = () => [pluginOne, pluginTwo]
new System({
plugins: bothPlugins
})
const secondSystem = new System({
plugins: bothPlugins
})
// Then
let Component = secondSystem.getSystem().getComponents("wow")
const wrapper = render(<Component name="Normal" />)
expect(wrapper.get(0).name).toEqual("container")
const children = wrapper.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Original component")
expect(children.eq(1).text()).toEqual("WOW much data")
})
})

View File

@ -0,0 +1,45 @@
import System from "core/system"
describe("wrapSelectors", () => {
it("should wrap correctly when registering multiple plugins targeting the same selector", function() {
const probeBase = {
statePlugins: {
probe: {
selectors: {
selectProbe: () => {
return "base"
}
}
}
}
}
const probeWrap1 = {
statePlugins: {
probe: {
wrapSelectors: {
selectProbe: (oriSelector) => (state, ...args) => {
const selectedValue = oriSelector(state, ...args)
return `${selectedValue}wrap1`
}
}
}
}
}
const probeWrap2 = {
statePlugins: {
probe: {
wrapSelectors: {
selectProbe: (oriSelector) => (state, ...args) => {
const selectedValue = oriSelector(state, ...args)
return `${selectedValue}wrap2`
}
}
}
}
}
const system = new System({ plugins: [probeBase, probeWrap1, probeWrap2] })
expect(system.getSystem().probeSelectors.selectProbe()).toEqual("basewrap1wrap2")
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
const oauthBlockBuilder = require("../../../docker/configurator/oauth")
const dedent = require("dedent")
describe("docker: env translator - oauth block", function() {
it("should omit the block if there are no valid keys", function () {
const input = {}
expect(oauthBlockBuilder(input)).toEqual(``)
})
it("should omit the block if there are no valid keys", function () {
const input = {
NOT_A_VALID_KEY: "asdf1234"
}
expect(oauthBlockBuilder(input)).toEqual(``)
})
it("should generate a block from empty values", function() {
const input = {
OAUTH_CLIENT_ID: ``,
OAUTH_CLIENT_SECRET: ``,
OAUTH_REALM: ``,
OAUTH_APP_NAME: ``,
OAUTH_SCOPE_SEPARATOR: "",
OAUTH_ADDITIONAL_PARAMS: ``,
OAUTH_USE_BASIC_AUTH: false,
OAUTH_USE_PKCE: false
}
expect(oauthBlockBuilder(input)).toEqual(dedent(`
ui.initOAuth({
clientId: "",
clientSecret: "",
realm: "",
appName: "",
scopeSeparator: "",
additionalQueryStringParams: undefined,
useBasicAuthenticationWithAccessCodeGrant: false,
usePkceWithAuthorizationCodeGrant: false,
})`))
})
it("should generate a full block", function() {
const input = {
OAUTH_CLIENT_ID: `myId`,
OAUTH_CLIENT_SECRET: `mySecret`,
OAUTH_REALM: `myRealm`,
OAUTH_APP_NAME: `myAppName`,
OAUTH_SCOPE_SEPARATOR: "%21",
OAUTH_ADDITIONAL_PARAMS: `{ "a": 1234, "b": "stuff" }`,
OAUTH_USE_BASIC_AUTH: true,
OAUTH_USE_PKCE: true
}
expect(oauthBlockBuilder(input)).toEqual(dedent(`
ui.initOAuth({
clientId: "myId",
clientSecret: "mySecret",
realm: "myRealm",
appName: "myAppName",
scopeSeparator: "%21",
additionalQueryStringParams: { "a": 1234, "b": "stuff" },
useBasicAuthenticationWithAccessCodeGrant: true,
usePkceWithAuthorizationCodeGrant: true,
})`))
})
})

View File

@ -0,0 +1,345 @@
const translator = require("../../../docker/configurator/translator")
const dedent = require("dedent")
describe("docker: env translator", function() {
describe("fundamentals", function() {
it("should generate an empty baseline config", function () {
const input = {}
expect(translator(input)).toEqual(``)
})
it("should call an onFound callback", function () {
const input = {
MY_THING: "hey"
}
const onFoundSpy = jest.fn()
const schema = {
MY_THING: {
type: "string",
name: "myThing",
onFound: onFoundSpy
}
}
const res = translator(input, {
schema
})
expect(res).toEqual(`myThing: "hey",`)
expect(onFoundSpy.mock.calls.length).toEqual(1)
})
it("should use a regular value over a legacy one, regardless of order", function () {
const schema = {
MY_THING: {
type: "string",
name: "myThing"
},
MY_OTHER_THING: {
type: "string",
name: "myThing",
legacy: true
}
}
// Regular value provided first
expect(translator({
MY_THING: "hey",
MY_OTHER_THING: "hello"
}, {
schema
})).toEqual(`myThing: "hey",`)
// Legacy value provided first
expect(translator({
MY_OTHER_THING: "hello",
MY_THING: "hey"
}, {
schema
})).toEqual(`myThing: "hey",`)
})
it("should use a legacy value over a base one, regardless of order", function () {
const schema = {
MY_THING: {
type: "string",
name: "myThing",
legacy: true
}
}
const baseConfig = {
myThing: {
value: "base",
schema: {
type: "string",
base: true
}
}
}
// Regular value provided first
expect(translator({
MY_THING: "legacy"
}, {
injectBaseConfig: true,
schema,
baseConfig
})).toEqual(`myThing: "legacy",`)
})
})
describe("Swagger UI configuration", function() {
it("should generate a base config including the base content", function () {
const input = {}
expect(translator(input, {
injectBaseConfig: true
})).toEqual(dedent(`
url: "https://petstore.swagger.io/v2/swagger.json",
"dom_id": "#swagger-ui",
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
queryConfigEnabled: false,
`))
})
it("should ignore an unknown config", function () {
const input = {
ASDF1234: "wow hello"
}
expect(translator(input)).toEqual(dedent(``))
})
it("should generate a string config", function () {
const input = {
URL: "http://petstore.swagger.io/v2/swagger.json",
FILTER: ""
}
expect(translator(input)).toEqual(dedent(`
url: "http://petstore.swagger.io/v2/swagger.json",
filter: "",`
).trim())
})
it("should generate a boolean config", function () {
const input = {
DEEP_LINKING: "true",
SHOW_EXTENSIONS: "false",
SHOW_COMMON_EXTENSIONS: ""
}
expect(translator(input)).toEqual(dedent(`
deepLinking: true,
showExtensions: false,
showCommonExtensions: undefined,`
))
})
it("should generate an object config", function () {
const input = {
SPEC: `{ swagger: "2.0" }`
}
expect(translator(input)).toEqual(dedent(`
spec: { swagger: "2.0" },`
).trim())
})
it("should generate an array config", function () {
const input = {
URLS: `["/one", "/two"]`,
SUPPORTED_SUBMIT_METHODS: ""
}
expect(translator(input)).toEqual(dedent(`
urls: ["/one", "/two"],
supportedSubmitMethods: undefined,`
).trim())
})
it("should properly escape key names when necessary", function () {
const input = {
URLS: `["/one", "/two"]`,
URLS_PRIMARY_NAME: "one",
}
expect(translator(input)).toEqual(dedent(`
urls: ["/one", "/two"],
"urls.primaryName": "one",`
).trim())
})
it("should disregard a legacy variable in favor of a regular one", function () {
const input = {
// Order is important to this test... legacy vars should be
// superseded regardless of what is fed in first.
API_URL: "/old.json",
URL: "/swagger.json",
URLS: `["/one", "/two"]`,
API_URLS: `["/three", "/four"]`,
}
expect(translator(input)).toEqual(dedent(`
url: "/swagger.json",
urls: ["/one", "/two"],`
).trim())
})
it("should pick up legacy variables when using base config", function () {
const input = {
API_URL: "/swagger.json",
API_URLS: `["/one", "/two"]`,
}
expect(translator(input, { injectBaseConfig: true })).toEqual(dedent(`
"dom_id": "#swagger-ui",
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
queryConfigEnabled: false,
url: "/swagger.json",
urls: ["/one", "/two"],`
).trim())
})
it("should generate a full config k:v string", function () {
const input = {
API_URL: "/old.yaml",
API_URLS: `["/old", "/older"]`,
CONFIG_URL: "/wow",
DOM_ID: "#swagger_ui",
SPEC: `{ swagger: "2.0" }`,
URL: "/swagger.json",
URLS: `["/one", "/two"]`,
URLS_PRIMARY_NAME: "one",
LAYOUT: "BaseLayout",
DEEP_LINKING: "false",
DISPLAY_OPERATION_ID: "true",
DEFAULT_MODELS_EXPAND_DEPTH: "0",
DEFAULT_MODEL_EXPAND_DEPTH: "1",
DEFAULT_MODEL_RENDERING: "example",
DISPLAY_REQUEST_DURATION: "true",
DOC_EXPANSION: "full",
FILTER: "wowee",
MAX_DISPLAYED_TAGS: "4",
SHOW_EXTENSIONS: "true",
SHOW_COMMON_EXTENSIONS: "false",
OAUTH2_REDIRECT_URL: "http://google.com/",
SHOW_MUTATED_REQUEST: "true",
SUPPORTED_SUBMIT_METHODS: `["get", "post"]`,
TRY_IT_OUT_ENABLED: "true",
VALIDATOR_URL: "http://smartbear.com/"
}
expect(translator(input)).toEqual(dedent(`
configUrl: "/wow",
"dom_id": "#swagger_ui",
spec: { swagger: "2.0" },
url: "/swagger.json",
urls: ["/one", "/two"],
"urls.primaryName": "one",
layout: "BaseLayout",
deepLinking: false,
displayOperationId: true,
defaultModelsExpandDepth: 0,
defaultModelExpandDepth: 1,
defaultModelRendering: "example",
displayRequestDuration: true,
docExpansion: "full",
filter: "wowee",
maxDisplayedTags: 4,
showExtensions: true,
showCommonExtensions: false,
oauth2RedirectUrl: "http://google.com/",
showMutatedRequest: true,
supportedSubmitMethods: ["get", "post"],
tryItOutEnabled: true,
validatorUrl: "http://smartbear.com/",`
).trim())
})
it("should generate a full config k:v string including base config", function () {
const input = {
API_URL: "/old.yaml",
API_URLS: `["/old", "/older"]`,
CONFIG_URL: "/wow",
DOM_ID: "#swagger_ui",
SPEC: `{ swagger: "2.0" }`,
URL: "/swagger.json",
URLS: `["/one", "/two"]`,
URLS_PRIMARY_NAME: "one",
LAYOUT: "BaseLayout",
DEEP_LINKING: "false",
DISPLAY_OPERATION_ID: "true",
DEFAULT_MODELS_EXPAND_DEPTH: "0",
DEFAULT_MODEL_EXPAND_DEPTH: "1",
DEFAULT_MODEL_RENDERING: "example",
DISPLAY_REQUEST_DURATION: "true",
DOC_EXPANSION: "full",
FILTER: "wowee",
MAX_DISPLAYED_TAGS: "4",
SHOW_EXTENSIONS: "true",
SHOW_COMMON_EXTENSIONS: "false",
OAUTH2_REDIRECT_URL: "http://google.com/",
SHOW_MUTATED_REQUEST: "true",
SUPPORTED_SUBMIT_METHODS: `["get", "post"]`,
TRY_IT_OUT_ENABLED: "false",
VALIDATOR_URL: "http://smartbear.com/"
}
expect(translator(input, { injectBaseConfig: true })).toEqual(dedent(`
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
queryConfigEnabled: false,
configUrl: "/wow",
"dom_id": "#swagger_ui",
spec: { swagger: "2.0" },
url: "/swagger.json",
urls: ["/one", "/two"],
"urls.primaryName": "one",
layout: "BaseLayout",
deepLinking: false,
displayOperationId: true,
defaultModelsExpandDepth: 0,
defaultModelExpandDepth: 1,
defaultModelRendering: "example",
displayRequestDuration: true,
docExpansion: "full",
filter: "wowee",
maxDisplayedTags: 4,
showExtensions: true,
showCommonExtensions: false,
oauth2RedirectUrl: "http://google.com/",
showMutatedRequest: true,
supportedSubmitMethods: ["get", "post"],
tryItOutEnabled: false,
validatorUrl: "http://smartbear.com/",`
).trim())
})
})
})

View File

@ -0,0 +1,4 @@
import { TextDecoder, TextEncoder } from "node:util"
global.TextDecoder = TextDecoder
global.TextEncoder = TextEncoder

View File

@ -0,0 +1,36 @@
import { JSDOM } from "jsdom"
import Enzyme from "enzyme"
import Adapter from "@wojtekmaj/enzyme-adapter-react-17"
import win from "../../src/core/window"
Enzyme.configure({ adapter: new Adapter() })
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === "undefined")
.reduce((result, prop) => ({
...result,
[prop]: Object.getOwnPropertyDescriptor(src, prop),
}), {})
Object.defineProperties(target, props)
}
function setUpDomEnvironment() {
const jsdom = new JSDOM("<!doctype html><html><body></body></html>", {
url: "http://localhost/",
})
const { window } = jsdom
global.window = window
global.document = window.document
global.navigator = {
userAgent: "node.js",
}
copyProps(win, window) // use UI's built-in window wrapper
copyProps(window, global)
}
setUpDomEnvironment()
// configure({ adapter: new Adapter() }) // enzyme@3

View File

@ -0,0 +1,12 @@
import path from "path"
import getAbsoluteFSPath from "../../../swagger-ui-dist-package/absolute-path"
describe("swagger-ui-dist", function(){
describe("getAbsoluteFSPath", function(){
it("returns absolute path", function(){
const expectedPath = path.resolve(__dirname, "../../../swagger-ui-dist-package")
expect(getAbsoluteFSPath()).toEqual(expectedPath)
})
})
})

View File

@ -0,0 +1,106 @@
import React from "react"
import { render } from "enzyme"
import { fromJS } from "immutable"
import Info, { InfoUrl } from "components/info"
import { Link } from "components/layout-utils"
import Markdown from "components/providers/markdown"
describe("<Info/> Anchor Target Safety", function(){
const dummyComponent = () => null
const components = {
Markdown,
InfoUrl,
Link
}
const baseProps = {
getComponent: c => components[c] || dummyComponent,
host: "example.test",
basePath: "/api",
info: fromJS({
title: "Hello World"
})
}
it("renders externalDocs links with safe `rel` attributes", function () {
const props = {
...baseProps,
externalDocs: fromJS({
url: "http://google.com/"
})
}
let wrapper = render(<Info {...props} />)
const anchor = wrapper.find("a")
expect(anchor.html()).toEqual("http://google.com/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("renders Contact links with safe `rel` attributes", function () {
const props = {
...baseProps,
info: fromJS({
contact: {
url: "http://google.com/",
name: "My Site"
}
})
}
let wrapper = render(<Info {...props} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://google.com/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("renders License links with safe `rel` attributes", function () {
const props = {
...baseProps,
info: fromJS({
license: {
url: "http://mit.edu/"
}
})
}
let wrapper = render(<Info {...props} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://mit.edu/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("renders termsOfService links with safe `rel` attributes", function () {
const props = {
...baseProps,
info: fromJS({
termsOfService: "http://smartbear.com/"
})
}
let wrapper = render(<Info {...props} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://smartbear.com/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("renders definition URL links with safe `rel` attributes", function () {
const props = {
...baseProps,
url: "http://petstore.swagger.io/v2/petstore.json"
}
let wrapper = render(<Info {...props} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://petstore.swagger.io/v2/petstore.json")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
})

View File

@ -0,0 +1,39 @@
import React from "react"
import { render } from "enzyme"
import { Link } from "components/layout-utils"
describe("<Link/> Anchor Target Safety", function () {
const dummyComponent = () => null
const components = {
Link
}
const baseProps = {
getComponent: c => components[c] || dummyComponent
}
it("renders regular links with `noreferrer` and `noopener`", function () {
const props = {
...baseProps,
href: "http://google.com/"
}
let wrapper = render(<Link {...props} />)
expect(wrapper.attr("href")).toEqual("http://google.com/")
expect(wrapper.attr("rel") || "").toMatch("noopener")
expect(wrapper.attr("rel") || "").toMatch("noreferrer")
})
it("enforces `noreferrer` and `noopener` on target=_blank links", function () {
const props = {
...baseProps,
href: "http://google.com/",
target: "_blank"
}
let wrapper = render(<Link {...props} />)
expect(wrapper.attr("href")).toEqual("http://google.com/")
expect(wrapper.attr("target")).toEqual("_blank")
expect(wrapper.attr("rel") || "").toMatch("noopener")
expect(wrapper.attr("rel") || "").toMatch("noreferrer")
})
})

View File

@ -0,0 +1,64 @@
import React from "react"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.jsx"
describe("Markdown Link Anchor Safety", function () {
describe("Swagger 2.0", function () {
it("sanitizes Markdown links", function () {
const str = `Hello, [here](http://google.com/) is my link`
const wrapper = render(<Markdown source={str} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://google.com/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("sanitizes raw HTML links", function () {
const str = `Hello, <a href="http://google.com/">here</a> is my link`
const wrapper = render(<Markdown source={str} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://google.com/")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
})
describe("OAS 3", function () {
it("sanitizes Markdown links", function () {
const str = `Hello, [here](http://google.com/) is my link`
const wrapper = render(<OAS3Markdown source={str} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://google.com/")
expect(anchor.attr("target")).toEqual("_blank")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
it("sanitizes raw HTML links", function () {
const str = `Hello, <a href="http://google.com/">here</a> is my link`
const wrapper = render(<OAS3Markdown source={str} />)
const anchor = wrapper.find("a")
expect(anchor.attr("href")).toEqual("http://google.com/")
expect(anchor.attr("rel") || "").toMatch("noopener")
expect(anchor.attr("rel") || "").toMatch("noreferrer")
})
})
})
function withMarkdownWrapper(str, { isOAS3 = false } = {}) {
if(isOAS3) {
return `<div class="renderedMarkdown"><p>${str}</p></div>`
}
return `<div class="markdown"><p>${str}</p>\n</div>`
}

View File

@ -0,0 +1,29 @@
import React from "react"
import { mount } from "enzyme"
import OnlineValidatorBadge from "components/online-validator-badge"
describe("<OnlineValidatorBadge/> Anchor Target Safety", function () {
it("should render a validator link with safe `rel` attributes", function () {
// When
const props = {
getConfigs: () => ({}),
getComponent: () => null,
specSelectors: {
url: () => "swagger.json"
}
}
const wrapper = mount(
<OnlineValidatorBadge {...props} />
)
const anchor = wrapper.find("a")
// Then
expect(anchor.props().href).toEqual(
"https://validator.swagger.io/validator/debug?url=swagger.json"
)
expect(anchor.props().target).toEqual("_blank")
expect(anchor.props().rel || "").toInclude("noopener")
expect(anchor.props().rel || "").toInclude("noreferrer")
})
})

View File

@ -0,0 +1,32 @@
import React from "react"
import { render } from "enzyme"
import { fromJS } from "immutable"
import Info from "components/info"
import Markdown from "components/providers/markdown"
describe("<Info/> Sanitization", function(){
const dummyComponent = () => null
const components = {
Markdown
}
const props = {
getComponent: c => components[c] || dummyComponent,
info: fromJS({
title: "Test Title **strong** <script>alert(1)</script>",
description: "Description *with* <script>Markdown</script>"
}),
host: "example.test",
basePath: "/api",
selectedServer: "https://example.test",
}
it("renders sanitized .title content", function(){
let wrapper = render(<Info {...props}/>)
expect(wrapper.find(".title").html()).toEqual("Test Title **strong** &lt;script&gt;alert(1)&lt;/script&gt;")
})
it("renders sanitized .description content", function() {
let wrapper = render(<Info {...props}/>)
expect(wrapper.find(".description").html()).toEqual("<div class=\"markdown\"><p>Description <em>with</em> </p>\n</div>")
})
})

View File

@ -0,0 +1,46 @@
import React from "react"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.jsx"
describe("Markdown Script Sanitization", function() {
describe("Swagger 2.0", function() {
it("sanitizes <script> elements", function() {
const str = `script <script>alert(1)</script>`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p>script </p>\n</div>`)
})
it("sanitizes <img> elements", function() {
const str = `<img src=x onerror="alert('img-in-description')">`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p><img src="x"></p>\n</div>`)
})
it("sanitizes <form> elements", function() {
const str = `"<form action='https://do.not.use.url/fake' method='post' action='java'><input type='email' id='email' placeholder='Email-address' name='email' value=''><button type='submit'>Login</button>"`
const el = render(<Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="markdown"><p>"</p><input value="" name="email" placeholder="Email-address" id="email" type="email"><button type="submit">Login</button>"<p></p>\n</div>`)
})
})
describe("OAS 3", function() {
it("sanitizes <script> elements", function() {
const str = `script <script>alert(1)</script>`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p>script </p></div>`)
})
it("sanitizes <img> elements", function() {
const str = `<img src=x onerror="alert('img-in-description')">`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p><img src="x"></p></div>`)
})
it("sanitizes <form> elements", function () {
const str = `"<form action='https://do.not.use.url/fake' method='post' action='java'><input type='email' id='email' placeholder='Email-address' name='email' value=''><button type='submit'>Login</button>"`
const el = render(<OAS3Markdown source={str} />)
expect(el.prop("outerHTML")).toEqual(`<div class="renderedMarkdown"><p>"</p><input value="" name="email" placeholder="Email-address" id="email" type="email"><button type="submit">Login</button>"<p></p></div>`)
})
})
})