Added Swagger
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
env:
|
||||
mocha: true
|
||||
@@ -0,0 +1,9 @@
|
||||
globals:
|
||||
cy: false
|
||||
Cypress: false
|
||||
expect: false
|
||||
assert: false
|
||||
|
||||
rules:
|
||||
"no-console": 0
|
||||
"no-unused-vars": 0
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
license:
|
||||
name: MIT
|
||||
servers:
|
||||
- url: http://petstore.swagger.io/v1
|
||||
paths:
|
||||
/pets:
|
||||
get:
|
||||
summary: List all pets
|
||||
operationId: listPets
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: A paged array of pets
|
||||
headers:
|
||||
x-next:
|
||||
description: A link to the next page of responses
|
||||
schema:
|
||||
type: string
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pets"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
post:
|
||||
summary: Create a pet
|
||||
operationId: createPets
|
||||
tags:
|
||||
- pets
|
||||
responses:
|
||||
'201':
|
||||
description: Null response
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/pets/{petId}:
|
||||
get:
|
||||
summary: Info for a specific pet
|
||||
operationId: showPetById
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the pet to retrieve
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Expected response to a valid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pets"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
components:
|
||||
schemas:
|
||||
Pet:
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
Pets:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
Error:
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
message:
|
||||
type: string
|
||||
@@ -0,0 +1,707 @@
|
||||
# As found on https://petstore.swagger.io, August 2018
|
||||
---
|
||||
swagger: '2.0'
|
||||
info:
|
||||
description: 'This is a sample server Petstore server. You can find out more about
|
||||
Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For
|
||||
this sample, you can use the api key `special-key` to test the authorization filters.'
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
termsOfService: http://swagger.io/terms/
|
||||
contact:
|
||||
email: apiteam@swagger.io
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
host: petstore.swagger.io
|
||||
basePath: "/v2"
|
||||
tags:
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
externalDocs:
|
||||
description: Find out more
|
||||
url: http://swagger.io
|
||||
- name: store
|
||||
description: Access to Petstore orders
|
||||
- name: user
|
||||
description: Operations about user
|
||||
externalDocs:
|
||||
description: Find out more about our store
|
||||
url: http://swagger.io
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
paths:
|
||||
"/pet":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Add a new pet to the store
|
||||
description: ''
|
||||
operationId: addPet
|
||||
consumes:
|
||||
- application/json
|
||||
- application/xml
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: Pet object that needs to be added to the store
|
||||
required: true
|
||||
schema:
|
||||
"$ref": "#/definitions/Pet"
|
||||
responses:
|
||||
'405':
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
put:
|
||||
tags:
|
||||
- pet
|
||||
summary: Update an existing pet
|
||||
description: ''
|
||||
operationId: updatePet
|
||||
consumes:
|
||||
- application/json
|
||||
- application/xml
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: Pet object that needs to be added to the store
|
||||
required: true
|
||||
schema:
|
||||
"$ref": "#/definitions/Pet"
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
'405':
|
||||
description: Validation exception
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/findByStatus":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by status
|
||||
description: Multiple status values can be provided with comma separated strings
|
||||
operationId: findPetsByStatus
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
description: Status values that need to be considered for filter
|
||||
required: true
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
default: available
|
||||
collectionFormat: multi
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/definitions/Pet"
|
||||
'400':
|
||||
description: Invalid status value
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/findByTags":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by tags
|
||||
description: Muliple tags can be provided with comma separated strings. Use
|
||||
tag1, tag2, tag3 for testing.
|
||||
operationId: findPetsByTags
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: tags
|
||||
in: query
|
||||
description: Tags to filter by
|
||||
required: true
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
collectionFormat: multi
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/definitions/Pet"
|
||||
'400':
|
||||
description: Invalid tag value
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
deprecated: true
|
||||
"/pet/{petId}":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Find pet by ID
|
||||
description: Returns a single pet
|
||||
operationId: getPetById
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to return
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/Pet"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
security:
|
||||
- api_key: []
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Updates a pet in the store with form data
|
||||
description: ''
|
||||
operationId: updatePetWithForm
|
||||
consumes:
|
||||
- application/x-www-form-urlencoded
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet that needs to be updated
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
- name: name
|
||||
in: formData
|
||||
description: Updated name of the pet
|
||||
required: false
|
||||
type: string
|
||||
- name: status
|
||||
in: formData
|
||||
description: Updated status of the pet
|
||||
required: false
|
||||
type: string
|
||||
responses:
|
||||
'405':
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: Deletes a pet
|
||||
description: ''
|
||||
operationId: deletePet
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: api_key
|
||||
in: header
|
||||
required: false
|
||||
type: string
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/{petId}/uploadImage":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: uploads an image
|
||||
description: ''
|
||||
operationId: uploadFile
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
produces:
|
||||
- application/json
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to update
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
- name: additionalMetadata
|
||||
in: formData
|
||||
description: Additional data to pass to server
|
||||
required: false
|
||||
type: string
|
||||
- name: file
|
||||
in: formData
|
||||
description: file to upload
|
||||
required: false
|
||||
type: file
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/ApiResponse"
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/store/inventory":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Returns pet inventories by status
|
||||
description: Returns a map of status codes to quantities
|
||||
operationId: getInventory
|
||||
produces:
|
||||
- application/json
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
security:
|
||||
- api_key: []
|
||||
"/store/order":
|
||||
post:
|
||||
tags:
|
||||
- store
|
||||
summary: Place an order for a pet
|
||||
description: ''
|
||||
operationId: placeOrder
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: order placed for purchasing the pet
|
||||
required: true
|
||||
schema:
|
||||
"$ref": "#/definitions/Order"
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/Order"
|
||||
'400':
|
||||
description: Invalid Order
|
||||
"/store/order/{orderId}":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Find purchase order by ID
|
||||
description: For valid response try integer IDs with value >= 1 and <= 10. Other
|
||||
values will generated exceptions
|
||||
operationId: getOrderById
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of pet that needs to be fetched
|
||||
required: true
|
||||
type: integer
|
||||
maximum: 10
|
||||
minimum: 1
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/Order"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
delete:
|
||||
tags:
|
||||
- store
|
||||
summary: Delete purchase order by ID
|
||||
description: For valid response try integer IDs with positive integer value.
|
||||
Negative or non-integer values will generate API errors
|
||||
operationId: deleteOrder
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of the order that needs to be deleted
|
||||
required: true
|
||||
type: integer
|
||||
minimum: 1
|
||||
format: int64
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
"/user":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Create user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: createUser
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: Created user object
|
||||
required: true
|
||||
schema:
|
||||
"$ref": "#/definitions/User"
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/createWithArray":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array
|
||||
description: ''
|
||||
operationId: createUsersWithArrayInput
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: List of user object
|
||||
required: true
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/definitions/User"
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/createWithList":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array
|
||||
description: ''
|
||||
operationId: createUsersWithListInput
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
description: List of user object
|
||||
required: true
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/definitions/User"
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/login":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs user into the system
|
||||
description: ''
|
||||
operationId: loginUser
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: username
|
||||
in: query
|
||||
description: The user name for login
|
||||
required: true
|
||||
type: string
|
||||
- name: password
|
||||
in: query
|
||||
description: The password for login in clear text
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
type: string
|
||||
headers:
|
||||
X-Rate-Limit:
|
||||
type: integer
|
||||
format: int32
|
||||
description: calls per hour allowed by the user
|
||||
X-Expires-After:
|
||||
type: string
|
||||
format: date-time
|
||||
description: date in UTC when token expires
|
||||
'400':
|
||||
description: Invalid username/password supplied
|
||||
"/user/logout":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs out current logged in user session
|
||||
description: ''
|
||||
operationId: logoutUser
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters: []
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/{username}":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get user by user name
|
||||
description: ''
|
||||
operationId: getUserByName
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: 'The name that needs to be fetched. Use user1 for testing. '
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/User"
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
put:
|
||||
tags:
|
||||
- user
|
||||
summary: Updated user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: updateUser
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: name that need to be updated
|
||||
required: true
|
||||
type: string
|
||||
- in: body
|
||||
name: body
|
||||
description: Updated user object
|
||||
required: true
|
||||
schema:
|
||||
"$ref": "#/definitions/User"
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid user supplied
|
||||
'404':
|
||||
description: User not found
|
||||
delete:
|
||||
tags:
|
||||
- user
|
||||
summary: Delete user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: deleteUser
|
||||
produces:
|
||||
- application/xml
|
||||
- application/json
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: The name that needs to be deleted
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
securityDefinitions:
|
||||
petstore_auth:
|
||||
type: oauth2
|
||||
authorizationUrl: https://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
|
||||
definitions:
|
||||
Order:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
petId:
|
||||
type: integer
|
||||
format: int64
|
||||
quantity:
|
||||
type: integer
|
||||
format: int32
|
||||
shipDate:
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: string
|
||||
description: Order Status
|
||||
enum:
|
||||
- placed
|
||||
- approved
|
||||
- delivered
|
||||
complete:
|
||||
type: boolean
|
||||
default: false
|
||||
xml:
|
||||
name: Order
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
username:
|
||||
type: string
|
||||
firstName:
|
||||
type: string
|
||||
lastName:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
phone:
|
||||
type: string
|
||||
userStatus:
|
||||
type: integer
|
||||
format: int32
|
||||
description: User Status
|
||||
xml:
|
||||
name: User
|
||||
Category:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: Category
|
||||
Tag:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: Tag
|
||||
Pet:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- photoUrls
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
category:
|
||||
"$ref": "#/definitions/Category"
|
||||
name:
|
||||
type: string
|
||||
example: doggie
|
||||
photoUrls:
|
||||
type: array
|
||||
xml:
|
||||
name: photoUrl
|
||||
wrapped: true
|
||||
items:
|
||||
type: string
|
||||
tags:
|
||||
type: array
|
||||
xml:
|
||||
name: tag
|
||||
wrapped: true
|
||||
items:
|
||||
"$ref": "#/definitions/Tag"
|
||||
status:
|
||||
type: string
|
||||
description: pet status in the store
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
xml:
|
||||
name: Pet
|
||||
ApiResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
type:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: http://swagger.io
|
||||
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- HTML for hot dev server -->
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger Editor</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-size: 9px;
|
||||
line-height: 1.42857143;
|
||||
color: #444;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#swagger-editor {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
max-width: 880px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#editor-wrapper {
|
||||
height: 100%;
|
||||
border: 1em solid #000;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.Pane2 {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<link href="./swagger-editor.css" rel="stylesheet">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-editor"></div>
|
||||
|
||||
<script>
|
||||
// https://github.com/cypress-io/cypress/issues/95
|
||||
delete window.fetch
|
||||
</script>
|
||||
<script src="/commons.js"> </script>
|
||||
<script src="/swagger-editor-bundle.js"> </script>
|
||||
<script src="/swagger-editor-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
localStorage.setItem("swagger-editor-content", " ") // always start empty
|
||||
|
||||
// Webpack outputs library variables with file-names-like-this
|
||||
window["SwaggerEditorBundle"] = window["SwaggerEditorBundle"] || window["swagger-editor-bundle"]
|
||||
window["SwaggerEditorStandalonePreset"] = window["SwaggerEditorStandalonePreset"] || window["swagger-editor-standalone-preset"]
|
||||
// Build a system
|
||||
const editor = SwaggerEditorBundle({
|
||||
dom_id: '#swagger-editor',
|
||||
layout: 'StandaloneLayout',
|
||||
presets: [
|
||||
SwaggerEditorStandalonePreset
|
||||
]
|
||||
})
|
||||
|
||||
window.editor = editor
|
||||
}
|
||||
</script>
|
||||
x
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
|
||||
<defs>
|
||||
<symbol viewBox="0 0 20 20" id="unlocked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 20 20" id="locked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 20 20" id="close">
|
||||
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow">
|
||||
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow-down">
|
||||
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
|
||||
<symbol viewBox="0 0 24 24" id="jump-to">
|
||||
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 24 24" id="expand">
|
||||
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" />
|
||||
</symbol>
|
||||
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import "./commands"
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
@@ -0,0 +1,101 @@
|
||||
describe("Editor #1862: codegen download links downgrade HTTPS", () => {
|
||||
describe("in Swagger 2", () => {
|
||||
beforeEach(() => {
|
||||
cy.visit("/?url=/documents/petstore.swagger.yaml")
|
||||
|
||||
cy.server()
|
||||
|
||||
cy.route({
|
||||
url: "*//generator.swagger.io/api/gen/servers",
|
||||
response: ["nodejs"]
|
||||
})
|
||||
|
||||
cy.route({
|
||||
url: "*//generator.swagger.io/api/gen/clients",
|
||||
response: ["javascript"]
|
||||
})
|
||||
})
|
||||
|
||||
it("should force HTTPS server downloads from Swagger.io Generator", () => {
|
||||
let wasHttpHit = false
|
||||
let wasHttpsHit = false
|
||||
|
||||
// Given
|
||||
|
||||
cy.route({
|
||||
url: "https://generator.swagger.io/api/gen/servers/nodejs",
|
||||
method: "POST",
|
||||
response: {
|
||||
"code": "a92bc815-f6e3-4a56-839b-fd2e6f379d52",
|
||||
"link": "http://generator.swagger.io:80/api/gen/download/a92bc815-f6e3-4a56-839b-fd2e6f379d52"
|
||||
}
|
||||
}).as("httpsServerNodejs")
|
||||
|
||||
cy.route({
|
||||
url: "http://generator.swagger.io/api/gen/download/*",
|
||||
onRequest: () => wasHttpHit = true,
|
||||
response: {}
|
||||
}).as("httpServerGenDownload")
|
||||
|
||||
cy.route({
|
||||
url: "https://generator.swagger.io/api/gen/download/*",
|
||||
onRequest: () => wasHttpsHit = true,
|
||||
response: {}
|
||||
}).as("httpsServerGenDownload")
|
||||
|
||||
// Then
|
||||
cy.contains("Generate Server")
|
||||
.click()
|
||||
|
||||
cy.contains("nodejs")
|
||||
.click()
|
||||
|
||||
cy.wait(["@httpsServerNodejs", "@httpsServerGenDownload"])
|
||||
.then(() => {
|
||||
expect(wasHttpHit).to.equal(false, "has HTTP server been hit")
|
||||
expect(wasHttpsHit).to.equal(true, "has HTTPS server been hit")
|
||||
})
|
||||
})
|
||||
|
||||
it("should force HTTPS client downloads from Swagger.io Generator", () => {
|
||||
let wasHttpHit = false
|
||||
let wasHttpsHit = false
|
||||
|
||||
// Given
|
||||
|
||||
cy.route({
|
||||
url: "https://generator.swagger.io/api/gen/clients/javascript",
|
||||
method: "POST",
|
||||
response: {
|
||||
"code": "a92bc815-f6e3-4a56-839b-fd2e6f379d52",
|
||||
"link": "http://generator.swagger.io:80/api/gen/download/a92bc815-f6e3-4a56-839b-fd2e6f379d52"
|
||||
}
|
||||
}).as("httpsClientJavascript")
|
||||
|
||||
cy.route({
|
||||
url: "http://generator.swagger.io/api/gen/download/*",
|
||||
onRequest: () => wasHttpHit = true,
|
||||
response: {}
|
||||
}).as("httpClientGenDownload")
|
||||
|
||||
cy.route({
|
||||
url: "https://generator.swagger.io/api/gen/download/*",
|
||||
onRequest: () => wasHttpsHit = true,
|
||||
response: {}
|
||||
}).as("httpsClientGenDownload")
|
||||
|
||||
// Then
|
||||
cy.contains("Generate Client")
|
||||
.click()
|
||||
|
||||
cy.contains("javascript")
|
||||
.click()
|
||||
|
||||
cy.wait(["@httpsClientJavascript", "@httpsClientGenDownload"])
|
||||
.then(() => {
|
||||
expect(wasHttpHit).to.equal(false, "has HTTP server been hit")
|
||||
expect(wasHttpsHit).to.equal(true, "has HTTPS server been hit")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,128 @@
|
||||
import expect from "expect"
|
||||
|
||||
describe("EditorLayout", function() {
|
||||
let EditorLayout
|
||||
|
||||
// If alert isn't defined, create a dummy one, and remember to clean it up afterwards
|
||||
if (typeof global.alert === "undefined") {
|
||||
before(function () {
|
||||
global.alert = function() { }
|
||||
})
|
||||
after(function () {
|
||||
delete global.alert
|
||||
})
|
||||
}
|
||||
|
||||
// Same for FileReader
|
||||
if (typeof global.FileReader === "undefined") {
|
||||
before(function () {
|
||||
global.FileReader = function() {}
|
||||
})
|
||||
after(function () {
|
||||
delete global.FileReader
|
||||
})
|
||||
}
|
||||
|
||||
// Create spies for alert and FileReader, and then load the module under test.
|
||||
before(function () {
|
||||
expect.spyOn(global, "alert")
|
||||
expect.spyOn(global, "FileReader")
|
||||
EditorLayout = require("src/layout").default
|
||||
})
|
||||
// Undo the spies afterwards
|
||||
after(function () {
|
||||
expect.restoreSpies()
|
||||
})
|
||||
|
||||
// Reset spies after each test
|
||||
afterEach(function () {
|
||||
global.alert.reset()
|
||||
global.FileReader.reset()
|
||||
})
|
||||
|
||||
describe("when file(s) are dropped", function() {
|
||||
describe("if one or more files are of an unexpected type", function() {
|
||||
it("should alert the user that their file(s) were rejected", () => {
|
||||
const editorLayout = new EditorLayout()
|
||||
|
||||
editorLayout.onDrop([], ["rejected.file.1"])
|
||||
editorLayout.onDrop([], ["rejected.file.1", "rejected.file.2"])
|
||||
|
||||
expect(global.alert.calls.length).toEqual(2)
|
||||
|
||||
global.alert.calls.forEach(call => {
|
||||
expect(call.arguments[0]).toMatch(/^Sorry.*/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("if more than one file of an expected type is dropped", function() {
|
||||
it("should alert the user that their file(s) were rejected", () => {
|
||||
const editorLayout = new EditorLayout()
|
||||
|
||||
editorLayout.onDrop(["accepted.file.1", "accepted.file.2"], [])
|
||||
expect(global.alert.calls.length).toEqual(1)
|
||||
expect(global.alert.calls[0].arguments[0]).toMatch(/^Sorry.*/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("if exactly one file of an expected type is dropped", function() {
|
||||
it("should call the updateSpec function passed in as props with the contents of the file", () => {
|
||||
const fileContents = "This is my awesome file!"
|
||||
const props = {
|
||||
specActions: {
|
||||
updateSpec: expect.createSpy()
|
||||
}
|
||||
}
|
||||
global.FileReader.andReturn({
|
||||
readAsText: function () { this.onloadend() },
|
||||
result: fileContents
|
||||
})
|
||||
|
||||
const editorLayout = new EditorLayout(props)
|
||||
|
||||
editorLayout.onDrop(["accepted.file"])
|
||||
|
||||
expect(props.specActions.updateSpec).toHaveBeenCalledWith(fileContents, "fileDrop")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("onChange", function() {
|
||||
it("should call specActions.updateSpec with origin = editor by default", function() {
|
||||
// Given
|
||||
const spy = expect.createSpy()
|
||||
const props ={
|
||||
specActions: {
|
||||
updateSpec: spy
|
||||
}
|
||||
}
|
||||
const editorLayout = new EditorLayout(props)
|
||||
|
||||
// When
|
||||
editorLayout.onChange("one: 1")
|
||||
|
||||
// Then
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
expect(spy.calls[0].arguments).toEqual(["one: 1", "editor"])
|
||||
})
|
||||
|
||||
it("should allow (onDrop) to override with different origin", function() {
|
||||
// Given
|
||||
const spy = expect.createSpy()
|
||||
const props ={
|
||||
specActions: {
|
||||
updateSpec: spy
|
||||
}
|
||||
}
|
||||
const editorLayout = new EditorLayout(props)
|
||||
|
||||
// When
|
||||
editorLayout.onChange("one: 1", "somethingElse")
|
||||
|
||||
// Then
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
expect(spy.calls[0].arguments).toEqual(["one: 1", "somethingElse"])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,141 @@
|
||||
import EventEmitter from "events"
|
||||
import { createSpy } from "expect"
|
||||
|
||||
export class Range {
|
||||
constructor(...args) {
|
||||
this._args = args
|
||||
}
|
||||
}
|
||||
|
||||
const ACE_STUBS = [
|
||||
"setKeyboardHandler",
|
||||
"focus",
|
||||
"resize",
|
||||
"destroy",
|
||||
"setTheme",
|
||||
"setFontSize",
|
||||
"setHighlightActiveLine",
|
||||
"setReadOnly",
|
||||
"setShowPrintMargin",
|
||||
]
|
||||
|
||||
|
||||
const SESSION_STUBS = [
|
||||
"setMode",
|
||||
"setUseWrapMode",
|
||||
"setAnnotations"
|
||||
]
|
||||
|
||||
export class Session extends EventEmitter {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.$markerId = 0
|
||||
this._markers = []
|
||||
|
||||
SESSION_STUBS.forEach(stub => {
|
||||
this[stub] = createSpy()
|
||||
})
|
||||
|
||||
this.selection = new EventEmitter()
|
||||
this.selection.toJSON = createSpy().andReturn({fake: true})
|
||||
this.selection.fromJSON = createSpy().andReturn({fake: true})
|
||||
}
|
||||
|
||||
addMarker = createSpy().andCall((marker) => {
|
||||
this._markers.push({...marker, id: this.$markerId++ })
|
||||
})
|
||||
|
||||
getMarkers = createSpy().andCall(() => {
|
||||
return this._markers
|
||||
})
|
||||
|
||||
removeMarker = createSpy().andCall((markerId) => {
|
||||
this._markers = this._markers.filter(a => a.id !== markerId)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export default class Ace extends EventEmitter {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.session = new Session()
|
||||
this.$options = {}
|
||||
this._undoStack = [""]
|
||||
this._undoPointer = 0 // It starts with nothing..
|
||||
|
||||
ACE_STUBS.forEach(stub => {
|
||||
this[stub] = createSpy()
|
||||
})
|
||||
|
||||
this.renderer = {
|
||||
setShowGutter: createSpy(),
|
||||
setScrollMargin: createSpy()
|
||||
}
|
||||
}
|
||||
|
||||
edit = createSpy().andReturn(this)
|
||||
|
||||
acequire = createSpy().andCall((module) => {
|
||||
if(module == "ace/range") {
|
||||
return { Range }
|
||||
}
|
||||
})
|
||||
|
||||
getSession = createSpy().andCall(() => {
|
||||
return this.session
|
||||
})
|
||||
|
||||
setOption = createSpy().andCall((option, val) => {
|
||||
this.$options[option] = val
|
||||
})
|
||||
|
||||
setOptions = createSpy().andCall((options) => {
|
||||
this.$options = {...this.$options, ...options}
|
||||
})
|
||||
|
||||
getOption = createSpy().andCall((optionName) => {
|
||||
return this.$options[optionName]
|
||||
})
|
||||
|
||||
setValue = createSpy().andCall((val, addToUndo=true) => {
|
||||
if(addToUndo) {
|
||||
// Wipe out line of redos
|
||||
this._undoStack = this._undoStack.slice(0, this._undoPointer + 1)
|
||||
// Add new value
|
||||
this._undoStack.push(val)
|
||||
this._undoPointer++
|
||||
}
|
||||
this._value = val
|
||||
this.emit("change") // Remove
|
||||
this.emit("change") // Insert
|
||||
})
|
||||
|
||||
getValue = createSpy().andCall(() => {
|
||||
return this._value || ""
|
||||
})
|
||||
|
||||
// User API, which closer matches what we want to test ( ie: implementation can improve )
|
||||
userTypes = createSpy().andCall((val) => {
|
||||
this.setValue(this.getValue() + val)
|
||||
})
|
||||
|
||||
userSees = createSpy().andCall(() => {
|
||||
return this.getValue()
|
||||
})
|
||||
|
||||
userUndo = createSpy().andCall(() => {
|
||||
this._undoPointer = this._undoPointer > 0 ? this._undoPointer - 1 : 0
|
||||
this.setValue(this._undoStack[this._undoPointer], false)
|
||||
})
|
||||
|
||||
userRedo = createSpy().andCall(() => {
|
||||
const max = this._undoStack.length - 1
|
||||
// const oriPointer = this._undoPointer
|
||||
this._undoPointer = this._undoPointer < max ? this._undoPointer + 1 : max
|
||||
this.setValue(this._undoStack[this._undoPointer], false)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import { transformPathToArray } from "src/plugins/json-schema-validator/validator/path-translator"
|
||||
|
||||
describe("validation plugin - path translator", function(){
|
||||
|
||||
describe("string paths", function(){
|
||||
|
||||
it("should translate a simple string path to an array", function(){
|
||||
// Given
|
||||
let jsSpec = {
|
||||
one: {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
two: 2
|
||||
}
|
||||
|
||||
let path = "instance.one.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["one", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should translate an ambiguous string path to an array", function(){
|
||||
// Since JSONSchema uses periods to mark different properties,
|
||||
// a key with a period in it is ambiguous, because it can mean at least two things.
|
||||
// In our case, the path can mean:
|
||||
// ["google", "com", "a"] or ["google.com", "a"]
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should translate paths separated by brackets", function() {
|
||||
// Given
|
||||
let jsSpec = {
|
||||
definitions: {
|
||||
"One.Two": {
|
||||
a: "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = "instance.definitions[\"One.Two\"]"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["definitions", "One.Two"])
|
||||
})
|
||||
|
||||
it("should translate paths separated by brackets using single quotes", function() {
|
||||
// Given
|
||||
let jsSpec = {
|
||||
definitions: {
|
||||
"One.Two": {
|
||||
a: {
|
||||
b: {
|
||||
c: {
|
||||
d: 123
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = "instance.definitions[\'One.Two\'].a.b[\'c\'].d"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["definitions", "One.Two", "a", "b", "c", "d"])
|
||||
})
|
||||
|
||||
it("should translate paths separated by brackets with string keys, and then periods", function() {
|
||||
// Given
|
||||
let jsSpec = {
|
||||
definitions: {
|
||||
"One.Two": {
|
||||
a: "1",
|
||||
abc123: "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = "instance.definitions[\"One.Two\"].abc123"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["definitions", "One.Two", "abc123"])
|
||||
})
|
||||
|
||||
it("should translate paths separated by brackets with string keys & single quotes, and then periods", function() {
|
||||
// Given
|
||||
let jsSpec = {
|
||||
definitions: {
|
||||
"One.Two": {
|
||||
a: "1",
|
||||
abc123: "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = "instance.definitions[\'One.Two\'].abc123"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["definitions", "One.Two", "abc123"])
|
||||
})
|
||||
|
||||
it("should translate an doubly ambiguous string path to an array", function(){
|
||||
// Since JSONSchema uses periods to mark different properties,
|
||||
// a key with two periods in it (like "www.google.com") is doubly ambiguous,
|
||||
// because it can mean at least three things.
|
||||
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"www.google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.www.google.com.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["www.google.com", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should return null for an invalid path", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.net.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(null)
|
||||
|
||||
})
|
||||
|
||||
it("should return inline array indices in their own value", function(){
|
||||
// "a[1]" => ["a", "1"]
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
"here is the target"
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1]"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1"])
|
||||
|
||||
})
|
||||
|
||||
it("should return the correct path when the last part is ambiguous", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
{
|
||||
"gmail.com": 1234
|
||||
}
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1].gmail.com"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1", "gmail.com"])
|
||||
|
||||
})
|
||||
|
||||
it("should return the correct path when the last part is doubly ambiguous", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
{
|
||||
"www.gmail.com": 1234
|
||||
}
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1].www.gmail.com"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1", "www.gmail.com"])
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,278 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import { pathForPosition, positionRangeForPath } from "plugins/ast/ast"
|
||||
|
||||
describe("ASTManager", function() {
|
||||
describe("#pathForPosition", function () {
|
||||
describe("out of range", function () {
|
||||
it("returns empty array for out of range row", function () {
|
||||
let position = { line: 3, column: 0 }
|
||||
let path = pathForPosition("swagger: 2.0", position)
|
||||
|
||||
expect(path).toEqual([])
|
||||
})
|
||||
|
||||
it("returns empty array for out of range column", function () {
|
||||
let position = { line: 0, column: 100 }
|
||||
let path = pathForPosition("swagger: 2.0", position)
|
||||
|
||||
expect(path).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is a simple hash `swagger: 2.0`", function () {
|
||||
it("should return empty array when pointer is at middle of the hash key", function () {
|
||||
let position = { line: 0, column: 3 }
|
||||
let path = pathForPosition("swagger: 2.0", position)
|
||||
|
||||
expect(path).toEqual([])
|
||||
})
|
||||
|
||||
it("should return ['swagger'] when pointer is at the value", function () {
|
||||
let position = { line: 0, column: 10 }
|
||||
let path = pathForPosition("swagger: 2.0", position)
|
||||
|
||||
expect(path).toEqual(["swagger"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is an array: ['abc', 'cde']", function () {
|
||||
let yaml = [
|
||||
/*
|
||||
0
|
||||
01234567 */
|
||||
/* 0 */ "- abc",
|
||||
/* 1 */ "- def"
|
||||
].join("\n")
|
||||
|
||||
it("should return empty array when pointer is at array dash", function () {
|
||||
let position = { line: 0, column: 0 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual([])
|
||||
})
|
||||
|
||||
it("should return ['0'] when pointer is at abc", function () {
|
||||
let position = { line: 0, column: 3 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["0"])
|
||||
})
|
||||
|
||||
it("should return ['1'] when pointer is at abc", function () {
|
||||
let position = { line: 1, column: 3 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["1"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is an array of arrays", function () {
|
||||
let yaml = [
|
||||
/*
|
||||
0 10
|
||||
0123456789012345 */
|
||||
/* 0 */ "-",
|
||||
/* 1 */ " - abc",
|
||||
/* 2 */ " - def",
|
||||
/* 3 */ "-",
|
||||
/* 4 */ " - ABC",
|
||||
/* 5 */ " - DEF"
|
||||
].join("\n")
|
||||
|
||||
it("should return ['0', '0'] when pointer is at 'abc'", function () {
|
||||
let position = { line: 1, column: 5 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["0", "0"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is an array of hashs", function () {
|
||||
let yaml = [
|
||||
/*
|
||||
0 10
|
||||
0123456789012345 */
|
||||
/* 0 */ "- key: value",
|
||||
/* 1 */ " num: 1",
|
||||
/* 2 */ "- name: Tesla",
|
||||
/* 3 */ " year: 2016"
|
||||
].join("\n")
|
||||
|
||||
it("should return ['0'] when pointer is at 'key'", function () {
|
||||
let position = { line: 0, column: 3 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["0"])
|
||||
})
|
||||
|
||||
it("should return ['0', 'key'] when pointer is at 'value'", function () {
|
||||
let position = { line: 0, column: 9 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["0", "key"])
|
||||
})
|
||||
|
||||
it("should return ['1', 'year'] when pointer is at '2016'", function () {
|
||||
let position = { line: 3, column: 10 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["1", "year"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("full document", function () {
|
||||
let yaml = [
|
||||
/*
|
||||
0 10 20 30
|
||||
012345678901234567890123456789012345678 */
|
||||
/* 0 */ "swagger: '2.0'",
|
||||
/* 1 */ "info:",
|
||||
/* 2 */ " title: Test document",
|
||||
/* 3 */ " version: 0.0.1",
|
||||
/* 4 */ " contact:",
|
||||
/* 5 */ " name: Sahar",
|
||||
/* 6 */ " url: github.com",
|
||||
/* 7 */ " email: me@example.com",
|
||||
/* 8 */ " "
|
||||
].join("\n")
|
||||
|
||||
it("should return ['info', 'contact', 'email'] when pointer is at me@", function () {
|
||||
let position = { line: 7, column: 13 }
|
||||
let path = pathForPosition(yaml, position)
|
||||
|
||||
expect(path).toEqual(["info", "contact", "email"])
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#positionRangeForPath", function() {
|
||||
it("return {{-1, -1}, {-1, -1}} for invalid paths", function() {
|
||||
let yaml = [
|
||||
"key: value",
|
||||
"anotherKey: value"
|
||||
].join("\n")
|
||||
|
||||
let position = positionRangeForPath(yaml, ["invalid"])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: -1,
|
||||
column: -1
|
||||
})
|
||||
expect(position.end).toEqual({
|
||||
line: -1,
|
||||
column: -1
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is a simple hash `swagger: 2.0`", function() {
|
||||
let yaml = "swagger: 2.0"
|
||||
|
||||
it("return {0, 0} for start of empty array path (root)", function() {
|
||||
let position = positionRangeForPath(yaml, [])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: 0,
|
||||
column: 0,
|
||||
pointer: 0
|
||||
})
|
||||
})
|
||||
|
||||
it("return {0, 12} for end of empty array path (root)", function() {
|
||||
let position = positionRangeForPath(yaml, [])
|
||||
|
||||
expect(position.end).toEqual({
|
||||
line: 0,
|
||||
column: 12,
|
||||
pointer: 12
|
||||
})
|
||||
})
|
||||
|
||||
it("return {0, 9} for start of ['swagger']", function() {
|
||||
let position = positionRangeForPath(yaml, ["swagger"])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: 0,
|
||||
column: 9,
|
||||
pointer: 9
|
||||
})
|
||||
})
|
||||
|
||||
it("return {0, 12} for end of ['swagger']", function() {
|
||||
let position = positionRangeForPath(yaml, ["swagger"])
|
||||
|
||||
expect(position.end).toEqual({
|
||||
line: 0,
|
||||
column: 12,
|
||||
pointer: 12
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("when document is an array of primitives", function() {
|
||||
let yaml = [
|
||||
"key:",
|
||||
" - value1",
|
||||
" - value2"
|
||||
].join("\n")
|
||||
|
||||
it("returns {1, 4} for ['key', '0']", function() {
|
||||
let position = positionRangeForPath(yaml, ["key", "0"])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: 1,
|
||||
column: 4,
|
||||
pointer: 9
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("full document", function() {
|
||||
let yaml = [
|
||||
/*
|
||||
0 10 20 30
|
||||
012345678901234567890123456789012345678 */
|
||||
/* 0 */ "swagger: '2.0'",
|
||||
/* 1 */ "info:",
|
||||
/* 2 */ " title: Test document",
|
||||
/* 3 */ " version: 0.0.1",
|
||||
/* 4 */ " contact:",
|
||||
/* 5 */ " name: Sahar",
|
||||
/* 6 */ " url: github.com",
|
||||
/* 7 */ " email: me@example.com",
|
||||
/* 8 */ " "
|
||||
].join("\n")
|
||||
|
||||
it("returns {2, 2} for start of ['info']", function() {
|
||||
let position = positionRangeForPath(yaml, ["info"])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: 2,
|
||||
column: 2,
|
||||
pointer: 23
|
||||
})
|
||||
})
|
||||
|
||||
it("returns {5, 10} for start of ['info', 'contact', 'name']", function() {
|
||||
let position = positionRangeForPath(yaml, ["info", "contact", "name"])
|
||||
|
||||
expect(position.start).toEqual({
|
||||
line: 5,
|
||||
column: 10,
|
||||
pointer: 82
|
||||
})
|
||||
})
|
||||
|
||||
it("returns {5, 15} for end of ['info', 'contact', 'name']", function() {
|
||||
let position = positionRangeForPath(yaml, ["info", "contact", "name"])
|
||||
|
||||
expect(position.end).toEqual({
|
||||
line: 5,
|
||||
column: 15,
|
||||
pointer: 87
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import expect, { createSpy } from "expect"
|
||||
import dedent from "dedent"
|
||||
import { getPathForPosition } from "src/plugins/editor-autosuggest/fn.js"
|
||||
|
||||
describe("Editor Autosuggest Plugin", function() {
|
||||
describe("getPathForPosition - YAML value preparation", function() {
|
||||
it.skip("should not modify a valid simple map", function() {
|
||||
// Given
|
||||
const editorValue = dedent(`
|
||||
a: "one"
|
||||
b: "two"
|
||||
`)
|
||||
const pos = { row: 0, col: 0 }
|
||||
const AST = {
|
||||
pathForPosition: createSpy()
|
||||
}
|
||||
|
||||
// When
|
||||
getPathForPosition({ editorValue, pos, prefix: "", AST })
|
||||
|
||||
// Then
|
||||
expect(AST.pathForPosition).toHaveBeenCalled()
|
||||
|
||||
const [preparedEditorValue] = AST.pathForPosition.calls[0].arguments
|
||||
expect(preparedEditorValue).toEqual(editorValue) // should be unchanged
|
||||
})
|
||||
it("should modify an array member map fragment", function() {
|
||||
// Given
|
||||
const editorValue = dedent(`
|
||||
myArray:
|
||||
- one: "abc"
|
||||
- two:
|
||||
`)
|
||||
const pos = { row: 2, col: 6 }
|
||||
const AST = {
|
||||
pathForPosition: createSpy()
|
||||
}
|
||||
|
||||
// When
|
||||
getPathForPosition({ editorValue, pos, prefix: "", AST })
|
||||
|
||||
// Then
|
||||
expect(AST.pathForPosition).toHaveBeenCalled()
|
||||
|
||||
const [preparedEditorValue] = AST.pathForPosition.calls[0].arguments
|
||||
expect(preparedEditorValue).toEqual(editorValue + " ~")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,163 @@
|
||||
import expect from "expect"
|
||||
import SwaggerUi from "swagger-ui"
|
||||
import EditorMetadataPlugin from "plugins/editor-metadata"
|
||||
|
||||
function getSystem(spec) {
|
||||
return new Promise((resolve) => {
|
||||
const system = SwaggerUi({
|
||||
spec,
|
||||
domNode: null,
|
||||
presets: [
|
||||
SwaggerUi.plugins.SpecIndex,
|
||||
SwaggerUi.plugins.ErrIndex,
|
||||
SwaggerUi.plugins.DownloadUrl,
|
||||
SwaggerUi.plugins.SwaggerJsIndex,
|
||||
],
|
||||
initialState: {
|
||||
layout: undefined
|
||||
},
|
||||
plugins: [
|
||||
EditorMetadataPlugin,
|
||||
() => ({
|
||||
statePlugins: {
|
||||
configs: {
|
||||
actions: {
|
||||
loaded: () => {
|
||||
return {
|
||||
type: "noop"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
resolve(system)
|
||||
})
|
||||
}
|
||||
|
||||
describe("editor metadata plugin", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
it("should provide a `getEditorMetadata` method", async () => {
|
||||
const spec = {}
|
||||
|
||||
const system = await getSystem(spec)
|
||||
|
||||
expect(system.getEditorMetadata).toBeA(Function)
|
||||
})
|
||||
|
||||
it("should return JS object spec content from the `getEditorMetadata` method", async () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
description: "hello there!",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const system = await getSystem(spec)
|
||||
|
||||
expect(system.getEditorMetadata().contentString).toEqual(`{"swagger":"2.0","paths":{"/":{"get":{"description":"hello there!","responses":{"200":{"description":"ok"}}}}}}`)
|
||||
expect(system.getEditorMetadata().contentObject).toEqual(spec)
|
||||
})
|
||||
|
||||
|
||||
it("should return YAML string spec content from the `getEditorMetadata` method", async () => {
|
||||
const spec = `---
|
||||
swagger: '2.0'
|
||||
paths:
|
||||
"/":
|
||||
get:
|
||||
description: hello there!
|
||||
responses:
|
||||
'200':
|
||||
description: ok`
|
||||
|
||||
const system = await getSystem()
|
||||
|
||||
system.specActions.updateSpec(spec)
|
||||
|
||||
expect(system.getEditorMetadata().contentString).toEqual(spec)
|
||||
expect(system.getEditorMetadata().contentObject).toEqual({
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
description: "hello there!",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("should return isValid for a valid spec", async () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
description: "hello there!",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const system = await getSystem(spec)
|
||||
|
||||
expect(system.getEditorMetadata().isValid).toBeA("boolean")
|
||||
expect(system.getEditorMetadata().isValid).toBe(true)
|
||||
})
|
||||
|
||||
|
||||
it("should return isValid for an invalid spec", async () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
description: "hello there!",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const err = {
|
||||
type: "spec",
|
||||
message: "it's broken!"
|
||||
}
|
||||
|
||||
const system = await getSystem(spec)
|
||||
|
||||
system.errActions.newSpecErr(err)
|
||||
|
||||
expect(system.getEditorMetadata().isValid).toBeA("boolean")
|
||||
expect(system.getEditorMetadata().isValid).toBe(false)
|
||||
expect(system.getEditorMetadata().errors).toEqual([err])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,594 @@
|
||||
import expect, { createSpy } from "expect"
|
||||
import rewiremock from "rewiremock"
|
||||
import Enzyme, { shallow } from "enzyme"
|
||||
import Adapter from "enzyme-adapter-react-15"
|
||||
import React from "react"
|
||||
import FakeAce, { Session } from "test/unit/mocks/ace.js"
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
const pause = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
|
||||
const EVENTUALLY = 900 // ms
|
||||
|
||||
/**
|
||||
* We're mocking out the editor,
|
||||
* so uses of the phrase "should see this in editor",
|
||||
* will match to the following Ace methods:
|
||||
*
|
||||
* - "what user see's in editor" => fakeAce.userSees()
|
||||
* - "user types something (end of document)" => fakeAce.userTypes("hi")
|
||||
* - "Ctrl-Z" => fakeAce.userUndo()
|
||||
* - "Ctrl-Shift-Z" => fakeAce.userRedo()
|
||||
**/
|
||||
|
||||
describe("editor", function() {
|
||||
before(function () {
|
||||
// Enzyme.configure({ adapter: new Adapter()})
|
||||
Enzyme.configure({ adapter: new Adapter()})
|
||||
|
||||
// Whole bunch of mocks!
|
||||
rewiremock.enable()
|
||||
rewiremock("brace/mode/yaml").with({})
|
||||
rewiremock("brace/theme/tomorrow_night_eighties").with({})
|
||||
rewiremock("brace/ext/language_tools").with({})
|
||||
rewiremock("brace/ext/searchbox").with({})
|
||||
rewiremock("./brace-snippets-yaml").with({})
|
||||
rewiremock("./editor.less").with({})
|
||||
})
|
||||
|
||||
after(function() {
|
||||
rewiremock.disable()
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
delete require.cache[require.resolve("react-ace")]
|
||||
})
|
||||
|
||||
describe("fake ace", function() {
|
||||
|
||||
it("should be an event emitter", () => {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const spy = createSpy()
|
||||
fakeAce.on("foo", spy)
|
||||
|
||||
// When
|
||||
fakeAce.emit("foo", "bar")
|
||||
|
||||
// Then
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
expect(spy.calls[0].arguments[0]).toEqual("bar")
|
||||
})
|
||||
|
||||
it("should return `this`, when calling .edit", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
const res = fakeAce.edit()
|
||||
|
||||
// Then
|
||||
expect(res).toBe(fakeAce)
|
||||
})
|
||||
|
||||
|
||||
it("should keep track of setValue", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.setValue("foo")
|
||||
|
||||
// Then
|
||||
const res = fakeAce.getValue()
|
||||
expect(res).toEqual("foo")
|
||||
})
|
||||
|
||||
it("should spy on setValue", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.setValue("foo")
|
||||
|
||||
// Then
|
||||
expect(fakeAce.setValue.calls.length).toEqual(1)
|
||||
expect(fakeAce.setValue.calls[0].arguments[0]).toEqual("foo")
|
||||
})
|
||||
|
||||
it("should return a single session, with getSession", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
const res = fakeAce.getSession()
|
||||
|
||||
// Then
|
||||
expect(res).toBeA(Session)
|
||||
})
|
||||
|
||||
it("should add options via setOptions", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.setOptions({one: "uno"})
|
||||
|
||||
// Then
|
||||
const res = fakeAce.getOption("one")
|
||||
expect(res).toEqual("uno")
|
||||
})
|
||||
|
||||
describe("userUndo/Redo", function() {
|
||||
|
||||
it("should revert to last input", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.userTypes("one")
|
||||
|
||||
// When
|
||||
fakeAce.userTypes("two")
|
||||
|
||||
// Then
|
||||
fakeAce.userUndo()
|
||||
expect(fakeAce.userSees()).toEqual("one")
|
||||
})
|
||||
|
||||
it("should revert to empty document, no changes were made", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.userUndo()
|
||||
|
||||
// Then
|
||||
expect(fakeAce.userSees()).toEqual("")
|
||||
})
|
||||
|
||||
it("should revert to empty document, after arbitrary undos", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.userUndo()
|
||||
fakeAce.userUndo()
|
||||
fakeAce.userUndo()
|
||||
fakeAce.userUndo()
|
||||
|
||||
// Then
|
||||
expect(fakeAce.userSees()).toEqual("")
|
||||
})
|
||||
|
||||
it("should not extend redos after last change", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.userTypes("x")
|
||||
|
||||
// When
|
||||
fakeAce.userRedo()
|
||||
fakeAce.userRedo()
|
||||
fakeAce.userRedo()
|
||||
|
||||
// Then
|
||||
expect(fakeAce.userSees()).toEqual("x")
|
||||
})
|
||||
|
||||
it("should allow redo after single undo", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.userTypes("x")
|
||||
fakeAce.userTypes("x")
|
||||
fakeAce.userUndo()
|
||||
|
||||
// When
|
||||
fakeAce.userRedo()
|
||||
|
||||
// Then
|
||||
expect(fakeAce.userSees()).toEqual("xx")
|
||||
})
|
||||
|
||||
it("should create new thread of undo stack, after new change", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.userTypes("1")
|
||||
fakeAce.userTypes("2")
|
||||
fakeAce.userTypes("3")
|
||||
fakeAce.userTypes("4")
|
||||
fakeAce.userUndo() // 123
|
||||
fakeAce.userUndo() // 12
|
||||
fakeAce.userTypes("5") // 125
|
||||
|
||||
// When
|
||||
fakeAce.userRedo() // 125, don't extend beyond
|
||||
|
||||
// Then
|
||||
expect(fakeAce.userSees()).toEqual("125")
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("fake session", function() {
|
||||
|
||||
it("should keep add state for markers", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const fakeSession = fakeAce.getSession()
|
||||
|
||||
// When
|
||||
fakeSession.addMarker({one: 1})
|
||||
|
||||
// Then
|
||||
const res = fakeSession.getMarkers()
|
||||
expect(res).toBeAn("array")
|
||||
expect(res.length).toEqual(1)
|
||||
expect(res[0]).toEqual({id: 0, one: 1})
|
||||
})
|
||||
|
||||
it("should keep remove state for markers", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const fakeSession = fakeAce.getSession()
|
||||
fakeSession.addMarker({one: 1})
|
||||
|
||||
// When
|
||||
fakeSession.removeMarker(0)
|
||||
|
||||
// Then
|
||||
const res = fakeSession.getMarkers()
|
||||
expect(res).toBeAn("array")
|
||||
expect(res.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("should spy on addMarker", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const fakeSession = fakeAce.getSession()
|
||||
|
||||
// When
|
||||
fakeSession.addMarker({one: 1})
|
||||
|
||||
// Then
|
||||
expect(fakeSession.addMarker.calls.length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should spy on setMode", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const fakeSession = fakeAce.getSession()
|
||||
|
||||
// When
|
||||
fakeSession.setMode()
|
||||
|
||||
// Then
|
||||
expect(fakeSession.setMode.calls.length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should have a .selection which includes toJSON, fromJSON", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
const fakeSession = fakeAce.getSession()
|
||||
|
||||
// Then
|
||||
expect(fakeSession.selection).toIncludeKey("toJSON")
|
||||
expect(fakeSession.selection).toIncludeKey("fromJSON")
|
||||
})
|
||||
|
||||
|
||||
describe("userTypes", function() {
|
||||
it("should emit 'change'", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const spy = createSpy()
|
||||
fakeAce.on("change", spy)
|
||||
|
||||
// When
|
||||
fakeAce.userTypes("hello")
|
||||
|
||||
// Then
|
||||
expect(spy.calls.length).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
it("should change the value", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.userTypes("hello")
|
||||
|
||||
// Then
|
||||
expect(fakeAce.getValue()).toEqual("hello")
|
||||
})
|
||||
})
|
||||
|
||||
describe("userSees", function() {
|
||||
it("should match userTypes", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.userTypes("hi")
|
||||
|
||||
// Then
|
||||
const res = fakeAce.userSees()
|
||||
expect(res).toEqual("hi")
|
||||
})
|
||||
|
||||
it("should match setValue", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.setValue("hello")
|
||||
|
||||
// Then
|
||||
const res = fakeAce.userSees()
|
||||
expect(res).toEqual("hello")
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("renderer", function() {
|
||||
it("should have a stub for setShowGutter", function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
|
||||
// When
|
||||
fakeAce.renderer.setShowGutter("foo")
|
||||
|
||||
// Then
|
||||
expect(fakeAce.renderer.setShowGutter.calls.length).toEqual(1)
|
||||
expect(fakeAce.renderer.setShowGutter.calls[0].arguments[0]).toEqual("foo")
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("editor component", function() {
|
||||
|
||||
it("should EVENTUALLY call onChange when user enters input", (done) => {
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const spy = createSpy()
|
||||
const wrapper = shallow(
|
||||
<Editor onChange={spy} />
|
||||
)
|
||||
wrapper
|
||||
.find("ReactAce").shallow()
|
||||
|
||||
// When
|
||||
// Simulate user input
|
||||
fakeAce.userTypes("hello")
|
||||
|
||||
// Then
|
||||
setTimeout(() => {
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
expect(spy.calls[0].arguments[0]).toEqual("hello")
|
||||
done()
|
||||
}, EVENTUALLY)
|
||||
|
||||
})
|
||||
|
||||
it("should EVENTUALLY put the contents of `value` prop into editor, without regard to `origin` property", (done) => {
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
|
||||
// When
|
||||
const wrapper = shallow(
|
||||
<Editor value={"original value"} />
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
|
||||
// Then
|
||||
setTimeout(() => {
|
||||
expect(fakeAce.userSees()).toEqual("original value")
|
||||
done()
|
||||
}, EVENTUALLY)
|
||||
})
|
||||
|
||||
it("should EVENTUALLY put the contents of `value` prop into editor, with `foo` origin property ", (done) => {
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
|
||||
// When
|
||||
const wrapper = shallow(
|
||||
<Editor value={"original value"} origin="foo" />
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
|
||||
// Then
|
||||
setTimeout(() => {
|
||||
expect(fakeAce.userSees()).toEqual("original value")
|
||||
done()
|
||||
}, EVENTUALLY)
|
||||
})
|
||||
|
||||
it("should NEVER update ace if the yaml originated in editor", async () => {
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
|
||||
// When
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" />
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
wrapper.setProps({value: "new value", origin: "editor"})
|
||||
|
||||
// Then
|
||||
await pause(EVENTUALLY)
|
||||
expect(fakeAce.userSees()).toEqual("original value")
|
||||
})
|
||||
|
||||
// SKIPPED: Does this have any value at this level? And not editor-container?
|
||||
it.skip("SKIP: should EVENTUALLY call onChange ONCE if the user types/pauses/types", async function() {
|
||||
this.timeout(10000)
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const spy = createSpy()
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" onChange={spy}/>
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
|
||||
// When
|
||||
fakeAce.userTypes(" one")
|
||||
await pause(EVENTUALLY / 2)
|
||||
fakeAce.userTypes("two")
|
||||
await pause(EVENTUALLY / 2)
|
||||
fakeAce.userTypes("three")
|
||||
await pause(EVENTUALLY / 2)
|
||||
|
||||
await pause(EVENTUALLY * 2)
|
||||
expect(fakeAce.userSees()).toEqual("original value onetwothree")
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should EVENTUALLY call onChange when ctrl-z", async function() {
|
||||
this.timeout(10000)
|
||||
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const spy = createSpy()
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" onChange={spy}/>
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
fakeAce.userTypes("one")
|
||||
|
||||
// When
|
||||
fakeAce.userUndo()
|
||||
|
||||
await pause(EVENTUALLY)
|
||||
expect(fakeAce.userSees()).toEqual("original value")
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
})
|
||||
|
||||
describe("markers", function() {
|
||||
|
||||
it("should place markers into editor", async function() {
|
||||
// Given
|
||||
const fakeAce = new FakeAce()
|
||||
const spy = createSpy()
|
||||
rewiremock("brace").with(fakeAce)
|
||||
rewiremock("../editor-helpers/marker-placer").with({placeMarkerDecorations: spy})
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const dummy = fromJS({one: 1})
|
||||
const wrapper = shallow(
|
||||
<Editor markers={dummy} />
|
||||
)
|
||||
|
||||
// When
|
||||
wrapper.find("ReactAce").shallow()
|
||||
await pause(EVENTUALLY)
|
||||
|
||||
// Then
|
||||
expect(spy.calls.length).toEqual(1)
|
||||
expect(spy.calls[0].arguments[0]).toInclude({markers: {one: 1}})
|
||||
})
|
||||
|
||||
it("should place markers after yaml", async function() {
|
||||
// Given
|
||||
const order = []
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.setValue.andCall(() => order.push("setValue"))
|
||||
const spy = createSpy().andCall(() => order.push("placeMarkers"))
|
||||
rewiremock("brace").with(fakeAce)
|
||||
rewiremock("../editor-helpers/marker-placer").with({placeMarkerDecorations: spy})
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" markers={{}} />
|
||||
)
|
||||
|
||||
// When
|
||||
wrapper.find("ReactAce").shallow()
|
||||
await pause(EVENTUALLY)
|
||||
|
||||
// Then
|
||||
expect(order).toEqual(["setValue", "placeMarkers"])
|
||||
})
|
||||
|
||||
|
||||
it.skip("should Test for markers being disabled/enabled during a yaml update", async function() {
|
||||
// Given
|
||||
const order = []
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.setValue.andCall(() => order.push("setValue"))
|
||||
const spy = createSpy().andCall(() => {
|
||||
order.push("placeMarkers")
|
||||
return () => order.push("removeMarkers")
|
||||
})
|
||||
rewiremock("brace").with(fakeAce)
|
||||
rewiremock("../editor-helpers/marker-placer").with({placeMarkerDecorations: spy})
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" markers={{}} />
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
|
||||
// When
|
||||
wrapper.setProps({value: "new value", origin: "bob"})
|
||||
await pause(EVENTUALLY)
|
||||
|
||||
// Then
|
||||
expect(order).toEqual(["setValue", "placeMarkers", "removeMarkers", "setValue", "placeMarkers"])
|
||||
})
|
||||
|
||||
it.skip("should Test for markers being disabled/enabled during ctrl-z", async function() {
|
||||
// Given
|
||||
const order = []
|
||||
const fakeAce = new FakeAce()
|
||||
fakeAce.setValue.andCall(() => order.push("setValue"))
|
||||
const spy = createSpy().andCall(() => {
|
||||
order.push("placeMarkers")
|
||||
return () => order.push("removeMarkers")
|
||||
})
|
||||
rewiremock("brace").with(fakeAce)
|
||||
rewiremock("../editor-helpers/marker-placer").with({placeMarkerDecorations: spy})
|
||||
const makeEditor = require("plugins/editor/components/editor.jsx").default
|
||||
const Editor = makeEditor({})
|
||||
const wrapper = shallow(
|
||||
<Editor value="original value" markers={{}} />
|
||||
)
|
||||
wrapper.find("ReactAce").shallow()
|
||||
|
||||
// When
|
||||
fakeAce.userUndo()
|
||||
await pause(EVENTUALLY)
|
||||
|
||||
// Then
|
||||
expect(order).toEqual(["setValue", "placeMarkers", "removeMarkers", "setValue", "placeMarkers"])
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import SwaggerUi from "swagger-ui"
|
||||
import EditorSpecPlugin from "src/plugins/editor/spec"
|
||||
import expect from "expect"
|
||||
|
||||
describe("editor state plugins", function() {
|
||||
describe("specSelectors.specOrigin", function() {
|
||||
it("should default to 'non-editor'", function() {
|
||||
// Given
|
||||
const system = SwaggerUi({plugins: [EditorSpecPlugin]})
|
||||
|
||||
// When
|
||||
const res = system.specSelectors.specOrigin()
|
||||
|
||||
// Then
|
||||
expect(res).toEqual("not-editor")
|
||||
})
|
||||
})
|
||||
describe("specActions.updateSpec", function() {
|
||||
it("should add a parameter - origin - to state", function() {
|
||||
// Given
|
||||
const system = SwaggerUi({plugins: [EditorSpecPlugin]})
|
||||
|
||||
// When
|
||||
system.specActions.updateSpec("one: 1", "editor")
|
||||
|
||||
// Then
|
||||
const res = system.specSelectors.specOrigin()
|
||||
expect(res).toEqual("editor")
|
||||
})
|
||||
|
||||
it("should default to 'non-editor'", function() {
|
||||
// Given
|
||||
const system = SwaggerUi({plugins: [EditorSpecPlugin]})
|
||||
|
||||
// When
|
||||
system.specActions.updateSpec("one: 1")
|
||||
|
||||
// Then
|
||||
const res = system.specSelectors.specOrigin()
|
||||
expect(res).toEqual("not-editor")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,90 @@
|
||||
import YAML from "js-yaml"
|
||||
import JSONSchemaValidator from "src/plugins/json-schema-validator/validator/index.js"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import expect from "expect"
|
||||
|
||||
const swagger2SchemaYaml = fs.readFileSync(path.join(__dirname, "../../../../src/plugins/json-schema-validator/swagger2-schema.yaml")).toString()
|
||||
const oas3SchemaYaml = fs.readFileSync(path.join(__dirname, "../../../../src/plugins/json-schema-validator/oas3-schema.yaml")).toString()
|
||||
|
||||
const swagger2Schema = YAML.safeLoad(swagger2SchemaYaml)
|
||||
const oas3Schema = YAML.safeLoad(oas3SchemaYaml)
|
||||
|
||||
|
||||
var testDocuments = fs
|
||||
.readdirSync(__dirname + "/test-documents")
|
||||
.filter(path => path.endsWith("yaml"))
|
||||
.map(path => ({
|
||||
path: "/test-documents/" + path,
|
||||
contentString: fs.readFileSync(__dirname + "/test-documents/" + path, "utf8"),
|
||||
}))
|
||||
.map(doc => ({
|
||||
path: doc.path,
|
||||
content: YAML.safeLoad(doc.contentString)
|
||||
}))
|
||||
|
||||
testDocuments.forEach(doc => {
|
||||
const { path, content } = doc
|
||||
const { meta = {}, cases = [] } = content
|
||||
|
||||
const validator = new JSONSchemaValidator()
|
||||
validator.addSchema(swagger2Schema, ["openapi-2.0"])
|
||||
validator.addSchema(oas3Schema, ["openapi-3.0"])
|
||||
|
||||
const rootDescribe = meta.skip ? describe.skip : describe
|
||||
|
||||
rootDescribe(`schema validation plugin - ` + (meta.title || path), function() {
|
||||
if(content.input && content.output) {
|
||||
// fold simple input/output docs into implicit cases
|
||||
cases.push({
|
||||
input: content.input,
|
||||
output: content.output,
|
||||
})
|
||||
}
|
||||
|
||||
if(cases && cases.length) {
|
||||
cases.forEach(currentCase => {
|
||||
const versionDefaultSchema = currentCase.input.openapi && !currentCase.input.swagger ? "openapi-3.0" : "openapi-2.0"
|
||||
|
||||
const result = validator.validate({
|
||||
jsSpec: currentCase.input,
|
||||
specStr: "", // not needed here
|
||||
schemaPath: versionDefaultSchema,
|
||||
source: "structural",
|
||||
})
|
||||
|
||||
if(currentCase.name) {
|
||||
// only create a new describe block if we have a name
|
||||
describe(currentCase.name || "", function () {
|
||||
assertCaseExpectations(currentCase, result)
|
||||
})
|
||||
} else {
|
||||
// else, just do the assertions under the root describe block
|
||||
assertCaseExpectations(currentCase, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function assertCaseExpectations(currentCase, result) {
|
||||
const itFn = currentCase.skip ? it.skip : it
|
||||
if (currentCase.output.match !== undefined) {
|
||||
itFn("should match expected error output", function () {
|
||||
expect(result).toMatch(currentCase.output.match)
|
||||
})
|
||||
}
|
||||
|
||||
if (currentCase.output.length !== undefined) {
|
||||
itFn("should have expected array length", function () {
|
||||
expect(result).toBeAn(Array)
|
||||
expect(result.length).toBe(currentCase.output.length)
|
||||
})
|
||||
}
|
||||
|
||||
if(currentCase.output.equal !== undefined) {
|
||||
itFn("should equal expected value", function() {
|
||||
expect(result).toEqual(currentCase.output.equal)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
meta:
|
||||
title: "#1394 - path key that does not start with `/`"
|
||||
cases:
|
||||
- name: in Swagger 2
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
version: "#1394"
|
||||
title: "bug #1394"
|
||||
paths:
|
||||
myPath:
|
||||
get:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should only have path names that start with `/`
|
||||
path: [paths]
|
||||
source: structural
|
||||
- name: in OpenAPI 3
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: "#1394"
|
||||
title: "bug #1394"
|
||||
paths:
|
||||
myPath:
|
||||
get:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should only have path names that start with `/`
|
||||
path: [paths]
|
||||
source: structural
|
||||
@@ -0,0 +1,30 @@
|
||||
meta:
|
||||
title: "#1480 - illegal `tokenUrl` in implicit OAuth Flow Object"
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
license:
|
||||
name: MIT
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
apiOAuth:
|
||||
type: oauth2
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: 'https://myapi.com/oauth/authorize'
|
||||
tokenUrl: 'https://myapi.com/oauth/token'
|
||||
refreshUrl: 'https://myapi.com/oauth/token/refresh'
|
||||
scopes:
|
||||
-'write:pets': "modify pets in your account"
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should NOT have additional properties
|
||||
additionalProperty: tokenUrl
|
||||
path: [components, securitySchemes, apiOAuth, flows, implicit]
|
||||
source: structural
|
||||
@@ -0,0 +1,26 @@
|
||||
meta:
|
||||
title: "#1489 - illegal presence of both `example` and `examples` in Media Type Object"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
version: "validation"
|
||||
title: Spec with both example and examples
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
text/plain:
|
||||
example: sample response
|
||||
examples:
|
||||
foo:
|
||||
value: anonther response
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should not have both `example` and `examples`, as they are mutually exclusive
|
||||
path: [paths, /, get, responses, "200", content, text/plain]
|
||||
source: structural
|
||||
@@ -0,0 +1,32 @@
|
||||
meta:
|
||||
title: "#1511 - illegal `example` in Path Parameter Object"
|
||||
input:
|
||||
swagger: '2.0'
|
||||
info:
|
||||
version: '1'
|
||||
title: oneOf validation fail
|
||||
description: ok
|
||||
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
summary: Extra `example` field in parameter
|
||||
description: ok
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
type: string
|
||||
required: true
|
||||
example: ok
|
||||
responses:
|
||||
"200":
|
||||
description: Ok
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should NOT have additional properties
|
||||
additionalProperty: example
|
||||
path: [paths, /, get, parameters, "0"]
|
||||
source: structural
|
||||
@@ -0,0 +1,72 @@
|
||||
meta:
|
||||
title: "#1519 - incorrect usage of Schema Object properties"
|
||||
cases:
|
||||
- name: "`required: true` in an object schema property subschema"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Example API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
post:
|
||||
tags:
|
||||
- login
|
||||
summary: Login using email or nickname
|
||||
description: Returns customer info and jwt access and refresh tokens
|
||||
security:
|
||||
- apiKey: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
emailOrNickname:
|
||||
type: string
|
||||
required: true
|
||||
password:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: ok
|
||||
output:
|
||||
length: 2
|
||||
match:
|
||||
- level: error
|
||||
message: should be an array of property names required within an object schema
|
||||
path: [paths, "/", post, requestBody, content, application/json, schema, properties, emailOrNickname, required]
|
||||
source: structural
|
||||
- level: error
|
||||
message: should be an array of property names required within an object schema
|
||||
path: [paths, "/", post, requestBody, content, application/json, schema, properties, password, required]
|
||||
source: structural
|
||||
- name: "`type: bool` instead of `type: boolean`"
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: A thing
|
||||
license:
|
||||
name: AGPL-3.0
|
||||
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
ExcellentThing:
|
||||
description: "Something excellent"
|
||||
properties:
|
||||
extremelyExcellent:
|
||||
description: "If false, this item is only somewhat excellent"
|
||||
type: bool
|
||||
example: true
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
source: structural
|
||||
message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string
|
||||
path: [components, schemas, ExcellentThing, properties, extremelyExcellent, type]
|
||||
@@ -0,0 +1,25 @@
|
||||
meta:
|
||||
title: "#1672 - Header Security Scheme Object lacking a `name`"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: "#1672"
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas: {}
|
||||
securitySchemes:
|
||||
headerFoo:
|
||||
type: apiKey
|
||||
in: header
|
||||
links: {}
|
||||
callbacks: {}
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should have required property 'name'
|
||||
missingProperty: name
|
||||
path: [components, securitySchemes, headerFoo]
|
||||
source: structural
|
||||
@@ -0,0 +1,17 @@
|
||||
meta:
|
||||
title: "#1709 - relative (badly-formed) OIDC URIs"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: "#1709"
|
||||
version: "1.0"
|
||||
servers:
|
||||
- url: https://api.example.com/v2
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
openId:
|
||||
type: openIdConnect
|
||||
openIdConnectUrl: /.well-known/openid-configuration
|
||||
output:
|
||||
equal: null # no errors
|
||||
@@ -0,0 +1,74 @@
|
||||
meta:
|
||||
title: "#1711 - Misleading 2.0 Security Scheme object errors"
|
||||
cases:
|
||||
- name: oauth2 definitions with non-absolute URIs
|
||||
input:
|
||||
swagger: "2.0"
|
||||
paths: {}
|
||||
info:
|
||||
title: wow
|
||||
version: 1.0.0
|
||||
securityDefinitions:
|
||||
oauth2_Password:
|
||||
type: oauth2
|
||||
tokenUrl: /authorizationserver/oauth/token
|
||||
flow: password
|
||||
scopes:
|
||||
basic: ''
|
||||
oauth2_client_credentials:
|
||||
type: oauth2
|
||||
tokenUrl: /authorizationserver/oauth/token
|
||||
flow: application
|
||||
scopes:
|
||||
extended: ''
|
||||
output:
|
||||
length: 2
|
||||
match:
|
||||
- level: error
|
||||
message: should be an absolute URI
|
||||
path: [securityDefinitions, oauth2_Password, tokenUrl]
|
||||
source: structural
|
||||
- level: error
|
||||
message: should be an absolute URI
|
||||
path: [securityDefinitions, oauth2_client_credentials, tokenUrl]
|
||||
source: structural
|
||||
- name: apiKey definition missing `in` property
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: wow
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
securityDefinitions:
|
||||
apikey:
|
||||
type: apiKey
|
||||
name: myAuth
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should have required property 'in'
|
||||
missingProperty: in
|
||||
path: [securityDefinitions, apikey]
|
||||
source: structural
|
||||
- name: basic definition with a superfluous `in` property
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: wow
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
securityDefinitions:
|
||||
basic:
|
||||
type: basic
|
||||
in: header
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should NOT have additional properties
|
||||
additionalProperty: in
|
||||
path: [securityDefinitions, basic]
|
||||
source: structural
|
||||
@@ -0,0 +1,29 @@
|
||||
meta:
|
||||
title: "#1797 - Missing `required: true` within a 3.0 Path Parameter Object"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Foo OpenApi Spec
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
allowEmptyValue: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: ok
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should have required property 'required'
|
||||
missingProperty: required
|
||||
path: [paths, /, get, parameters, "0"]
|
||||
source: structural
|
||||
@@ -0,0 +1,37 @@
|
||||
meta:
|
||||
title: "#1808 - Misleading errors for a Responses Object within Operation Object"
|
||||
cases:
|
||||
- name: in OpenAPI 2.0
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
version: 0.0.0
|
||||
title: test
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses: {}
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should define at least one response
|
||||
path: [paths, /, get, responses]
|
||||
source: structural
|
||||
- name: in OpenAPI 3.0
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 0.0.0
|
||||
title: test
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses: {}
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should define at least one response
|
||||
path: [paths, /, get, responses]
|
||||
source: structural
|
||||
@@ -0,0 +1,20 @@
|
||||
meta:
|
||||
title: "#1832 - Schema Object with a non-valid type"
|
||||
input:
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: test
|
||||
version: 0.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
Model1:
|
||||
type: int
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string
|
||||
path: [definitions, Model1, type]
|
||||
source: structural
|
||||
@@ -0,0 +1,28 @@
|
||||
meta:
|
||||
title: "#1853 - OpenAPI 3.0 path parameter missing both `schema` and `content`"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Contact List API
|
||||
description: CRUD a simple Contact item.
|
||||
version: '0.1'
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
summary: Get a single contact by Id
|
||||
operationId: getContactById
|
||||
responses:
|
||||
"200":
|
||||
description: ok
|
||||
parameters:
|
||||
- name: contactId
|
||||
in: path
|
||||
description: ID of contact to return
|
||||
required: true
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- level: error
|
||||
message: should have either a `schema` or `content` property
|
||||
path: [paths, /, get, parameters, "0"]
|
||||
source: structural
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
|
||||
cases:
|
||||
- name: a valid additionalProperties schema
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
output:
|
||||
equal: null
|
||||
- name: a valid schema with an invalid additionalProperties schema
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: int
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string
|
||||
path: [definitions, mySchema, additionalProperties]
|
||||
- name: a valid schema with an invalid additionalProperties string value
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: "wow"
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: "should be either a Schema Object or a boolean value"
|
||||
path: [definitions, mySchema, additionalProperties]
|
||||
- name: a valid schema with a valid additionalProperties reference
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#"
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema with `additionalProperties: true`"
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema with `additionalProperties: false`"
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema without `additionalProperties`"
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
definitions:
|
||||
mySchema:
|
||||
type: object
|
||||
output:
|
||||
equal: null
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
|
||||
cases:
|
||||
- name: a valid additionalProperties schema
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
output:
|
||||
equal: null
|
||||
- name: a valid schema with an invalid additionalProperties schema
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: int
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string
|
||||
path: [components, schemas, mySchema, additionalProperties]
|
||||
- name: a valid schema with an invalid additionalProperties string value
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: "wow"
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: should be a Reference Object, Schema Object, or boolean value
|
||||
path: [components, schemas, mySchema, additionalProperties]
|
||||
- name: a valid schema with a valid additionalProperties reference
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#"
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema with `additionalProperties: true`"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema with `additionalProperties: false`"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
output:
|
||||
equal: null
|
||||
- name: "a valid schema without `additionalProperties`"
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
mySchema:
|
||||
type: object
|
||||
output:
|
||||
equal: null
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
meta:
|
||||
title: 3.0 Parameter Object schema/content exclusivity
|
||||
cases:
|
||||
- name: both schema and content defined
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
schema:
|
||||
type: string
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: ok
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: should have either a `schema` or `content` property
|
||||
- name: content defined with disallowed siblings also present
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
explode: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: ok
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: "should not have `style`, `explode`, `allowReserved`, `example`, or `examples` when `content` is present"
|
||||
- name: neither schema or content defined
|
||||
input:
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
|
||||
responses:
|
||||
'200':
|
||||
description: ok
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: should have either a `schema` or `content` property
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
cases:
|
||||
- name: schema reference with additional property
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: "abc"
|
||||
xyz: 123
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: |-
|
||||
should NOT have additional properties
|
||||
additionalProperty: xyz
|
||||
path: [paths, /, get, responses, 200, schema]
|
||||
|
||||
- name: schema with invalid `type`
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
type: blah
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string, file
|
||||
- name: valid schema with invalid subschema type
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
properties:
|
||||
a:
|
||||
type: str
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: |-
|
||||
should be equal to one of the allowed values
|
||||
allowedValues: array, boolean, integer, number, object, string
|
||||
- name: valid schema with no `type`
|
||||
input:
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
output:
|
||||
equal: null
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
cases:
|
||||
- name: valid HTTP/bearer security scheme
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info: { title: "", version: 1.0.0 }
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
myScheme:
|
||||
type: http
|
||||
scheme: bearer
|
||||
output:
|
||||
equal: null
|
||||
- name: valid HTTP/bearer+bearerFormat security scheme
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info: { title: "", version: 1.0.0 }
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
myScheme:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: Bearer
|
||||
output:
|
||||
equal: null
|
||||
- name: valid HTTP/basic security scheme
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info: { title: "", version: 1.0.0 }
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
myScheme:
|
||||
type: http
|
||||
scheme: basic
|
||||
output:
|
||||
equal: null
|
||||
- name: invalid HTTP/basic+bearerFormat security scheme
|
||||
input:
|
||||
openapi: 3.0.0
|
||||
info: { title: "", version: 1.0.0 }
|
||||
paths: {}
|
||||
components:
|
||||
securitySchemes:
|
||||
myScheme:
|
||||
type: http
|
||||
scheme: basic
|
||||
bearerFormat: Bearer
|
||||
output:
|
||||
length: 1
|
||||
match:
|
||||
- message: "should NOT have a `bearerFormat` property without `scheme: bearer` being set"
|
||||
path: [components, securitySchemes, myScheme]
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# The Editor's semantic validation handles this better than JSON Schema can,
|
||||
# so we want it to be disabled in the schema validator.
|
||||
|
||||
meta:
|
||||
title: "Tag Objects should not be validated for uniqueness"
|
||||
cases:
|
||||
- name: in Swagger 2.0
|
||||
input:
|
||||
swagger: '2.0'
|
||||
info:
|
||||
version: 0.0.0
|
||||
title: test
|
||||
tags:
|
||||
- name: pet
|
||||
- name: pet
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
paths: {}
|
||||
output:
|
||||
equal: null
|
||||
- name: in OpenAPI 3.0
|
||||
input:
|
||||
openapi: 3.0.2
|
||||
info:
|
||||
version: 0.0.0
|
||||
title: test
|
||||
tags:
|
||||
- name: pet
|
||||
- name: pet
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
paths: {}
|
||||
output:
|
||||
equal: null
|
||||
@@ -0,0 +1,922 @@
|
||||
import expect from "expect"
|
||||
import SwaggerUi from "swagger-ui"
|
||||
import ValidateBasePlugin from "plugins/validate-base"
|
||||
import ValidateSemanticPlugin from "plugins/validate-semantic"
|
||||
import ASTPlugin from "plugins/ast"
|
||||
|
||||
function getSystem(spec) {
|
||||
return new Promise((resolve) => {
|
||||
const system = SwaggerUi({
|
||||
spec,
|
||||
domNode: null,
|
||||
presets: [
|
||||
SwaggerUi.plugins.SpecIndex,
|
||||
SwaggerUi.plugins.ErrIndex,
|
||||
SwaggerUi.plugins.DownloadUrl,
|
||||
SwaggerUi.plugins.SwaggerJsIndex,
|
||||
],
|
||||
initialState: {
|
||||
layout: undefined
|
||||
},
|
||||
plugins: [
|
||||
ASTPlugin,
|
||||
ValidateBasePlugin,
|
||||
ValidateSemanticPlugin,
|
||||
() => ({
|
||||
statePlugins: {
|
||||
configs: {
|
||||
actions: {
|
||||
loaded: () => {
|
||||
return {
|
||||
type: "noop"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
resolve(system)
|
||||
})
|
||||
}
|
||||
|
||||
describe("validation plugin - selectors", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
describe("allSchemas", function() {
|
||||
describe("OpenAPI 2.0", function() {
|
||||
it("should pick up parameter schemas", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
test: {
|
||||
parameters: [{
|
||||
name: "common"
|
||||
}],
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "tags"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].path).toEqual(["paths","test","parameters","0"])
|
||||
expect(nodes[1].path).toEqual(["paths","test","get","parameters","0"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up response schemas", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
test: {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path.join(".")).toEqual("paths.test.get.responses.200.schema")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up global definitions", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
properties: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up global definitions named 'x-' (i.e. not consider them extensions)", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
"x-fooModel": {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("x-fooModel")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up response headers", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
test: {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
headers: {
|
||||
foo: {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path.join(".")).toEqual("paths.test.get.responses.200.headers.foo")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in properties", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in additionalProperties - simple", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("additionalProperties")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in additionalProperties - complex", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(3)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("additionalProperties")
|
||||
expect(nodes[2].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in array", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("items")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in array of objects", () => {
|
||||
const spec = {
|
||||
definitions: {
|
||||
fooModel: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(3)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("items")
|
||||
expect(nodes[2].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("OpenAPI 3.0", function() {
|
||||
it("should pick up schemas in 'components'", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up schemas named 'x-' (i.e. not consider them extensions)", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
"x-fooModel": {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("x-fooModel")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up request body schemas and response schemas", () => {
|
||||
const spec = {
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/ping": {
|
||||
"post": {
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/myRequestMediaType": {
|
||||
"schema": {
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/myResponseMediaType": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].key).toEqual("schema")
|
||||
expect(nodes[0].parent.key).toEqual("application/myRequestMediaType")
|
||||
expect(nodes[1].key).toEqual("schema")
|
||||
expect(nodes[1].parent.key).toEqual("application/myResponseMediaType")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up schemas in response components", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
responses: {
|
||||
MyResponse: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["components","responses","MyResponse","content","application/json","schema"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in properties", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in additionalProperties - simple", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("additionalProperties")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in additionalProperties - complex", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(3)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("additionalProperties")
|
||||
expect(nodes[2].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in array", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("items")
|
||||
})
|
||||
})
|
||||
|
||||
it("should pick up subschemas in array of objects", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
fooModel: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSchemas())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(3)
|
||||
expect(nodes[0].key).toEqual("fooModel")
|
||||
expect(nodes[1].key).toEqual("items")
|
||||
expect(nodes[2].key).toEqual("foo")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("allResponses", function() {
|
||||
it("should pick up operation responses with specific codes like 200", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["paths","/foo","get","responses","200"])
|
||||
})
|
||||
})
|
||||
it("should pick up the 'default' response", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
default: {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["paths","/foo","get","responses","default"])
|
||||
})
|
||||
})
|
||||
it("should pick up OAS3 response code ranges like 2XX", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"2XX": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["paths","/foo","get","responses","2XX"])
|
||||
})
|
||||
})
|
||||
it("should ignore x- extension keys in an operation's `responses` section", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"x-ext": {
|
||||
foo: "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should pick up OpenAPI 2 global response definitions", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
responses: {
|
||||
OkResponse: {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["responses","OkResponse"])
|
||||
})
|
||||
})
|
||||
it("should pick up OpenAPI 2 response definitions named 'x-'", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
responses: {
|
||||
"x-MyResponse": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["responses","x-MyResponse"])
|
||||
})
|
||||
})
|
||||
it("should pick up OpenAPI 3 response components", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components : {
|
||||
responses: {
|
||||
OkResponse: {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["components","responses","OkResponse"])
|
||||
})
|
||||
})
|
||||
it("should pick up OpenAPI 3 response components named 'x-'", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components : {
|
||||
responses: {
|
||||
"x-MyResponse": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allResponses())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].path).toEqual(["components","responses","x-MyResponse"])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("allSecurityDefinitions", () => {
|
||||
it("should pick up OAS2 security schemes", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
securityDefinitions: {
|
||||
basicAuth: {
|
||||
type: "basic"
|
||||
},
|
||||
apiKeyAuth: {
|
||||
type: "apiKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].key).toEqual("basicAuth")
|
||||
expect(nodes[1].key).toEqual("apiKeyAuth")
|
||||
})
|
||||
})
|
||||
it("should pick up OAS3 security schemes", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
securitySchemes: {
|
||||
basicAuth: {
|
||||
type: "http"
|
||||
},
|
||||
apiKeyAuth: {
|
||||
type: "apiKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].key).toEqual("basicAuth")
|
||||
expect(nodes[1].key).toEqual("apiKeyAuth")
|
||||
})
|
||||
})
|
||||
it("should pick up OAS2 security schemes named x- (i.e. not consider them extensions)", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
securityDefinitions: {
|
||||
"x-auth": {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("x-auth")
|
||||
})
|
||||
})
|
||||
it("should pick up OAS3 security schemes named x- (i.e. not consider them extensions)", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
securitySchemes: {
|
||||
"x-auth": {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(1)
|
||||
expect(nodes[0].key).toEqual("x-auth")
|
||||
})
|
||||
})
|
||||
it("should not pick up arbitrary OAS2 nodes named `securityDefinitions`", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
definitions: {
|
||||
securityDefinitions: {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should not pick up arbitrary OAS3 nodes named `securitySchemes`", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
securitySchemes: {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityDefinitions())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("allSecurityRequirements", () => {
|
||||
it("should pick up global security requirements", () => {
|
||||
const spec = {
|
||||
security: [
|
||||
{
|
||||
auth1: []
|
||||
},
|
||||
{
|
||||
auth2: [],
|
||||
auth3: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityRequirements())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].node).toEqual(
|
||||
{ "auth1": [] }
|
||||
)
|
||||
expect(nodes[1].node).toEqual(
|
||||
{ "auth2": [], "auth3": [] }
|
||||
)
|
||||
})
|
||||
})
|
||||
it("should pick up operation-level security requirements", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
security: [
|
||||
{
|
||||
auth1: []
|
||||
},
|
||||
{
|
||||
auth2: [],
|
||||
auth3: []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityRequirements())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].node).toEqual(
|
||||
{ "auth1": [] }
|
||||
)
|
||||
expect(nodes[1].node).toEqual(
|
||||
{ "auth2": [], "auth3": [] }
|
||||
)
|
||||
})
|
||||
})
|
||||
it("should pick up empty security requirements", () => {
|
||||
const spec = {
|
||||
security: [
|
||||
{}
|
||||
],
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
security: [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityRequirements())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(2)
|
||||
expect(nodes[0].node).toNotBe(nodes[1].node)
|
||||
expect(nodes[0].node).toEqual({})
|
||||
expect(nodes[1].node).toEqual({})
|
||||
})
|
||||
})
|
||||
it("should not pick up the `security` key inside extensions", () => {
|
||||
const spec = {
|
||||
paths: {
|
||||
"/": {
|
||||
"x-ext1": {
|
||||
security: [
|
||||
{ foo: [] }
|
||||
]
|
||||
}
|
||||
},
|
||||
"x-ext2": {
|
||||
get: {
|
||||
security: [
|
||||
{ bar: [] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getSystem(spec)
|
||||
.then(system => system.validateSelectors.allSecurityRequirements())
|
||||
.then(nodes => {
|
||||
expect(nodes.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,94 @@
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - 2and3 operations", () => {
|
||||
describe("Operations must have unique operationIds", () => {
|
||||
describe("OpenAPI 3.0", () => {
|
||||
it("should return an error when operationId collisions exist", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "myId"
|
||||
},
|
||||
post: {
|
||||
operationId: "myId"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Operations must have unique operationIds.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "post", "operationId"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when operationId collisions don't exist", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "myId1"
|
||||
},
|
||||
post: {
|
||||
operationId: "myId2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
describe("Swagger 2.0", () => {
|
||||
it("should return an error when operationId collisions exist", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "myId"
|
||||
},
|
||||
post: {
|
||||
operationId: "myId"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Operations must have unique operationIds.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "post", "operationId"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when operationId collisions don't exist", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "myId1"
|
||||
},
|
||||
post: {
|
||||
operationId: "myId2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,997 @@
|
||||
import expect from "expect"
|
||||
import validateHelper from "../validate-helper.js"
|
||||
|
||||
describe(`validation plugin - semantic - 2and3 parameters`, () => {
|
||||
describe(`parameters must have unique name + in values`, () => {
|
||||
describe(`direct siblings`, () => {
|
||||
it("should return an error for an invalid Swagger 2 definition", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "pathLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "pathLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "opLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "opLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
const secondError = allErrors[1]
|
||||
expect(allErrors.length).toEqual(2)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "parameters", "1"])
|
||||
expect(firstError.message).toEqual("Sibling parameters must have unique name + in values")
|
||||
expect(secondError.path).toEqual(["paths", "/pets", "get", "parameters", "1"])
|
||||
expect(secondError.message).toEqual("Sibling parameters must have unique name + in values")
|
||||
})
|
||||
})
|
||||
it("should return an error for an invalid OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "pathLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pathLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "opLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "opLevel",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
const secondError = allErrors[1]
|
||||
expect(allErrors.length).toEqual(2)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "parameters", "1"])
|
||||
expect(firstError.message).toEqual("Sibling parameters must have unique name + in values")
|
||||
expect(secondError.path).toEqual(["paths", "/pets", "get", "parameters", "1"])
|
||||
expect(secondError.message).toEqual("Sibling parameters must have unique name + in values")
|
||||
})
|
||||
})
|
||||
it("should return no errors for a valid Swagger 2 definition", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "wags",
|
||||
"in": "query",
|
||||
"description": "wags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for a valid OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "wags",
|
||||
"in": "query",
|
||||
"description": "wags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "string"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe(`inherited siblings`, () => {
|
||||
it("should return no errors for a valid Swagger 2 definition due to inheritance", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
parameters: {
|
||||
MyParam: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
in: "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for a valid OpenAPI 3 definition due to inheritance", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
parameters: {
|
||||
MyParam: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
in: "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should not return an error for root parameters in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
parameters: {
|
||||
MyParam: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "otherParam",
|
||||
in: "query"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should not return an error for root parameters in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
parameters: {
|
||||
MyParam: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "otherParam",
|
||||
in: "query"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for a valid Swagger 2 definition", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
parameters: {
|
||||
MyParamOne: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
},
|
||||
MyParamTwo: {
|
||||
name: "anotherParam1",
|
||||
in: "query"
|
||||
},
|
||||
},
|
||||
"paths": {
|
||||
"/pets/{one}/{two}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "anotherParam2",
|
||||
in: "query"
|
||||
},
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "two",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "anotherParam3",
|
||||
in: "query"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors for a valid OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
parameters: {
|
||||
MyParamOne: {
|
||||
name: "one",
|
||||
in: "query"
|
||||
},
|
||||
MyParamTwo: {
|
||||
name: "anotherParam1",
|
||||
in: "query"
|
||||
},
|
||||
},
|
||||
"paths": {
|
||||
"/pets/{one}/{two}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "one",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "anotherParam2",
|
||||
in: "query"
|
||||
},
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "two",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
in: "query"
|
||||
},
|
||||
{
|
||||
name: "anotherParam3",
|
||||
in: "query"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
describe(`parameter defaults must be present in enums`, () => {
|
||||
it("should return an error for an invalid Swagger 2 definition", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
enum: [1, 2, 3],
|
||||
default: 0
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0", "default"])
|
||||
expect(firstError.message).toEqual("Default values must be present in `enum`")
|
||||
})
|
||||
})
|
||||
it("should return an error for an invalid OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
schema: {
|
||||
enum: [1, 2, 3],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0", "schema", "default"])
|
||||
expect(firstError.message).toEqual("Default values must be present in `enum`")
|
||||
})
|
||||
})
|
||||
it("should return an error for an invalid OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
parameters: {
|
||||
MyParam: {
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
schema: {
|
||||
enum: [1, 2, 3],
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.path).toEqual(["components", "parameters", "MyParam", "schema", "default"])
|
||||
expect(firstError.message).toEqual("Default values must be present in `enum`")
|
||||
})
|
||||
})
|
||||
it("should return no errors for a Swagger 2 definition without default set", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
enum: [1, 2, 3]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for an OpenAPI 3 definition without default set", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
schema: {
|
||||
enum: [1, 2, 3]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for a Swagger 2 definition without enum set", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
default: 0
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should return no errors for an OpenAPI 3 definition without enum set", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "num",
|
||||
"in": "query",
|
||||
"type": "number",
|
||||
schema: {
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`path parameters must be in path definition`, () => {
|
||||
it("should return no errors for a valid Swagger 2 definition", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
info: {
|
||||
"title": "Correct path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo/{param1}/{param2}/{param3}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/parameters/param3"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
param3: {
|
||||
name: "param3",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return 2 errors for each path parameter that isn't in the path in spec 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
info: {
|
||||
"title": "Unused path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(2)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "parameters", "0","name"])
|
||||
expect(firstError.message).toEqual(`Path parameter "param1" must have the corresponding {param1} segment in the "/foo" path`)
|
||||
const secondError = allErrors[1]
|
||||
expect(secondError.path).toEqual(["paths", "/foo", "get", "parameters", "0","name"])
|
||||
expect(secondError.message).toEqual(`Path parameter "param2" must have the corresponding {param2} segment in the "/foo" path`)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return 2 errors for each referenced path parameter that isn't in the path for 2 spec", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
info: {
|
||||
"title": "Unused path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo/{param1}/{param2}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/parameters/param3"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/parameters/param4"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
param3: {
|
||||
name: "param3",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
param4: {
|
||||
name: "param4",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(2)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/foo/{param1}/{param2}", "parameters", "1","name"])
|
||||
expect(firstError.message).toEqual(`Path parameter "param3" must have the corresponding {param3} segment in the "/foo/{param1}/{param2}" path`)
|
||||
const secondError = allErrors[1]
|
||||
expect(secondError.path).toEqual(["paths", "/foo/{param1}/{param2}","get", "parameters", "1","name"])
|
||||
expect(secondError.message).toEqual(`Path parameter "param4" must have the corresponding {param4} segment in the "/foo/{param1}/{param2}" path`)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no errors for a valid Swagger 3 definition", () => {
|
||||
const spec = {
|
||||
swagger: "3.0",
|
||||
info: {
|
||||
"title": "Correct path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo/{param1}/{param2}/{param3}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/parameters/param3"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
param3: {
|
||||
name: "param3",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return 2 errors for each path parameter that isn't in the path in spec 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.2",
|
||||
info: {
|
||||
"title": "Unused path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(2)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "parameters", "0","name"])
|
||||
expect(firstError.message).toEqual(`Path parameter "param1" must have the corresponding {param1} segment in the "/foo" path`)
|
||||
const secondError = allErrors[1]
|
||||
expect(secondError.path).toEqual(["paths", "/foo", "get", "parameters", "0","name"])
|
||||
expect(secondError.message).toEqual(`Path parameter "param2" must have the corresponding {param2} segment in the "/foo" path`)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return 2 errors for each referenced path parameter that isn't in the path for 3 spec", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.2",
|
||||
info: {
|
||||
"title": "Unused path parameters in path",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/foo/{param1}/{param2}": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param1",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/components/parameters/param3"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
name: "param2",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
$ref: "#/components/parameters/param4"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
"description": "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
param3: {
|
||||
name: "param3",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
},
|
||||
param4: {
|
||||
name: "param4",
|
||||
in: "path",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(2)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/foo/{param1}/{param2}", "parameters", "1","name"])
|
||||
expect(firstError.message).toEqual(`Path parameter "param3" must have the corresponding {param3} segment in the "/foo/{param1}/{param2}" path`)
|
||||
const secondError = allErrors[1]
|
||||
expect(secondError.path).toEqual(["paths", "/foo/{param1}/{param2}","get", "parameters", "1","name"])
|
||||
expect(secondError.message).toEqual(`Path parameter "param4" must have the corresponding {param4} segment in the "/foo/{param1}/{param2}" path`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,484 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - 2and3 paths", () => {
|
||||
describe("Paths cannot have query strings in them", () => {
|
||||
it("should return one problem for an stray '?' in a Swagger 2 path string", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/report?asdf=123": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Query strings in paths are not allowed.")
|
||||
expect(firstError.path).toEqual(["paths", "/report?asdf=123"])
|
||||
})
|
||||
})
|
||||
it("should return one problem for an stray '?' in an OpenAPI 3 path string", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/report?asdf=123": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Query strings in paths are not allowed.")
|
||||
expect(firstError.path).toEqual(["paths", "/report?asdf=123"])
|
||||
})
|
||||
})
|
||||
it("should return no problems for a correct Swagger 2 path template", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
it("should return no problems for a correct OpenAPI 3 path template", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Path parameter definitions need matching paramater declarations", function(){
|
||||
describe("OpenAPI 3", () => {
|
||||
it("should not return problems for a valid path-level definiton/declaration pair", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return problems for a valid path-level definiton/declaration pair using a $ref", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [
|
||||
{ $ref: "#/parameters/id" }
|
||||
]
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
id: {
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return problems for a valid operation-level definiton/declaration pair", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should return one problem for a path parameter defined at the operation level that is not present within every operation on the path", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
},
|
||||
post: {
|
||||
description: "the path parameter definition is missing here"
|
||||
},
|
||||
delete: {
|
||||
description: "the path parameter definition is missing here"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( `Declared path parameter \"id\" needs to be defined within every operation in the path (missing in "post", "delete"), or moved to the path-level parameters object`)
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return one problem when the definition is completely absent", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should return one error when no parameters are defined", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should return one problem for a missed 'in' value", function(){
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
// in: "path",
|
||||
description: "An id"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Swagger 2", () => {
|
||||
it("should not return problems for a valid path-level definiton/declaration pair", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return problems for a valid path-level definiton/declaration pair using a $ref", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [
|
||||
{ $ref: "#/parameters/id" }
|
||||
]
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
id: {
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return problems for a valid operation-level definiton/declaration pair", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should return one problem for a path parameter defined at the operation level that is not present within every operation on the path", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
description: "An id",
|
||||
required: true
|
||||
}]
|
||||
},
|
||||
post: {
|
||||
description: "the path parameter definition is missing here"
|
||||
},
|
||||
delete: {
|
||||
description: "the path parameter definition is missing here"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( `Declared path parameter \"id\" needs to be defined within every operation in the path (missing in "post", "delete"), or moved to the path-level parameters object`)
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return one problem when the definition is completely absent", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should return one error when no parameters are defined", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
info: {
|
||||
title: "test",
|
||||
version: "1.0.0"
|
||||
},
|
||||
paths: {
|
||||
"/{12345instanceABCDE_instance_12345}": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"12345instanceABCDE_instance_12345\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/{12345instanceABCDE_instance_12345}"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should return a well-formed error when ", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it.skip("should return one problem for a missed 'in' value", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
// in: "path",
|
||||
description: "An id"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a specific error for parameters with different casing characters OpenAPI 3 definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.1",
|
||||
"paths": {
|
||||
"/{myParam}": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "myparam",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual("Parameter names are case-sensitive. The parameter named \"myparam\" does not match the case used in the path \"/{myParam}\".")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no errors for parameters with same characters in path and parameters definition", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.1",
|
||||
"paths": {
|
||||
"/{myParam}": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "myParam",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,698 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - 2and3 refs", function() {
|
||||
this.timeout(10 * 1000)
|
||||
describe("Ref siblings", () => {
|
||||
|
||||
it("should return a warning when another property is a sibling of a $ref in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/components/schemas/abc",
|
||||
description: "My very cool get"
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("Sibling values alongside $refs are ignored.\nTo add properties to a $ref, wrap the $ref into allOf, or move the extra properties into the referenced definition (if applicable).")
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "description"])
|
||||
})
|
||||
})
|
||||
it("should return a warning when another property is a sibling of a $ref in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/definitions/abc",
|
||||
description: "My very cool get"
|
||||
}
|
||||
}
|
||||
},
|
||||
definitions: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("Sibling values alongside $refs are ignored.\nTo add properties to a $ref, wrap the $ref into allOf, or move the extra properties into the referenced definition (if applicable).")
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "description"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no warnings when a $ref has no siblings in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/components/schemas/abc"
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no warnings when a $ref has no siblings in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/definitions/abc"
|
||||
}
|
||||
}
|
||||
},
|
||||
definitions: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no warnings when a path item $ref has siblings in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
$ref: "#/components/schemas/abc",
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/components/schemas/abc"
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no warnings when a path item $ref has siblings in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
$ref: "#/definitions/abc",
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
$ref: "#/definitions/abc"
|
||||
}
|
||||
}
|
||||
},
|
||||
definitions: {
|
||||
abc: {}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
})
|
||||
describe("Unused definitions", () => {
|
||||
it("should return a warning when a definition is declared but not used in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
"x-Foo": {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("Definition was declared but never used in document")
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.path).toEqual(["components", "schemas", "x-Foo"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return a warning when a definition with special characters is properly referenced in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/x~1Foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/x~0Bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
"x/Foo": {
|
||||
type: "object"
|
||||
},
|
||||
"x~Bar": {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
// We want warnings only, without errors about invalid component names
|
||||
const allWarnings = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.level === "warning")
|
||||
expect(allWarnings.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a warning when a definition is declared but not used in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {}
|
||||
},
|
||||
definitions: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("Definition was declared but never used in document")
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.path).toEqual(["definitions", "abc"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return a warning when a definition with special character is declared and used in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
responses: {
|
||||
200: {
|
||||
schema: {
|
||||
$ref: "#/definitions/x~1Foo"
|
||||
}
|
||||
},
|
||||
400: {
|
||||
schema: {
|
||||
$ref: "#/definitions/x~0Bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
definitions: {
|
||||
"x/Foo": {
|
||||
type: "object"
|
||||
},
|
||||
"x~Bar": {
|
||||
type: "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
describe("Malformed $ref values", () => {
|
||||
it("should return an error when a JSON pointer lacks a leading `#/` in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#myObj/abc"
|
||||
}
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("$ref paths must begin with `#/`")
|
||||
expect(firstError.level).toEqual("error")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should return an error when a JSON pointer lacks a leading `#/` in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#myObj/abc"
|
||||
}
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("$ref paths must begin with `#/`")
|
||||
expect(firstError.level).toEqual("error")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer is a well-formed remote reference in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "http://google.com#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string",
|
||||
properties: {
|
||||
$ref: "http://google.com/MyRegularURLReference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer is a well-formed remote reference in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "http://google.com#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string",
|
||||
properties: {
|
||||
$ref: "http://google.com/MyRegularURLReference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return an error when a JSON pointer uses incorrect percent-encoding in Swagger 2", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/foo bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"foo bar": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors[0]).toInclude({
|
||||
level: "warning",
|
||||
message: "Definition was declared but never used in document",
|
||||
path: ["definitions", "foo bar"]
|
||||
})
|
||||
expect(allSemanticErrors.length).toEqual(2)
|
||||
expect(allSemanticErrors[1]).toInclude({
|
||||
level: "error",
|
||||
message: "$ref values must be RFC3986-compliant percent-encoded URIs",
|
||||
path: ["paths", "/foo", "get", "responses", "200", "schema", "$ref"]
|
||||
})
|
||||
})
|
||||
})
|
||||
it("should return an error when a JSON pointer uses incorrect percent-encoding in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/foo bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
"foo bar": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors.length).toEqual(3)
|
||||
expect(allSemanticErrors[0]).toInclude({
|
||||
level: "warning",
|
||||
message: "Definition was declared but never used in document",
|
||||
path: ["components", "schemas", "foo bar"]
|
||||
})
|
||||
expect(allSemanticErrors[1]).toInclude({
|
||||
level: "error",
|
||||
message: "$ref values must be RFC3986-compliant percent-encoded URIs",
|
||||
path: ["paths", "/foo", "get", "responses", "200", "content", "application/json", "schema", "$ref"]
|
||||
}),
|
||||
expect(allSemanticErrors[2]).toInclude({
|
||||
level: "error",
|
||||
message: "Component names can only contain the characters A-Z a-z 0-9 - . _",
|
||||
path: ["components", "schemas", "foo bar"]
|
||||
})
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer uses correct percent-encoding in Swagger 2", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/foo%20bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"foo bar": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer uses correct percent-encoding in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/foo": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/foo%20bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
"foo bar": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors.length).toEqual(1)
|
||||
expect(allSemanticErrors[0]).toInclude({
|
||||
level: "error",
|
||||
message: "Component names can only contain the characters A-Z a-z 0-9 - . _",
|
||||
path: ["components", "schemas", "foo bar"]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
describe("Nonexistent $ref pointers", () => {
|
||||
it("should return an error when a local JSON pointer does not exist in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#/myObj/DoesNotExist"
|
||||
}
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string",
|
||||
properties: {
|
||||
$ref: "http://google.com/MyRegularURLReference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
|
||||
expect(firstError.level).toEqual("error")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should return an error when a local JSON pointer does not exist in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#/myObj/DoesNotExist"
|
||||
}
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string",
|
||||
properties: {
|
||||
$ref: "http://google.com/MyRegularURLReference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
.filter((el, i, arr) => arr.indexOf(el) === i)
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
|
||||
expect(firstError.level).toEqual("error")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer exists in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer exists in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string",
|
||||
properties: {
|
||||
$ref: "http://google.com/MyRegularURLReference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer is a remote reference in Swagger 2", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "http://google.com#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a JSON pointer is a remote reference in OpenAPI 3", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
$ref: "http://google.com#/myObj/abc"
|
||||
},
|
||||
},
|
||||
myObj: {
|
||||
abc: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.source !== "resolver")
|
||||
expect(allSemanticErrors).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - 2and3 security", () => {
|
||||
it("should return an error when top-level security references a non-existing security scheme", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
security: [
|
||||
{
|
||||
fictional_security_definition: [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["security", "0"])
|
||||
expect(firstError.message).toMatch("Security requirements must match a security definition")
|
||||
})
|
||||
})
|
||||
|
||||
it("should return an error when an operation references a non-existing security scheme", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
security: [
|
||||
{
|
||||
fictional_security_definition: [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "security", "0"])
|
||||
expect(firstError.message).toMatch("Security requirements must match a security definition")
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a warning when a security scheme is defined but not used in OpenAPI 2.0", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
securityDefinitions: {
|
||||
auth: {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.message).toMatch("Security scheme was defined but never used.")
|
||||
expect(firstError.path).toEqual(["securityDefinitions", "auth"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a warning when a security scheme is defined but not used in OpenAPI 3.0", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
securitySchemes: {
|
||||
auth: {
|
||||
type: "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.level).toEqual("warning")
|
||||
expect(firstError.message).toMatch("Security scheme was defined but never used.")
|
||||
expect(firstError.path).toEqual(["components", "securitySchemes", "auth"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no errors when a security scheme is defined and referenced globally in OpenAPI 2.0", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
security: [
|
||||
{ auth: [] }
|
||||
],
|
||||
securityDefinitions: {
|
||||
auth: {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors when a security scheme is defined and used in an operation in OpenAPI 2.0", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
security: [
|
||||
{ auth: [] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
securityDefinitions: {
|
||||
auth: {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors when a security scheme is defined and referenced globally in OpenAPI 3.0", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
security: [
|
||||
{ auth: [] }
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
auth: {
|
||||
type: "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors when a security scheme is defined and used in an operation in OpenAPI 3.0", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
security: [
|
||||
{ auth: [] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
securitySchemes: {
|
||||
auth: {
|
||||
type: "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errrors when `security` contains multiple requirements combined using logical OR", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
security: [
|
||||
{},
|
||||
{ auth: [] }
|
||||
],
|
||||
securityDefinitions: {
|
||||
auth: {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors when security schemes are combined using logical AND", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
security: [
|
||||
{
|
||||
auth1: [],
|
||||
auth2: []
|
||||
}
|
||||
],
|
||||
securityDefinitions: {
|
||||
auth1: {
|
||||
type: "apiKey"
|
||||
},
|
||||
auth2: {
|
||||
type: "apiKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - 2and3 tags", () => {
|
||||
describe("global `tags` array", () => {
|
||||
describe("OpenAPI 3.0", () => {
|
||||
it("should return an error when two tag objects are equivalent", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return validateHelper(spec).then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual("Tag Objects must have unique `name` field values.")
|
||||
expect(firstError.path).toEqual(["tags", "1"])
|
||||
})
|
||||
})
|
||||
it("should return an error when two tag objects have the same name", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "pet",
|
||||
description: "Everything about your pets",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return validateHelper(spec).then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual("Tag Objects must have unique `name` field values.")
|
||||
expect(firstError.path).toEqual(["tags", "1"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when two tags have unique names", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "store",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
describe("Swagger 2.0", () => {
|
||||
it("should return an error when two tag objects are equivalent", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return validateHelper(spec).then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual("Tag Objects must have unique `name` field values.")
|
||||
expect(firstError.path).toEqual(["tags", "1"])
|
||||
})
|
||||
})
|
||||
it("should return an error when two tag objects have the same name", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "pet",
|
||||
description: "Everything about your pets",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return validateHelper(spec).then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual("Tag Objects must have unique `name` field values.")
|
||||
expect(firstError.path).toEqual(["tags", "1"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when two tags have unique names", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
tags: [
|
||||
{
|
||||
name: "pet",
|
||||
},
|
||||
{
|
||||
name: "store",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("editor bug #1817 - path parameter semantic error with TRACE", function() {
|
||||
it("should return no problems for a path parameter defined in a Swagger 2 TRACE operation", function () {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
trace: {
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
it("should return no problems for a path parameter defined in an OpenAPI 3 TRACE operation", function () {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
trace: {
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,532 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrors } from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - form data", function(){
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
describe("/parameters/...", function(){
|
||||
describe("typo in formdata", function(){
|
||||
it("should warn about formdata ( typo )", function(){
|
||||
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
parameters: {
|
||||
CoolParam: [
|
||||
{ in: "formdata" },
|
||||
]
|
||||
},
|
||||
paths: {
|
||||
"/some": {
|
||||
post: {
|
||||
parameters: [
|
||||
{ in: "formdata" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Parameter "in: formdata" is invalid, did you mean "in: formData"?`)
|
||||
expect(firstError.path).toEqual(["paths", "/some", "post", "parameters", "0"])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("missing consumes", function(){
|
||||
it("should complain if 'type:file` and no 'in: formData", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/some": {
|
||||
post: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{
|
||||
type: "file",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Parameters with "type: file" must have "in: formData"`)
|
||||
expect(firstError.path).toEqual(["paths", "/some", "post", "parameters", "0"])
|
||||
})
|
||||
})
|
||||
it("should complain if 'type:file` and no consumes - 'multipart/form-data'", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/some": {
|
||||
post: {
|
||||
parameters: [
|
||||
{
|
||||
in: "formData",
|
||||
type: "file",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "type: file" must include "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/some", "post"])
|
||||
})
|
||||
})
|
||||
it("should complain if 'in:formData` and no consumes - 'multipart/form-data' or 'application/x-www-form-urlencoded'", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/some": {
|
||||
post: {
|
||||
parameters: [
|
||||
{
|
||||
in: "formData",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/some", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not complain if 'in:formData` and consumes is set globally", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
consumes: [
|
||||
"multipart/form-data"
|
||||
],
|
||||
paths: {
|
||||
"/some": {
|
||||
post: {
|
||||
parameters: [
|
||||
{
|
||||
in: "formData",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("/pathitems/...", function(){
|
||||
it("should complain about having both in the same parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "formData" },
|
||||
{ in: "body" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Parameters cannot have both a "in: body" and "in: formData", as "formData" _will_ be the body`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "parameters"])
|
||||
})
|
||||
})
|
||||
it("should complain if 'type:file` and no 'in: formData", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ type: "file" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Parameters with "type: file" must have "in: formData"`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "parameters", "0"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("Path-level form parameters and operation-level consumes", function(){
|
||||
describe("`in: formData` + `type: file`", function() {
|
||||
it("should report an error for missing consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "file"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "type: file" must include "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should report an error for incorrect global consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
consumes: ["application/json"],
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "file"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "type: file" must include "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should report an error for incorrect operation-level consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "file"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
consumes: ["application/json"],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "type: file" must include "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not report an error for correct global consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
consumes: ["multipart/form-data"],
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "file"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not report an error for correct operation consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "file"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
consumes: ["multipart/form-data"],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe("`in: formData`", function() {
|
||||
it("should report an error for missing consumes with a path-level formData parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should report an error for incorrect global consumes with a path-level formData parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
consumes: ["application/json"],
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should report an error for incorrect operation-level consumes with a path-level formData parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
consumes: ["application/json"],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Operations with parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not report an error for correct global consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
consumes: ["multipart/form-data"],
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not report an error for correct operation consumes with a path-level parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
name: "param",
|
||||
in: "formData",
|
||||
required: true,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
post: {
|
||||
consumes: ["multipart/form-data"],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "ok"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,228 @@
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - oas3 components", () => {
|
||||
describe("OAS3 component names must consist of allowed characters", () => {
|
||||
it("should return an error when OAS3 component names contain forbidden characters", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
"Foo«Bar»": {}
|
||||
},
|
||||
parameters: {
|
||||
"Foo«Bar»": {
|
||||
in: "query",
|
||||
name: "foo",
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
"Foo«Bar»": {
|
||||
description: "ok"
|
||||
}
|
||||
},
|
||||
examples: {
|
||||
"Foo«Bar»": {
|
||||
value: 1
|
||||
}
|
||||
},
|
||||
requestBodies: {
|
||||
"Foo«Bar»": {
|
||||
content: {
|
||||
"text/plain": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
"Foo«Bar»": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
securitySchemes: {
|
||||
"Foo«Bar»": {
|
||||
type: "http",
|
||||
scheme: "basic"
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
"Foo«Bar»": {
|
||||
"{$request.body#/callbackUrl}": {}
|
||||
}
|
||||
},
|
||||
links: {
|
||||
"Foo«Bar»": {
|
||||
operationId: "getUser"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
// We want errors only, without "unused definition" warnings
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
.filter(err => err.level === "error")
|
||||
|
||||
const errorMessage = "Component names can only contain the characters A-Z a-z 0-9 - . _"
|
||||
|
||||
expect(allErrors.length).toEqual(9)
|
||||
expect(allErrors[0]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "schemas", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[1]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "parameters", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[2]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "responses", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[3]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "examples", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[4]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "requestBodies", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[5]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "headers", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[6]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "securitySchemes", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[7]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "callbacks", "Foo«Bar»"]
|
||||
})
|
||||
expect(allErrors[8]).toInclude({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
path: ["components", "links", "Foo«Bar»"]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return errors when OAS3 component names consist of allowed characters", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
schemas: {
|
||||
"A-a.0_": {}
|
||||
},
|
||||
parameters: {
|
||||
"B-b.1_": {
|
||||
in: "query",
|
||||
name: "foo",
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
"C-c.3_": {
|
||||
description: "ok"
|
||||
}
|
||||
},
|
||||
examples: {
|
||||
"D-d.4_": {
|
||||
value: 1
|
||||
}
|
||||
},
|
||||
requestBodies: {
|
||||
"E-e.5_": {
|
||||
content: {
|
||||
"test/plain": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
"F-f.6_": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
securitySchemes: {
|
||||
"G-g.7_": {
|
||||
type: "http",
|
||||
scheme: "basic"
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
"H-h.8_": {
|
||||
"{$request.body#/callbackUrl}": {}
|
||||
}
|
||||
},
|
||||
links: {
|
||||
"I-i.9_": {
|
||||
operationId: "getUser"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return errors when an x- extension key contains special characters", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
"x-foo«bar»": {
|
||||
"key with spaces": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should not return errors when OAS2 component names contain special characters", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
definitions: {
|
||||
"Foo«Bar»": {
|
||||
type: "object"
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
"Foo«Bar»": {
|
||||
in: "query",
|
||||
name: "foo",
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
"Foo«Bar»": {
|
||||
description: "ok"
|
||||
}
|
||||
},
|
||||
securityDefinitions: {
|
||||
"Foo«Bar»": {
|
||||
type: "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,91 @@
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrors } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - oas3 operations", () => {
|
||||
describe("GET and DELETE operations may not have a requestBody", () => {
|
||||
it("should return an error when a requestBody exists in a GET operation", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`GET operations cannot have a requestBody.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "requestBody"])
|
||||
})
|
||||
})
|
||||
it("should return an error when a requestBody exists in a DELETE operation", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
delete: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`DELETE operations cannot have a requestBody.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "delete", "requestBody"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when other methods contain a requestBody", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,165 @@
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - oas3 parameters", () => {
|
||||
describe("Header parameters should not be named Authorization, Content-Type, or Accept", () => {
|
||||
it("should return a warning when a header parameter named Authorization is defined in an operation", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "header",
|
||||
name: "authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toMatch(`Header parameters named "Authorization" are ignored.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "parameters", "0", "name"])
|
||||
})
|
||||
})
|
||||
it("should return a warning when a header parameter named Content-Type is defined in an operation", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "header",
|
||||
name: "content-type"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toMatch(`Header parameters named "Content-Type" are ignored.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "parameters", "0", "name"])
|
||||
})
|
||||
})
|
||||
it("should return a warning when a header parameter named Accept is defined in an operation", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "header",
|
||||
name: "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toMatch(`Header parameters named "Accept" are ignored.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "parameters", "0", "name"])
|
||||
})
|
||||
})
|
||||
it("should return a warning when a header parameter named Authorization is defined in components", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
components: {
|
||||
parameters: {
|
||||
auth: {
|
||||
in: "header",
|
||||
name: "authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toMatch(`Header parameters named "Authorization" are ignored.`)
|
||||
expect(firstError.path).toEqual(["components", "parameters", "auth", "name"])
|
||||
})
|
||||
})
|
||||
it("should return no warnings when a non-header parameter is named Authorization", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "query",
|
||||
name: "authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no warnings when a header parameter name contains 'Authorization' as a substring", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "header",
|
||||
name: "X-Authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no warnings when a header parameter is named 'Authorization' in OpenAPI 2.0", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
in: "header",
|
||||
name: "authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,706 @@
|
||||
import expect from "expect"
|
||||
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "../validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - oas3 refs", () => {
|
||||
describe("$refs for request bodies must reference a request body by position", () => {
|
||||
it("should return an error when a requestBody incorrectly references a local component schema", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "#/components/schemas/MySchema"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
MySchema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`requestBody $refs cannot point to '#/components/schemas/…', they must point to '#/components/requestBodies/…'`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "post", "requestBody", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when a requestBody references a remote component schema", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "http://google.com/#/components/schemas/MySchema"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return an error when a requestBody in a callback incorrectly references a local component schema", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
info: null,
|
||||
version: "1.0.0-oas3",
|
||||
title: "example",
|
||||
paths: {
|
||||
"/api/callbacks": {
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
callback: {
|
||||
"/callback": {
|
||||
post: {
|
||||
requestBody: {
|
||||
$ref: "#/components/schemas/callbackRequest"
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
callbackRequest: {
|
||||
type: "object",
|
||||
properties: {
|
||||
property1: {
|
||||
type: "integer",
|
||||
example: 10000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`requestBody $refs cannot point to '#/components/schemas/…', they must point to '#/components/requestBodies/…'`)
|
||||
expect(firstError.path).toEqual(["paths", "/api/callbacks", "post", "callbacks",
|
||||
"callback", "/callback", "post", "requestBody", "$ref"])
|
||||
})
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references a local component request body", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "#/components/requestBodies/MyBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
MyBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references a local operation request body", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "#/paths/~1/put/requestBody"
|
||||
}
|
||||
},
|
||||
put: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references a remote component request body", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "http://google.com/#/components/requestBodies/MyBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
MyBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references a remote https component request body", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "https://example.com/file.yaml#/components/requestBodies/group1/addPetBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
MyBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references an external yaml file", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "addPetBody.yaml"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
MyBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
it("should return no errors when a requestBody correctly references an external yaml pointing some node", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/": {
|
||||
post: {
|
||||
operationId: "myId",
|
||||
requestBody: {
|
||||
$ref: "./components.yaml#/path/to/some/node"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
MyBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
|
||||
describe("$refs for requestbody schemas must reference a schema by position", () => {
|
||||
it("should return an error when a requestBody schema incorrectly references a local component requestBody", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
post: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/requestBodies/Foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
requestBodies: {
|
||||
Foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const allSemanticErrors = allErrors.filter(err => err.source === "semantic")
|
||||
|
||||
expect(allSemanticErrors.length).toEqual(1)
|
||||
|
||||
const firstError = allSemanticErrors[0]
|
||||
|
||||
expect(firstError.message).toEqual(`requestBody schema $refs must point to a position where a Schema Object can be legally placed`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "post", "requestBody", "content", "application/json", "schema", "$ref"])
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a requestBody schema references a local component schema", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
post: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
Foo: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a requestBody schema references remote document paths", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
post: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "http://google.com#/Foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a requestBody schema references entire remote documents", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
post: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "addPetBody.yaml"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a requestBody schema references local operation requestBody schemas", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
post: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
},
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
put: {
|
||||
requestBody: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
},
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/paths/~1foo/post/requestBody/content/application~1json/schema"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("response header $refs should not point to parameters", () => {
|
||||
it("should return an error when a response header incorrectly references a local parameter component", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
headers: {
|
||||
"X-MyHeader": {
|
||||
$ref: "#/components/parameters/MyHeader"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
headers: {
|
||||
MyHeader: {
|
||||
$ref: "#/components/parameters/MyHeader"
|
||||
}
|
||||
},
|
||||
parameters: {
|
||||
MyHeader: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`OAS3 header $refs should point to #/components/headers/... and not #/components/parameters/...`)
|
||||
expect(firstError.path).toEqual(["paths", "/foo", "get","responses","200", "headers", "X-MyHeader", "$ref"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no errors when a response header correctly references a local header component", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
headers: {
|
||||
"X-MyHeader": {
|
||||
$ref: "#/components/headers/MyHeader"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
headers: {
|
||||
MyHeader: {
|
||||
$ref: "#/components/headers/MyHeader"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors for external $refs in response headers", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
headers: {
|
||||
"X-MyHeader": {
|
||||
$ref: "https://www.google.com/#/components/parameter/MyHeader"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
headers: {
|
||||
MyHeader: {
|
||||
$ref: "#/components/headers/MyHeader"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
|
||||
describe("parameter $refs should not point to header components", () => {
|
||||
it("should return an error when a parameter incorrectly references a response header component", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "#/components/headers/foo"
|
||||
}
|
||||
],
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "#/components/headers/foo"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
myParam: {
|
||||
$ref: "#/components/headers/foo"
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
foo: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(3)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`OAS3 parameter $refs should point to #/components/parameters/... and not #/components/headers/...`)
|
||||
expect(firstError.path).toEqual(["paths","/foo","parameters", "0", "$ref"])
|
||||
const secondError = allErrors[1]
|
||||
expect(secondError.message).toEqual(`OAS3 parameter $refs should point to #/components/parameters/... and not #/components/headers/...`)
|
||||
expect(secondError.path).toEqual(["paths","/foo","get","parameters", "0", "$ref"])
|
||||
const thirdError = allErrors[2]
|
||||
expect(thirdError.message).toEqual(`OAS3 parameter $refs should point to #/components/parameters/... and not #/components/headers/...`)
|
||||
expect(thirdError.path).toEqual(["components","parameters", "myParam", "$ref"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no errors when a parameter correctly references a parameter component", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "#/components/parameters/foo"
|
||||
}
|
||||
],
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "#/components/parameters/foo"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
foo: {
|
||||
$ref: "#/components/parameters/foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return no errors for external parameter $refs", () => {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
paths: {
|
||||
"/foo": {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "http://www.google.com/#/components/parameters/foo"
|
||||
}
|
||||
],
|
||||
get: {
|
||||
parameters: [
|
||||
{
|
||||
$ref: "http://www.google.com/#/components/parameters/foo"
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
foo: {
|
||||
$ref: "http://www.google.com/#/components/parameters/foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,142 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrors } from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - parameters", function() {
|
||||
this.timeout(10 * 1000 ) // For swagger-ui startup
|
||||
|
||||
it("should return an error when an array type parameter omits an `items` property", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tags",
|
||||
"in": "query",
|
||||
"description": "tags to filter by",
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0"])
|
||||
expect(firstError.message).toMatch(/.*type.*array.*require.*items/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Operations cannot have both a 'body' parameter and a 'formData' parameter", () => {
|
||||
it("should complain about having both in the same operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "formData" },
|
||||
{ in: "body" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Parameters cannot have both a "in: body" and "in: formData", as "formData" _will_ be the body`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "parameters"])
|
||||
})
|
||||
})
|
||||
it("should not complain about having only a body parameter in the same operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "body" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
it("should not complain about having only a formData parameter in the same operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "formData" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Operations must have only one body parameter", () => {
|
||||
it("should complain about having two body parameters in the same operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "body" },
|
||||
{ in: "body" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual(`Multiple body parameters are not allowed.`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "parameters"])
|
||||
})
|
||||
})
|
||||
it("should not complain about having one body parameter in the same operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
consumes: ["multipart/form-data"],
|
||||
parameters: [
|
||||
{ in: "body" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,275 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrors } from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - paths", function(){
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
describe("Empty path templates are not allowed", () => {
|
||||
|
||||
it("should return one problem for an empty path template", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{}": {}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.message).toEqual( "Empty path parameter declarations are not valid")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{}"])
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Path parameters declared in the path string need matching definitions", () => {
|
||||
|
||||
it("should return one problem for an undefined declared path parameter", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual( "Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return one problem for an path parameter defined in another path", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {},
|
||||
"/UncoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Declared path parameter \"id\" needs to be defined as a path parameter at either the path or operation level")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{id}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no problems for a path parameter defined in the path", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should return no problems for a path parameter defined in an operation", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Path strings must be equivalently different", () => {
|
||||
|
||||
it("should return one problem for an equivalent templated path strings", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
},
|
||||
"/CoolPath/{count}": {
|
||||
parameters: [{
|
||||
name: "count",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Equivalent paths are not allowed.")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath/{count}"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should return no problems for a templated and untemplated pair of path strings", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/": {},
|
||||
"/CoolPath/{count}": {
|
||||
parameters: [{
|
||||
name: "count",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should return no problems for a templated and double-templated set of path strings", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{group_id1}/all": {
|
||||
parameters: [{
|
||||
name: "group_id1",
|
||||
in: "path",
|
||||
required: true
|
||||
}]
|
||||
},
|
||||
"/CoolPath/{group_id2}/{user_id2}": {
|
||||
parameters: [
|
||||
{
|
||||
name: "group_id2",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "user_id2",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Paths must have unique name + in parameters", () => {
|
||||
it("should return no problems for a name collision only", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "id",
|
||||
in: "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
it("should return no problems when 'in' is not defined", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath/{id}": {
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "id",
|
||||
// in: "path"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("Integrations", () => {
|
||||
it.skip("should return two problems for an equivalent path string missing a parameter definition", function(){
|
||||
// const spec = {
|
||||
// paths: {
|
||||
// "/CoolPath/{id}": {
|
||||
// parameters: [{
|
||||
// name: "id",
|
||||
// in: "path"
|
||||
// }]
|
||||
// },
|
||||
// "/CoolPath/{count}": {}
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let res = validate({ resolvedSpec: spec })
|
||||
// expect(res.errors).toEqual([
|
||||
// {
|
||||
// message: "Equivalent paths are not allowed.",
|
||||
// path: "paths./CoolPath/{count}"
|
||||
// },
|
||||
// {
|
||||
// message: "Declared path parameter \"count\" needs to be defined as a path parameter at either the path or operation level",
|
||||
// path: "paths./CoolPath/{count}"
|
||||
// }
|
||||
// ])
|
||||
// expect(res.warnings).toEqual([])
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,227 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - refs", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
describe.skip("Refs are restricted in specific areas of a spec", () => {
|
||||
describe("Response $refs", () => {
|
||||
it("should return a problem for a parameters $ref in a response position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
responses: {
|
||||
"200": {
|
||||
$ref: "#/parameters/abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "responses", "200", "$ref"])
|
||||
// expect(firstError.message).toMatch("")
|
||||
})
|
||||
})
|
||||
|
||||
// FIXME: This poses a problem for our newer validation PR, as it only iterates over the resolved spec.
|
||||
// We can look for $$refs, but that may be too fragile.
|
||||
// PS: We have a flag in mapSpec, that adds $$refs known as metaPatches
|
||||
it("should return a problem for a definitions $ref in a response position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
schema: {
|
||||
$ref: "#/responses/abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
const firstError = allErrors[0]
|
||||
expect(allErrors.length).toEqual(1)
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "responses", "200", "$ref"])
|
||||
expect(firstError.message).toEqual(`Response references are not allowed within schemas`)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return a problem for a responses $ref in a response position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
responses: {
|
||||
"200": {
|
||||
$ref: "#/responses/abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
describe("Schema $refs", () => {
|
||||
// See note on resolved vs raw spec
|
||||
it("should return a problem for a parameters $ref in a schema position", function(){
|
||||
// const spec = {
|
||||
// paths: {
|
||||
// "/CoolPath": {
|
||||
// schema: {
|
||||
// $ref: "#/parameters/abc"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// let res = validate({ jsSpec: spec })
|
||||
// expect(res.errors.length).toEqual(1)
|
||||
// expect(res.errors[0].path).toEqual(["paths", "/CoolPath", "schema", "$ref"])
|
||||
// expect(res.warnings.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("should return a problem for a responses $ref in a schema position", function(){
|
||||
// const spec = {
|
||||
// paths: {
|
||||
// "/CoolPath": {
|
||||
// schema: {
|
||||
// $ref: "#/responses/abc"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let res = validate({ jsSpec: spec })
|
||||
// expect(res.errors.length).toEqual(1)
|
||||
// expect(res.errors[0].path).toEqual(["paths", "/CoolPath", "schema", "$ref"])
|
||||
// expect(res.warnings.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("should not return a problem for a definition $ref in a schema position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
schema: {
|
||||
$ref: "#/definition/abc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return a problem for a schema property named 'properties'", function(){
|
||||
// #492 regression
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"definitions": {
|
||||
"ServicePlan": {
|
||||
"description": "New Plan to be added to a service.",
|
||||
"properties": {
|
||||
"plan_id": {
|
||||
"type": "string",
|
||||
"description": "ID of the new plan from the catalog."
|
||||
},
|
||||
"parameters": {
|
||||
"$ref": "#/definitions/Parameter"
|
||||
},
|
||||
"previous_values": {
|
||||
"$ref": "#/definitions/PreviousValues"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
let allErrors = system.errSelectors.allErrors().toJS()
|
||||
allErrors = allErrors.filter(a => a.level != "warning")
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe("Parameter $refs", () => {
|
||||
|
||||
it("should return a problem for a definition $ref in a parameter position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
parameters: [{
|
||||
$ref: "#/definitions/abc"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "parameters", "0", "$ref"])
|
||||
expect(firstError.message).toMatch("")
|
||||
})
|
||||
})
|
||||
|
||||
it("should return a problem for a responses $ref in a parameter position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
parameters: [{
|
||||
$ref: "#/responses/abc"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "parameters", "0", "$ref"])
|
||||
expect(firstError.message).toMatch("")
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return a problem for a parameter $ref in a parameter position", function(){
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
parameters: [{
|
||||
$ref: "#/parameters/abc"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,544 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, {
|
||||
expectNoErrors,
|
||||
expectNoErrorsOrWarnings
|
||||
} from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - schema", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
it("should return an error when a definition's property is readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
blah: {
|
||||
$ref: "#/definitions/CoolModel"
|
||||
},
|
||||
definitions: {
|
||||
CoolModel: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
readOnly: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
|
||||
expect(firstError.path).toEqual(["definitions", "CoolModel", "required", "0"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should not return an error when a definition's property is not readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
definitions: {
|
||||
CoolModel: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors
|
||||
.allErrors()
|
||||
.filter(a => a.get("level") === "error") // We have an incidental "warning"
|
||||
.toJS()
|
||||
expect(allErrors).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should return an error when a response schema's property is readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
responses: {
|
||||
200: {
|
||||
schema: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
readOnly: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors
|
||||
.allErrors()
|
||||
.toJS()
|
||||
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
|
||||
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "responses", "200", "schema", "required", "0"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a response schema's property is not readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
responses: {
|
||||
200: {
|
||||
schema: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should return an error when a parameter schema's property is readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "BadParameter",
|
||||
in: "body",
|
||||
schema: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
readOnly: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors
|
||||
.allErrors()
|
||||
.toJS()
|
||||
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "parameters", "0", "schema", "required", "0"])
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when a parameter schema's property is not readOnly and required by the schema", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
parameters: [{
|
||||
name: "BadParameter",
|
||||
in: "body",
|
||||
schema: {
|
||||
required: ["BadProperty"],
|
||||
properties: {
|
||||
BadProperty: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
describe("Type key", () => {
|
||||
it("should return an error when \"type\" is an array", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/CoolPath": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
schema: {
|
||||
type: ["number", "string"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors
|
||||
.allErrors()
|
||||
.toJS()
|
||||
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.message).toEqual(`Schema "type" key must be a string`)
|
||||
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "responses", "200", "schema", "type"])
|
||||
})
|
||||
})
|
||||
it("should not return an error when \"type\" is a property name", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"definitions": {
|
||||
"ApiResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
let allErrors = system.errSelectors.allErrors().toJS()
|
||||
allErrors = allErrors.filter(a => a.level != "warning") // ignore warnings
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should not return an error when \"type\" is a property name inside additionalProperties", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"definitions": {
|
||||
"ApiResponse": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
let allErrors = system.errSelectors.allErrors().toJS()
|
||||
allErrors = allErrors.filter(a => a.level != "warning") // ignore warnings
|
||||
expect(allErrors.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
it("should not return an error when \"type\" is a model name", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"definitions": {
|
||||
"type": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrors(spec)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`"type: array" requires "items"`, () => {
|
||||
describe("header objects", function() {
|
||||
// It takes a while to start up swagger-ui, for some reason
|
||||
|
||||
it("should return an error when an array header object omits an `items` property", () => {
|
||||
|
||||
// Given
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "Swagger Petstore"
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"description": "Returns all pets from the system that the user has access to",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "pet response",
|
||||
"headers": {
|
||||
"X-MyHeader": {
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
|
||||
// Then
|
||||
expect(system.errSelectors.allErrors().count()).toEqual(1)
|
||||
const firstError = system.errSelectors.allErrors().first().toJS()
|
||||
expect(firstError.message).toMatch(/.*type.*array.*require.*items/)
|
||||
expect(firstError.path).toEqual(["paths", "/pets", "get", "responses", "200", "headers", "X-MyHeader"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should not return an error when an array header object has an `items` property", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "Swagger Petstore"
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"description": "Returns all pets from the system that the user has access to",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "pet response",
|
||||
"headers": {
|
||||
"X-MyHeader": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
describe("definitions", function() {
|
||||
// It takes a while to start up swagger-ui, for some reason
|
||||
|
||||
it("should return an error when an array definition omits an `items` property", () => {
|
||||
|
||||
// Given
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
$ref: "#/definitions/asdf"
|
||||
},
|
||||
"definitions": {
|
||||
"asdf": {
|
||||
type: "array"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
|
||||
// Then
|
||||
expect(system.errSelectors.allErrors().count()).toEqual(1)
|
||||
const firstError = system.errSelectors.allErrors().first().toJS()
|
||||
expect(firstError.message).toMatch(/.*type.*array.*require.*items/)
|
||||
expect(firstError.path).toEqual(["definitions", "asdf"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should not return an error when an array definition has an `items` property", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "Swagger Petstore"
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"get": {
|
||||
"description": "Returns all pets from the system that the user has access to",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "pet response",
|
||||
"headers": {
|
||||
"X-MyHeader": {
|
||||
$ref: "#/definitions/array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"array": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
describe("'pattern' Z anchors", function() {
|
||||
it("should return an error when a schema has a Z anchor in its pattern", () => {
|
||||
|
||||
// Given
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
$ref: "#/definitions/asdf"
|
||||
},
|
||||
"definitions": {
|
||||
"asdf": {
|
||||
type: "string",
|
||||
pattern: "^[-a-zA-Z0-9_]+\\Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
|
||||
// Then
|
||||
expect(system.errSelectors.allErrors().count()).toEqual(1)
|
||||
const firstError = system.errSelectors.allErrors().first().toJS()
|
||||
expect(firstError.message).toEqual(`"\\Z" anchors are not allowed in regular expression patterns`)
|
||||
expect(firstError.path).toEqual(["definitions", "asdf", "pattern"])
|
||||
})
|
||||
|
||||
})
|
||||
it("should return an error when a subschema has a Z anchor in its pattern", () => {
|
||||
|
||||
// Given
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
"/": {
|
||||
get: {
|
||||
responses: {
|
||||
"200": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
slug: {
|
||||
type: "string",
|
||||
pattern: "^[-a-zA-Z0-9_]+\\Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// When
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
// Then
|
||||
expect(system.errSelectors.allErrors().count()).toEqual(1)
|
||||
const firstError = system.errSelectors.allErrors().first().toJS()
|
||||
expect(firstError.message).toEqual(`"\\Z" anchors are not allowed in regular expression patterns`)
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "responses", "200", "schema", "properties", "slug", "pattern"])
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it("should not return an error when a regex pattern doesn't use a Z anchor", () => {
|
||||
const spec = {
|
||||
swagger: "2.0",
|
||||
paths: {
|
||||
$ref: "#/definitions/asdf"
|
||||
},
|
||||
"definitions": {
|
||||
"asdf": {
|
||||
type: "string",
|
||||
pattern: "^[-a-zA-Z0-9_]+"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,146 @@
|
||||
import expect from "expect"
|
||||
import validateHelper, { expectNoErrorsOrWarnings } from "./validate-helper.js"
|
||||
|
||||
describe("validation plugin - semantic - security scopes", function() {
|
||||
this.timeout(10 * 1000) // For the slow validateHelper startup ( via swagger-ui )
|
||||
|
||||
it("should return an error when an operation references a non-existing security scope", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "query",
|
||||
"scopes": {
|
||||
"asdf": "blah blah"
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"description": "asdf",
|
||||
"security": [
|
||||
{
|
||||
"api_key": [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "security", "0", "0"])
|
||||
expect(firstError.message).toEqual("Security scope definition write:pets could not be resolved")
|
||||
})
|
||||
})
|
||||
|
||||
it("should return an error when an operation references a security definition with no scopes", () => {
|
||||
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "query"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"description": "asdf",
|
||||
"security": [
|
||||
{
|
||||
"api_key": [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors.length).toEqual(1)
|
||||
const firstError = allErrors[0]
|
||||
expect(firstError.path).toEqual(["paths", "/", "get", "security", "0", "0"])
|
||||
expect(firstError.message).toMatch("Security scope definition write:pets could not be resolved")
|
||||
})
|
||||
})
|
||||
|
||||
it("should not return an error when an operation references an existing security scope", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "query",
|
||||
"scopes": {
|
||||
"write:pets": "write to pets"
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"description": "asdf",
|
||||
"security": [
|
||||
{
|
||||
"api_key": [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
|
||||
it("should not return an error when an top-level security references an existing security scope", () => {
|
||||
const spec = {
|
||||
"swagger": "2.0",
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "query",
|
||||
"scopes": {
|
||||
"write:pets": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"description": "asdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expectNoErrorsOrWarnings(spec)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,72 @@
|
||||
import expect from "expect"
|
||||
import SwaggerUi from "swagger-ui"
|
||||
import debounce from "lodash/debounce"
|
||||
|
||||
import ValidateBasePlugin from "plugins/validate-base"
|
||||
import ValidateSemanticPlugin from "plugins/validate-semantic"
|
||||
import ASTPlugin from "plugins/ast"
|
||||
|
||||
const envDelay = process.env.ASYNC_TEST_DELAY
|
||||
|
||||
const DELAY_MS = (typeof envDelay === "string" ? parseInt(envDelay) : envDelay) || 50
|
||||
|
||||
export default function validateHelper(spec) {
|
||||
return new Promise((resolve) => {
|
||||
const system = SwaggerUi({
|
||||
spec,
|
||||
domNode: null,
|
||||
presets: [
|
||||
SwaggerUi.plugins.SpecIndex,
|
||||
SwaggerUi.plugins.ErrIndex,
|
||||
SwaggerUi.plugins.DownloadUrl,
|
||||
SwaggerUi.plugins.SwaggerJsIndex,
|
||||
SwaggerUi.plugins.Oas3Index
|
||||
],
|
||||
initialState: {
|
||||
layout: undefined
|
||||
},
|
||||
plugins: [
|
||||
ASTPlugin,
|
||||
ValidateBasePlugin,
|
||||
ValidateSemanticPlugin,
|
||||
() => ({
|
||||
statePlugins: {
|
||||
configs: {
|
||||
actions: {
|
||||
loaded: () => {
|
||||
return {
|
||||
type: "noop"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
system.validateActions.all()
|
||||
|
||||
const registerActivity = debounce(() => resolve(system), DELAY_MS)
|
||||
|
||||
system.getStore().subscribe(registerActivity)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export function expectNoErrorsOrWarnings(spec) {
|
||||
return validateHelper(spec)
|
||||
.then( system => {
|
||||
const allErrors = system.errSelectors.allErrors().toJS()
|
||||
expect(allErrors).toEqual([])
|
||||
})
|
||||
}
|
||||
|
||||
export function expectNoErrors(spec) {
|
||||
return validateHelper(spec)
|
||||
.then(system => {
|
||||
let allErrors = system.errSelectors.allErrors().toJS()
|
||||
allErrors = allErrors.filter(a => a.level === "error") // ignore warnings
|
||||
expect(allErrors).toEqual([])
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
if (typeof process === "object") {
|
||||
require("jsdom-global")()
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { fromJS } from "immutable"
|
||||
import React from "react"
|
||||
import expect from "expect"
|
||||
import { configure, mount } from "enzyme"
|
||||
import Adapter from "enzyme-adapter-react-15"
|
||||
import AddForm from "src/standalone/topbar-insert/forms/components/AddForm"
|
||||
import FormChild from "src/standalone/topbar-insert/forms/components/FormChild"
|
||||
import FormDropdown from "src/standalone/topbar-insert/forms/components/FormDropdown"
|
||||
import FormInput from "src/standalone/topbar-insert/forms/components/FormInput"
|
||||
import FormInputWrapper from "src/standalone/topbar-insert/forms/components/FormInputWrapper"
|
||||
import FormMap from "src/standalone/topbar-insert/forms/components/FormMap"
|
||||
import InsertForm from "src/standalone/topbar-insert/forms/components/InsertForm"
|
||||
import InsertFormInput from "src/standalone/topbar-insert/forms/components/InsertFormInput"
|
||||
import InsertFormList from "src/standalone/topbar-insert/forms/components/InsertFormList"
|
||||
|
||||
configure({ adapter: new Adapter() })
|
||||
|
||||
describe("editor topbar insert form UI generation", function() {
|
||||
this.timeout(10 * 1000)
|
||||
let components, props
|
||||
|
||||
beforeEach(() => {
|
||||
components = {
|
||||
FormDropdown,
|
||||
AddForm,
|
||||
FormChild,
|
||||
FormInput,
|
||||
FormInputWrapper,
|
||||
FormMap,
|
||||
InsertForm,
|
||||
InsertFormInput,
|
||||
InsertFormList
|
||||
}
|
||||
|
||||
props = {
|
||||
getComponent: (c) => components[c]
|
||||
}
|
||||
})
|
||||
|
||||
it("should produce a valid form UI for a simple form object", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "test value",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
updateForm: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should produce a form ui with a dropdown", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
options: ["optiona", "optionb"],
|
||||
updateForm: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("select").length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should produce a form ui with a list control", () => {
|
||||
const listControlItem = (updateForm, path) => fromJS({
|
||||
listItem: {
|
||||
value: "list item value",
|
||||
name: "List Item",
|
||||
updateForm: newForm => updateForm(newForm, path.concat(["listItem"]))
|
||||
}
|
||||
})
|
||||
|
||||
let path = []
|
||||
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: [listControlItem(() => null, path.concat(["fielda", "value", 0]))],
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
updateForm: () => null,
|
||||
defaultItem: i => listControlItem(() => null, path.concat(["fielda", "value", i]) )
|
||||
}
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(1)
|
||||
expect(wrapper.find("a").length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should produce a form ui with a map", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
keyValue: "",
|
||||
value: {
|
||||
fieldb: {
|
||||
value: "test value",
|
||||
isRequired: true,
|
||||
name: "field b",
|
||||
updateForm: () => null
|
||||
}
|
||||
},
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
updateForm: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(2)
|
||||
})
|
||||
|
||||
it("should not render an input that depends on an input with no value", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
options: ["optiona", "optionb"],
|
||||
updateForm: () => null
|
||||
},
|
||||
fieldb: {
|
||||
dependsOn: ["fielda", "value"],
|
||||
updateOptions: () => { return ["option c", "option d"] },
|
||||
value: "",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
options: [],
|
||||
updateForm: () => null
|
||||
},
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("select").length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should render an input that depends on an input with a value", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "optiona",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
options: ["optiona", "optionb"],
|
||||
updateForm: () => null
|
||||
},
|
||||
fieldb: {
|
||||
dependsOn: ["fielda", "value"],
|
||||
updateOptions: () => { return ["option c", "option d"] },
|
||||
value: "",
|
||||
isRequired: true,
|
||||
name: "field a",
|
||||
options: [],
|
||||
updateForm: () => null
|
||||
},
|
||||
})
|
||||
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("select").length).toEqual(2)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,316 @@
|
||||
import expect from "expect"
|
||||
import React from "react"
|
||||
import { configure, mount } from "enzyme"
|
||||
import Adapter from "enzyme-adapter-react-15"
|
||||
import { fromJS, List } from "immutable"
|
||||
|
||||
import AddForm from "src/standalone/topbar-insert/forms/components/AddForm"
|
||||
import FormChild from "src/standalone/topbar-insert/forms/components/FormChild"
|
||||
import FormDropdown from "src/standalone/topbar-insert/forms/components/FormDropdown"
|
||||
import FormInput from "src/standalone/topbar-insert/forms/components/FormInput"
|
||||
import FormInputWrapper from "src/standalone/topbar-insert/forms/components/FormInputWrapper"
|
||||
import FormMap from "src/standalone/topbar-insert/forms/components/FormMap"
|
||||
import InsertForm from "src/standalone/topbar-insert/forms/components/InsertForm"
|
||||
import InsertFormInput from "src/standalone/topbar-insert/forms/components/InsertFormInput"
|
||||
import InsertFormList from "src/standalone/topbar-insert/forms/components/InsertFormList"
|
||||
|
||||
import { pathForm, pathObject } from "src/standalone/topbar-insert/forms/form-objects/path-object"
|
||||
import { operationForm, operationObject } from "src/standalone/topbar-insert/forms/form-objects/operation-object"
|
||||
import { infoForm, infoObject } from "src/standalone/topbar-insert/forms/form-objects/info-object"
|
||||
import { licenseForm} from "src/standalone/topbar-insert/forms/form-objects/license-object"
|
||||
import { contactForm } from "src/standalone/topbar-insert/forms/form-objects/contact-object"
|
||||
import { tagsForm, tagsObject } from "src/standalone/topbar-insert/forms/form-objects/tags-object"
|
||||
import { tagForm } from "src/standalone/topbar-insert/forms/form-objects/tag-object"
|
||||
import { serversForm, serversObject } from "src/standalone/topbar-insert/forms/form-objects/servers-object"
|
||||
import { serverVariableForm } from "src/standalone/topbar-insert/forms/form-objects/server-variable-object"
|
||||
import { externalDocumentationForm } from "src/standalone/topbar-insert/forms/form-objects/external-documentation-object"
|
||||
import { addOperationTagsForm, addOperationTagsObject } from "src/standalone/topbar-insert/forms/form-objects/add-operation-tags"
|
||||
import { selectOperationForm } from "src/standalone/topbar-insert/forms/form-objects/select-operation"
|
||||
import { selectResponseForm } from "src/standalone/topbar-insert/forms/form-objects/select-response"
|
||||
import { exampleObject, exampleForm } from "src/standalone/topbar-insert/forms/form-objects/example-value-object"
|
||||
|
||||
configure({ adapter: new Adapter() })
|
||||
|
||||
describe("editor topbar insert forms", function() {
|
||||
this.timeout(10 * 1000)
|
||||
let components, props
|
||||
|
||||
beforeEach(() => {
|
||||
components = {
|
||||
FormDropdown,
|
||||
AddForm,
|
||||
FormChild,
|
||||
FormInput,
|
||||
FormInputWrapper,
|
||||
FormMap,
|
||||
InsertForm,
|
||||
InsertFormInput,
|
||||
InsertFormList
|
||||
}
|
||||
|
||||
props = {
|
||||
getComponent: (c) => components[c]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
describe("operation object", function () {
|
||||
let form = operationForm(null, [])
|
||||
|
||||
const tag = form.getIn(["tags", "defaultItem"])(null, [])
|
||||
|
||||
form = form
|
||||
.setIn(["path", "value"], "/testpath")
|
||||
.setIn(["summary", "value"], "test summary")
|
||||
.setIn(["description", "value"], "test description")
|
||||
.setIn(["operationid", "value"], "testid")
|
||||
.setIn(["tags", "value"], new List([tag.setIn(["tag", "value"], "test tag" )]))
|
||||
|
||||
const object = operationObject(form)
|
||||
|
||||
it("should correctly process the operation form into the operation object", () => {
|
||||
const expected = {
|
||||
summary: "test summary",
|
||||
description: "test description",
|
||||
operationId: "testid",
|
||||
tags: ["test tag"],
|
||||
responses: {
|
||||
default: {
|
||||
description: "Default error sample response"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(4)
|
||||
expect(wrapper.find("select").length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("info object", () => {
|
||||
const license = licenseForm(null, [], null)
|
||||
.setIn(["value", "name", "value"], "test name")
|
||||
.setIn(["value", "url", "value"], "test url")
|
||||
|
||||
const contact = contactForm(null, [])
|
||||
.setIn(["value", "name", "value"], "test name")
|
||||
.setIn(["value", "url", "value"], "test url")
|
||||
.setIn(["value", "email", "value"], "testemail@test.com")
|
||||
|
||||
let form = infoForm(null, [])
|
||||
.setIn(["title", "value"], "test title")
|
||||
.setIn(["version", "value"], "test version")
|
||||
.setIn(["description", "value"], "test description")
|
||||
.setIn(["termsofservice", "value"], "testtermsofservice")
|
||||
.setIn(["license"], license)
|
||||
.setIn(["contact"], contact)
|
||||
|
||||
it("should correctly process the info form into the info object", () => {
|
||||
const object = infoObject(form)
|
||||
|
||||
const expected = {
|
||||
title: "test title",
|
||||
version: "test version",
|
||||
description: "test description",
|
||||
termsOfService: "testtermsofservice",
|
||||
license: {
|
||||
name: "test name",
|
||||
url: "test url"
|
||||
},
|
||||
contact: {
|
||||
name: "test name",
|
||||
url: "test url",
|
||||
email: "testemail@test.com"
|
||||
}
|
||||
}
|
||||
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(4)
|
||||
})
|
||||
})
|
||||
|
||||
describe("path object", () => {
|
||||
let form = pathForm(null, [])
|
||||
|
||||
// Set some values as though the user had entered data
|
||||
form = form
|
||||
.setIn(["path", "value"], "/test")
|
||||
.setIn(["summary", "value"], "test summary")
|
||||
.setIn(["description", "value"], "test description")
|
||||
|
||||
it("should correctly process the path form into the path object", () => {
|
||||
const object = pathObject(form)
|
||||
const expected = {
|
||||
key: "/test",
|
||||
value: {
|
||||
summary: "test summary",
|
||||
description: "test description"
|
||||
}
|
||||
}
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe("tag declarations object", () => {
|
||||
let form = tagsForm(null, [])
|
||||
|
||||
const externalDocs = externalDocumentationForm(null, [])
|
||||
.setIn(["url", "value"], "test url")
|
||||
.setIn(["description", "value"], "test description")
|
||||
|
||||
const tag = tagForm(null, [])
|
||||
.setIn(["name", "value"], "test tag name")
|
||||
.setIn(["description", "value"], "test description")
|
||||
.setIn(["externalDocs", "value"], externalDocs)
|
||||
|
||||
// Set some values as though the user had entered data
|
||||
form = form.setIn(["tags", "value"], new List([tag]))
|
||||
|
||||
it("should correctly process the tag declarations form into the tags object", () => {
|
||||
const object = tagsObject(form)
|
||||
const expected = [
|
||||
{
|
||||
name: "test tag name",
|
||||
description: "test description",
|
||||
externalDocs: {
|
||||
url: "test url",
|
||||
description: "test description"
|
||||
}
|
||||
}
|
||||
]
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("servers object", () => {
|
||||
let form = serversForm(null, [])
|
||||
const serverVariable = serverVariableForm(null, [])
|
||||
.setIn(["value", 0, "value", "default", "value"], "test default")
|
||||
.setIn(["value", 0, "value", "enum", "value"], fromJS([{ value: "test enum value"}]) )
|
||||
.setIn(["value", 0, "value", "vardescription", "value"], "test var description")
|
||||
.setIn(["value", 0, "keyValue"], "keyvalue")
|
||||
|
||||
form = form
|
||||
.setIn(["servers", "value", 0, "url", "value"], "test url")
|
||||
.setIn(["servers", "value", 0, "description", "value"], "test description")
|
||||
.setIn(["servers", "value", 0, "variables"], serverVariable)
|
||||
|
||||
it ("should correctly process the servers form into the servers object", () => {
|
||||
const object = serversObject(form)
|
||||
const expected = [
|
||||
{
|
||||
url: "test url",
|
||||
description: "test description",
|
||||
variables: {
|
||||
keyvalue: {
|
||||
default: "test default",
|
||||
description: "test var description",
|
||||
enum: [
|
||||
"test enum value"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(6)
|
||||
})
|
||||
})
|
||||
|
||||
describe("add tags object", () => {
|
||||
const selectOperation = selectOperationForm(null, [])
|
||||
.setIn(["path", "value"], "/test")
|
||||
.setIn(["operation", "value"], "GET")
|
||||
|
||||
let form = addOperationTagsForm(null, [])
|
||||
.setIn(["selectoperation", "value"], selectOperation)
|
||||
.setIn(["tags", "value", 0, "tag", "value"], "test tag")
|
||||
|
||||
it ("should correctly process the add tags to operation into the add tags object", () => {
|
||||
const object = addOperationTagsObject(form)
|
||||
|
||||
const expected = {
|
||||
selectedOperation: ["paths", "/test", "GET"],
|
||||
tags: [
|
||||
"test tag"
|
||||
]
|
||||
}
|
||||
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(1)
|
||||
expect(wrapper.find("select").length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("add example response object", () => {
|
||||
const selectResponse = selectResponseForm(null, [])
|
||||
.setIn(["path", "value"], "/test")
|
||||
.setIn(["operation", "value"], "GET")
|
||||
.setIn(["response", "value"], "200")
|
||||
.setIn(["mediatype", "value"], "application/json")
|
||||
|
||||
let form = exampleForm(null, [])
|
||||
.setIn(["selectresponse", "value"], selectResponse)
|
||||
.setIn(["exampleName", "value"], "sample example name")
|
||||
.setIn(["exampleValue", "value"], "sample example value")
|
||||
|
||||
it ("should correctly process the add example form into the form values object", () => {
|
||||
const object = exampleObject(form)
|
||||
|
||||
const expected = {
|
||||
responsePath: ["paths", "/test", "GET", "responses", "200", "content", "application/json", "examples"],
|
||||
exampleValue: "sample example value",
|
||||
exampleName: "sample example name"
|
||||
}
|
||||
|
||||
expect(object).toEqual(expected)
|
||||
})
|
||||
|
||||
it ("should correctly render the form UI for the form object", () => {
|
||||
const element = <InsertForm {...props} formData={form} />
|
||||
const wrapper = mount(element)
|
||||
|
||||
expect(wrapper.find("input").length).toEqual(1)
|
||||
expect(wrapper.find("select").length).toEqual(4)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,111 @@
|
||||
import { fromJS, OrderedMap, List } from "immutable"
|
||||
import expect from "expect"
|
||||
import {
|
||||
validateUrl,
|
||||
validateAlphaNum,
|
||||
checkForEmptyValue,
|
||||
checkForErrors
|
||||
} from "src/standalone/topbar-insert/forms/helpers/validation-helpers"
|
||||
|
||||
describe("editor topbar insert form validation", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
it("should produce no errors for a valid form", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "test value",
|
||||
isRequired: true,
|
||||
name: "field a"
|
||||
},
|
||||
fieldb: {
|
||||
value: [
|
||||
{ value: "value a", isRequired: false, isValid: () => true },
|
||||
{ value: "value b", isRequired: false, isValid: () => true }
|
||||
],
|
||||
isRequired: true
|
||||
},
|
||||
fieldc: {
|
||||
value: "",
|
||||
isRequired: false
|
||||
}
|
||||
})
|
||||
|
||||
const errors = checkForErrors(form)[1]
|
||||
const updatedForm = checkForErrors(form)[0]
|
||||
|
||||
expect(errors).toEqual(false)
|
||||
expect(updatedForm.getIn(["fielda", "value"])).toEqual("test value")
|
||||
expect(updatedForm.getIn(["fieldb", "hasErrors"])).toEqual(false)
|
||||
})
|
||||
|
||||
it("should produce errors for a form with an empty required value", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "",
|
||||
isRequired: true,
|
||||
name: "field a"
|
||||
}
|
||||
})
|
||||
|
||||
const errors = checkForErrors(form)[1]
|
||||
const updatedForm = checkForErrors(form)[0]
|
||||
|
||||
expect(errors).toBeTruthy()
|
||||
expect(updatedForm.getIn(["fielda", "value"])).toEqual("")
|
||||
expect(updatedForm.getIn(["fielda", "hasErrors"])).toBeTruthy()
|
||||
})
|
||||
|
||||
it("should produce errors for a form with data that does not meet validation", () => {
|
||||
const form = fromJS({
|
||||
fielda: {
|
||||
value: "@#*&$*@)#$&@#*$",
|
||||
isRequired: false,
|
||||
name: "field a",
|
||||
isValid: () => false
|
||||
}
|
||||
})
|
||||
|
||||
const errors = checkForErrors(form)[1]
|
||||
const updatedForm = checkForErrors(form)[0]
|
||||
|
||||
expect(errors).toBeTruthy()
|
||||
expect(updatedForm.getIn(["fielda", "value"])).toEqual("@#*&$*@)#$&@#*$")
|
||||
expect(updatedForm.getIn(["fielda", "hasErrors"])).toBeTruthy()
|
||||
})
|
||||
|
||||
it("should correctly validate valid urls", () => {
|
||||
expect(validateUrl("https://petstore.swagger.io")).toBeTruthy()
|
||||
expect(validateUrl("https://www.bing.com/search?q=open+api&qs=n&form=QBLH&sp=-1&pq=open+api&sc=6-8&sk=&cvid=2B4FC1A0686B42FAA4DE3534FDA56A8B")).toBeTruthy()
|
||||
})
|
||||
|
||||
it ("should correctly validate invalid urls", () => {
|
||||
expect(validateUrl("")).toBeFalsy()
|
||||
expect(validateUrl("test")).toBeFalsy()
|
||||
})
|
||||
|
||||
it ("should correctly validate alphanumeric strings", () => {
|
||||
expect(validateAlphaNum("abcde12345")).toBeTruthy()
|
||||
expect(validateAlphaNum("42")).toBeTruthy()
|
||||
expect(validateAlphaNum("AaBbCc")).toBeTruthy()
|
||||
})
|
||||
|
||||
it ("should correctly validate invalid alphanumeric strings", () => {
|
||||
expect(validateAlphaNum("")).toBeFalsy()
|
||||
expect(validateAlphaNum("abc@123")).toBeFalsy()
|
||||
expect(validateAlphaNum("*")).toBeFalsy()
|
||||
})
|
||||
|
||||
it ("should correctly detect an empty value", () => {
|
||||
expect(checkForEmptyValue(new OrderedMap())).toBeTruthy()
|
||||
expect(checkForEmptyValue(" ")).toBeTruthy()
|
||||
expect(checkForEmptyValue([])).toBeTruthy()
|
||||
expect(checkForEmptyValue(new List())).toBeTruthy()
|
||||
expect(checkForEmptyValue("")).toBeTruthy()
|
||||
})
|
||||
|
||||
it ("should correclty detect a non-empty value", () => {
|
||||
expect(checkForEmptyValue("value")).toBeFalsy()
|
||||
expect(checkForEmptyValue(fromJS({ test: ""}))).toBeFalsy()
|
||||
expect(checkForEmptyValue(fromJS([""]))).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,136 @@
|
||||
import expect from "expect"
|
||||
import React from "react"
|
||||
import SwaggerUi from "swagger-ui"
|
||||
import insertPlugin from "src/standalone/topbar-insert"
|
||||
import { fromJS } from "immutable"
|
||||
import { configure, mount } from "enzyme"
|
||||
import Adapter from "enzyme-adapter-react-15"
|
||||
|
||||
configure({ adapter: new Adapter() })
|
||||
|
||||
function getSystem(spec) {
|
||||
return new Promise((resolve) => {
|
||||
const system = SwaggerUi({
|
||||
spec,
|
||||
domNode: null,
|
||||
presets: [
|
||||
SwaggerUi.plugins.SpecIndex,
|
||||
SwaggerUi.plugins.ErrIndex,
|
||||
SwaggerUi.plugins.DownloadUrl,
|
||||
SwaggerUi.plugins.SwaggerJsIndex,
|
||||
],
|
||||
initialState: {
|
||||
layout: undefined
|
||||
},
|
||||
plugins: [
|
||||
insertPlugin,
|
||||
() => ({
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors: {
|
||||
isOAS3: () => () => true
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
() => ({
|
||||
statePlugins: {
|
||||
configs: {
|
||||
actions: {
|
||||
loaded: () => {
|
||||
return {
|
||||
type: "noop"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
resolve(system)
|
||||
})
|
||||
}
|
||||
|
||||
describe("editor topbar insert menu plugin", function() {
|
||||
this.timeout(10 * 1000)
|
||||
|
||||
it("should provide a `addToSpec` method as a spec action", async () => {
|
||||
const spec = {}
|
||||
const system = await getSystem(spec)
|
||||
expect(system.specActions.addToSpec).toBeA(Function)
|
||||
})
|
||||
|
||||
it("should provide an <InsertMenu /> component to render a menu option", async function(){
|
||||
const spec = {}
|
||||
const system = await getSystem(spec)
|
||||
const InsertMenu = system.getSystem().getComponents("TopbarInsert")
|
||||
let wrapper = mount(<InsertMenu {...system} getComponent={(c) => system.getSystem().getComponents(c)} />)
|
||||
expect(wrapper.find(".menu-item").length).toEqual(1)
|
||||
})
|
||||
|
||||
it("should correctly update the spec when addToSpec is called", async () => {
|
||||
const spec = {
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "My New Service"
|
||||
},
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"summary": "Sample endpoint for my awesome service.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toAdd = fromJS({
|
||||
"summary": "Sample endpoint for my awesome service.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const expected = {
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "My New Service"
|
||||
},
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"summary": "Sample endpoint for my awesome service.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"summary": "Sample endpoint for my awesome service.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const system = await getSystem(spec)
|
||||
|
||||
system.specActions.addToSpec(["paths", "/test"], toAdd, "post")
|
||||
expect(system.specSelectors.specJson().toJS()).toEqual(expected)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user