feat: migrate unit tests to Jest (#6353)

* config(jest): updated setup
* config(jest): update testMatch to include jsx files
* config(jest): add transformIgnorePatterns
* config(jest): update ignore files that do not work in jest yet
* config: add test:unit-jest to test script

* fix(jest): lint with eslint-plugin-jest

* refactor(jest): move unit test directory
* refactor(mocha): restore mocha tests that fail in jest

* docs(jest): update helpful scripts with test:unit-jest
This commit is contained in:
Tim Lai
2020-09-01 10:41:01 -07:00
committed by GitHub
parent 0c60696d26
commit 1a27c0a8bd
70 changed files with 2131 additions and 1704 deletions

354
test/unit/core/curlify.js Normal file
View File

@@ -0,0 +1,354 @@
import Im from "immutable"
import curl from "core/curlify"
import win from "core/window"
describe("curlify", function () {
it("prints a curl statement with custom content-type", function () {
let req = {
url: "http://example.com",
method: "POST",
body: {
id: 0,
name: "doggie",
status: "available"
},
headers: {
Accept: "application/json",
"content-type": "application/json"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X POST \"http://example.com\" -H \"Accept: application/json\" -H \"content-type: application/json\" -d {\"id\":0,\"name\":\"doggie\",\"status\":\"available\"}")
})
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 \"http://example.com\" -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 \"http://example.com\" -H \"conTenT Type: application/Moar\" -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 \"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 \"http://swaggerhub.com/v1/one?name=john|smith\" -H \"authorization: Basic Zm9vOmJhcg==\"")
})
it("prints a curl statement with html", function () {
let req = {
url: "http://swaggerhub.com/v1/one?name=john|smith",
method: "GET",
headers: {
accept: "application/json"
},
body: {
description: "<b>Test</b>"
}
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X GET \"http://swaggerhub.com/v1/one?name=john|smith\" -H \"accept: application/json\" -d {\"description\":\"<b>Test</b>\"}")
})
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 \"http://swaggerhub.com/v1/one?name=john|smith\" -d {\"description\":\"@prefix nif:<http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> .@prefix itsrdf: <http://www.w3.org/2005/11/its/rdf#> .\"}")
})
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 \"http://example.com\" -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 \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -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 \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -F \"fruits[]=apple\" -F \"fruits[]=banana\" -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 \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -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 \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -F \"file=@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 \"http://example.com\" -H \"accept: application/json\" -d {\"id\":10101}")
})
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 \"http://example.com\" -H \"accept: application/json\" -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 \"http://example.com\" -H \"x-custom-name: multipart/form-data\" -H \"content-type: multipart/form-data\" -F \"id=123\" -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 \"http://example.com\" -H \"content-type: multipart/form-data\" -H \"x-custom-name: any-value\" -F \"id=123\" -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("shoud 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 \"http://example.com\" -H \"x-custom-name: multipart/form-data\" -d {\"id\":\"123\",\"file\":{\"name\":\"file.txt\",\"type\":\"text/plain\"}}")
})
it("shoud 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 \"http://example.com\" -H \"x-custom-name: multipart/form-data\" -d {\"id\":\"123\",\"file\":{\"name\":\"file.txt\"}}")
})
})
it("should escape dollar signs in headers and request body", function () {
let req = {
url: "http://example.com",
method: "POST",
headers: { "X-DOLLAR": "token/123$" },
body: "CREATE ($props)"
}
let curlified = curl(Im.fromJS(req))
expect(curlified).toEqual("curl -X POST \"http://example.com\" -H \"X-DOLLAR: token/123\\$\" -d \"CREATE (\\$props)\"")
})
})

View File

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

View File

@@ -0,0 +1,77 @@
import win from "core/window"
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] } },
authActions: {},
errActions: {},
configs: { oauth2RedirectUrl: "" },
authConfigs: {}
}
beforeEach(() => {
win.open = jest.fn()
})
describe("authorize redirect", () => {
it("should build authorize url", () => {
const windowOpenSpy = jest.spyOn(win, "open")
oauth2Authorize(authConfig)
expect(windowOpenSpy.mock.calls.length).toEqual(1)
expect(windowOpenSpy.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?response_type=code&redirect_uri=&state=")
windowOpenSpy.mockReset()
})
it("should append query parameters to authorizeUrl with query parameters", () => {
const windowOpenSpy = jest.spyOn(win, "open")
mockSchema.authorizationUrl = "https://testAuthorizationUrl?param=1"
oauth2Authorize(authConfig)
expect(windowOpenSpy.mock.calls.length).toEqual(1)
expect(windowOpenSpy.mock.calls[0][0]).toMatch("https://testAuthorizationUrl?param=1&response_type=code&redirect_uri=&state=")
windowOpenSpy.mockReset()
})
it("should send code_challenge when using authorizationCode flow with usePkceWithAuthorizationCodeGrant enabled", () => {
const windowOpenSpy = jest.spyOn(win, "open")
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(win.open.mock.calls.length).toEqual(1)
const actualUrl = new URLSearchParams(win.open.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
windowOpenSpy.mockReset()
generateCodeVerifierSpy.mockReset()
createCodeChallengeSpy.mockReset()
})
})
})

View File

@@ -0,0 +1,187 @@
import { authorizeRequest, authorizeAccessCodeWithFormParams } from "corePlugins/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: () => ({})
},
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"
}
})
},
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())
},
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 data = {
auth: {
schema: {
get: () => "http://tokenUrl"
},
codeVerifier: "mock_code_verifier"
},
redirectUrl: "http://google.com"
}
const authActions = {
authorizeRequest: jest.fn()
}
authorizeAccessCodeWithFormParams(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")
})
})
})

View File

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

View File

@@ -0,0 +1,132 @@
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([])
})
})
})

View File

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

View File

@@ -0,0 +1,26 @@
import { downloadConfig } from "corePlugins/configs/spec-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)
})
})
})

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,507 @@
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)
})
})
})
})

View File

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

View File

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

View File

@@ -0,0 +1,115 @@
import { fromJS } 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,
specSelectors: {
specJson: () => fromJS({
openapi: "3.0.0"
}),
securityDefinitions: () => {
return fromJS({
"oauth2AuthorizationCode": {
"type": "oauth2",
"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"
}
}
}
}
})
}
}
}
// 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"
}
},
{
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"
}
},
])
})
})
})

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,247 @@
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" />)
const container = wrapper.children().first()
expect(container[0].name).toEqual("container")
const children = container.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" />)
const container = wrapper.children().first()
expect(container[0].name).toEqual("container")
const children = container.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" />)
const container = wrapper.children().first()
expect(container[0].name).toEqual("container")
const children = container.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" />)
const container = wrapper.children().first()
expect(container[0].name).toEqual("container")
const children = container.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 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" />)
const container = wrapper.children().first()
expect(container[0].name).toEqual("container")
const children = container.children()
expect(children.length).toEqual(2)
expect(children.eq(0).text()).toEqual("Original component")
expect(children.eq(1).text()).toEqual("WOW much data")
})
})

1671
test/unit/core/utils.js Normal file

File diff suppressed because it is too large Load Diff