fix
This commit is contained in:
@@ -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$'")
|
||||
})
|
||||
})
|
||||
@@ -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`)
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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"]
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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"
|
||||
}])
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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"
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
])
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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({})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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")
|
||||
})
|
||||
})
|
||||
@@ -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
Reference in New Issue
Block a user