diff --git a/.env b/.env index e51ba15..cc2ec1e 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -REACT_APP_API_URL=http://localhost:8080 +REACT_APP_API_URL=https://dev.editor.jsight.io/api REACT_APP_CLOUD_URL=https://cloud.jsight.io REACT_APP_GTM_ID= REACT_APP_CUSTOM_MESSAGE_URL=https://jsightapi.github.io/online-editor-custom-messages.json diff --git a/.gitignore b/.gitignore index 7402277..a43094f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ yarn-error.log* .vscode /local +.tool-versions diff --git a/Dockerfile b/Dockerfile index 9332938..f239223 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # stage1 as builder FROM node:14-alpine as builder -ARG API_URL="/parse-jsight" +ARG API_URL="/api" ARG GTM_ID="" ARG CLOUD_URL="https://cloud.jsight.io" ARG CUSTOM_MESSAGE_URL="https://jsightapi.github.io/online-editor-custom-messages.json" diff --git a/README.md b/README.md index 1cc6269..deb0602 100644 --- a/README.md +++ b/README.md @@ -32,17 +32,20 @@ -**JSight Online Editor** – you've never designed API so fast. We really mean it. +**JSight Online Editor** – the most human-friendly language for designing APIs. -1. Write 2-4 times less code. -2. Design API 2-3 times faster. -3. Download beautiful HTML docs :book: or share API specifications in the cloud [:cloud:](https://editor.jsight.io). -4. Do not distract from the API design because of the API description process. -5. Design API together, it is extremely convenient with JSight language. +You've never designed API so fast. We really mean it. + +1. OpenAPI is automatically generated. +2. Write 2-4 times less code. +3. Design API 2-3 times faster. +4. Download beautiful HTML docs :book: or share API specifications in the cloud [:cloud:](https://editor.jsight.io). +5. Do not distract from the API design because of the API description process. +6. Design API together, it is extremely convenient with JSight language. No more multiple design iterations and edits required. All you need is to share the screen between the participants and come up with an API together right in the Editor. All this is possible thanks to the compactness and simplicity of the JSight language. - [Compare JSight with Open API](#scroll--jsight-api-language). + [Compare JSight with OpenAPI](#scroll--jsight-api-language). Supported standards: [HTTP REST](#scroll--jsight-api-language), [JSON-RPC 2.0](#json-rpc-20-new-feature). @@ -60,7 +63,7 @@ Supported standards: [HTTP REST](#scroll--jsight-api-language), [JSON-RPC - Constantine M., JSight CRDO. + Konstantin M., JSight CRDO.   @@ -95,7 +98,7 @@ Supported standards: [HTTP REST](#scroll--jsight-api-language), [JSON-RPC   -![Demo](./img/demo.gif) +![Demo](./img/openapi.gif)
  @@ -130,9 +133,10 @@ Supported standards: [HTTP REST](#scroll--jsight-api-language), [JSON-RPC JSight Online Editor officially supports the latest browsers: -- Chrome 102.0, -- Firefox 101.0, -- Safari 15.3. +- Chrome 124.0, +- Firefox 125.0, +- Safari 17.3, +- Edge 124.0. ### Installing @@ -194,10 +198,18 @@ Start JSight Server (on port 8080): +Linux: + ``` SERVER_HOST_PORT=8080 docker-compose -f jsight-server-docker-compose.yml up -d ``` +Windows PowerShell: + +``` +$env:SERVER_HOST_PORT=8080; docker-compose -f jsight-server-docker-compose.yml up -d --build +``` + @@ -371,10 +383,18 @@ Build and start JSight Online Editor: +Linux: + ``` FE_HOST_PORT=80 SERVER_HOST_PORT=8080 docker-compose -f docker-compose.yml up -d --build ``` +Windows PowerShell: + +``` +$env:FE_HOST_PORT=80; $env:SERVER_HOST_PORT=8080; docker-compose -f docker-compose.yml up -d --build +``` + @@ -1461,7 +1481,7 @@ TYPE @pet // A pet. TYPE @cat // A cat. { // {allOf: "@pet"} "status": "relaxing", - "bestFriend": @cat, + "bestFriend": @cat, // {optional: true} "topFriends": { // {additionalProperties: true} @petName: @cat | @pig }, @@ -1704,9 +1724,10 @@ support](https://jsight.io/docs/jsight-api-0-3-quick-tutorial/lesson10). ## :book:   Features -- Description of your REST API in a very simple and intuitive [JSight API language](https://jsight.io/docs/jsight-api-0-3). +- Description of your REST and JSON RPC API in a very simple and intuitive [JSight API language](https://jsight.io/docs/jsight-api-0-3). +- Instant automatic OpenAPI definition generation. - Convenient syntax highlighting. -- Instant automatic document generation. +- Instant automatic HTML document generation. - Intuitive document navigation. - View data schemas in two formats: in the form of an example (Code View) and in a tabular form (Table View). - Expand nested data types on click. @@ -1722,7 +1743,7 @@ support](https://jsight.io/docs/jsight-api-0-3-quick-tutorial/lesson10). - Download document also in MarkDown, PDF, and DOCX formats. - Quick Help built into the editor allows you to quickly access the necessary information on the JSight API language without leaving the editor. -- Converting the JSight specification to OpenAPI and vice versa. +- Converting the OpenAPI specification to JSight. - Automatic generation of API clients and API server stubs. - Support for other types of API: gRPC, Kafka, RabbitMQ, WebSocket. - Sending test requests to the API. @@ -1883,8 +1904,8 @@ Links to the components: - JSight Server API specification: https://github.com/jsightapi/jsight-server/blob/main/jsight/jsight-server-api.jst. - JSight Server repo: https://github.com/jsightapi/jsight-server. -- JSight API Go Library repo: https://github.com/jsightapi/jsight-api-go-library. -- JSight Schema Go Library repo: https://github.com/jsightapi/jsight-schema-go-library. +- JSight API Go Library repo: https://github.com/jsightapi/jsight-api-core. +- JSight Schema Go Library repo: https://github.com/jsightapi/jsight-schema-core.
  @@ -1892,7 +1913,8 @@ https://github.com/jsightapi/jsight-server/blob/main/jsight/jsight-server-api.js ## :test_tube:   Testing -The manual tests are available here: [tests/manual-tests/](./tests/manual-tests/). +- The **manual tests** are available here: [tests/manual-tests/](./tests/manual-tests/). +- The **automated tests** can be run via dedicated CI/CD pipelines. See more info about [running automated tests here](./tests/running-automated-tests-ci.md).
  @@ -1921,6 +1943,8 @@ document. + +
  @@ -1984,7 +2008,7 @@ details. - All JSight repositories: https://github.com/jsightapi. - GitHub discussions: https://github.com/jsightapi/online-editor-frontend/discussions. -- JSight video lessons: +- JSight Youtube: https://www.youtube.com/watch?v=AegCETY9Cdk&list=PLy9sOecVhlybvRoDhGlzyMwKVxR-gFkdX
diff --git a/docker-compose.yml b/docker-compose.yml index 2507bef..3bb4b31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: - '${FE_HOST_PORT}:80' jsight-server: - image: jsight/jsight-server:4 + image: jsight/jsight-server:6.0 environment: - JSIGHT_SERVER_CORS=true - JSIGHT_SERVER_STATISTICS diff --git a/img/openapi.gif b/img/openapi.gif new file mode 100644 index 0000000..409a093 Binary files /dev/null and b/img/openapi.gif differ diff --git a/jsight-server-docker-compose.yml b/jsight-server-docker-compose.yml index c65fe98..a17ba38 100644 --- a/jsight-server-docker-compose.yml +++ b/jsight-server-docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: jsight-server: - image: jsight/jsight-server:4 + image: jsight/jsight-server:6.0 environment: - JSIGHT_SERVER_CORS=true - JSIGHT_SERVER_STATISTICS diff --git a/package-lock.json b/package-lock.json index 52b6ff3..b3e3146 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,17 +16,17 @@ "monaco-editor": "0.31.1", "re-resizable": "6.9.1", "react": "^18.1.0", - "react-collapse": "5.1.1", - "react-collapsed": "3.3.0", + "react-animate-height": "2.1.2", "react-dom": "^18.1.0", "react-gtm-module": "2.0.11", + "react-helmet": "6.1.0", "react-intersection-observer": "8.32.5", "react-modal": "3.14.4", "react-popper": "2.2.5", "react-router-dom": "5.3.0", "react-scripts": "5.0.0", "react-scroll": "1.8.4", - "react-toastify": "8.1.0", + "react-toastify": "9.0.1", "react-transition-group": "4.4.2", "react-virtuoso": "2.7.1", "sanitize-html": "2.7.0", @@ -46,6 +46,7 @@ "@types/react-collapse": "5.0.1", "@types/react-dom": "17.0.14", "@types/react-gtm-module": "2.0.1", + "@types/react-helmet": "^6.1.5", "@types/react-modal": "3.13.1", "@types/react-router-dom": "5.3.1", "@types/react-transition-group": "4.4.4", @@ -3469,6 +3470,15 @@ "integrity": "sha512-T/DN9gAbCYk5wJ1nxf4pSwmXz4d1iVjM++OoG+mwMfz9STMAotGjSb65gJHOS5bPvl6vLSsJnuC+y/43OQrltg==", "dev": true }, + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-modal": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz", @@ -5085,6 +5095,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-css": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", @@ -12898,6 +12913,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-animate-height": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-animate-height/-/react-animate-height-2.1.2.tgz", + "integrity": "sha512-A9jfz/4CTdsIsE7WCQtO9UkOpMBcBRh8LxyHl2eoZz1ki02jpyUL5xt58gabd0CyeLQ8fRyQ+s2lyV2Ufu8Owg==", + "dependencies": { + "classnames": "^2.2.5", + "prop-types": "^15.6.1" + }, + "engines": { + "node": ">= 6.0.0" + }, + "peerDependencies": { + "react": ">=15.6.2", + "react-dom": ">=15.6.2" + } + }, "node_modules/react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -12938,30 +12969,6 @@ "semver": "bin/semver" } }, - "node_modules/react-collapse": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-collapse/-/react-collapse-5.1.1.tgz", - "integrity": "sha512-k6cd7csF1o9LBhQ4AGBIdxB60SUEUMQDAnL2z1YvYNr9KoKr+nDkhN6FK7uGaBd/rYrYfrMpzpmJEIeHRYogBw==", - "peerDependencies": { - "react": ">=16.3.0" - } - }, - "node_modules/react-collapsed": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-3.3.0.tgz", - "integrity": "sha512-2oXo9xsleo3ZwmNP7GymWbkZp2SwDZImLduy1LKgQMsqZTDldyzP2GnfvYb9vyGFDYHRYFhnWlwDmG2yWkt8eQ==", - "dependencies": { - "raf": "^3.4.1", - "tiny-warning": "^1.0.3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-dev-utils": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz", @@ -13096,6 +13103,20 @@ "resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz", "integrity": "sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw==" }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/react-intersection-observer": { "version": "8.32.5", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.32.5.tgz", @@ -13593,10 +13614,18 @@ "react-dom": "^15.5.4 || ^16.0.0 || ^17.0.0" } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-toastify": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz", - "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.1.tgz", + "integrity": "sha512-c2zeZHkCX+WXuItS/JRqQ/8CH8Qm/je+M0rt09xe9fnu5YPJigtNOdD8zX4fwLA093V2am3abkGfOowwpkrwOQ==", "dependencies": { "clsx": "^1.1.1" }, @@ -18831,6 +18860,15 @@ "integrity": "sha512-T/DN9gAbCYk5wJ1nxf4pSwmXz4d1iVjM++OoG+mwMfz9STMAotGjSb65gJHOS5bPvl6vLSsJnuC+y/43OQrltg==", "dev": true }, + "@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-modal": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz", @@ -20033,6 +20071,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" }, + "classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "clean-css": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", @@ -25606,6 +25649,15 @@ "loose-envify": "^1.1.0" } }, + "react-animate-height": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-animate-height/-/react-animate-height-2.1.2.tgz", + "integrity": "sha512-A9jfz/4CTdsIsE7WCQtO9UkOpMBcBRh8LxyHl2eoZz1ki02jpyUL5xt58gabd0CyeLQ8fRyQ+s2lyV2Ufu8Owg==", + "requires": { + "classnames": "^2.2.5", + "prop-types": "^15.6.1" + } + }, "react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -25636,20 +25688,6 @@ } } }, - "react-collapse": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-collapse/-/react-collapse-5.1.1.tgz", - "integrity": "sha512-k6cd7csF1o9LBhQ4AGBIdxB60SUEUMQDAnL2z1YvYNr9KoKr+nDkhN6FK7uGaBd/rYrYfrMpzpmJEIeHRYogBw==" - }, - "react-collapsed": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-3.3.0.tgz", - "integrity": "sha512-2oXo9xsleo3ZwmNP7GymWbkZp2SwDZImLduy1LKgQMsqZTDldyzP2GnfvYb9vyGFDYHRYFhnWlwDmG2yWkt8eQ==", - "requires": { - "raf": "^3.4.1", - "tiny-warning": "^1.0.3" - } - }, "react-dev-utils": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz", @@ -25750,6 +25788,17 @@ "resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz", "integrity": "sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw==" }, + "react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + } + }, "react-intersection-observer": { "version": "8.32.5", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.32.5.tgz", @@ -26116,10 +26165,15 @@ "prop-types": "^15.7.2" } }, + "react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==" + }, "react-toastify": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz", - "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.1.tgz", + "integrity": "sha512-c2zeZHkCX+WXuItS/JRqQ/8CH8Qm/je+M0rt09xe9fnu5YPJigtNOdD8zX4fwLA093V2am3abkGfOowwpkrwOQ==", "requires": { "clsx": "^1.1.1" } diff --git a/package.json b/package.json index 73269ac..eb1f821 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "web-vitals": "1.0.1", "yaml": "1.10.2" }, + "proxy": "https://dev.editor.jsight.io", "scripts": { "start": "react-app-rewired start", "one": "./scripts/save-export-js.sh", diff --git a/src/api/baseUrl.ts b/src/api/baseUrl.ts new file mode 100644 index 0000000..380ca74 --- /dev/null +++ b/src/api/baseUrl.ts @@ -0,0 +1,3 @@ +export const baseUrl = process.env.REACT_APP_API_URL || `${window.location.origin}`; + +export const convertJsightUrl = `${baseUrl}/convert-jsight`; diff --git a/src/api/codeSharing.ts b/src/api/codeSharing.ts index 4d3e71f..7f17a9f 100644 --- a/src/api/codeSharing.ts +++ b/src/api/codeSharing.ts @@ -31,7 +31,11 @@ export const createNewState = (content: string) => { }, }; - return runRequest(`${cloudUrl}/item`, {body: JSON.stringify(item)}, 5000); + return runRequest( + `${cloudUrl}/item`, + {body: JSON.stringify(item)}, + {timeout: 5000} + ); }; export const updateState = (code: string, content: string) => { @@ -46,7 +50,7 @@ export const updateState = (code: string, content: string) => { return runRequest( `${cloudUrl}/item/${code}`, {body: JSON.stringify(item)}, - 5000 + {timeout: 5000} ); }; diff --git a/src/api/getJDocExchange.ts b/src/api/convert.ts similarity index 54% rename from src/api/getJDocExchange.ts rename to src/api/convert.ts index c52cf93..fbffafa 100644 --- a/src/api/getJDocExchange.ts +++ b/src/api/convert.ts @@ -1,10 +1,8 @@ import {runRequest} from 'utils/runRequest'; -import {JDocType} from 'types/exchange'; import {v4 as uuidv4} from 'uuid'; +import {convertJsightUrl} from './baseUrl'; -export const baseUrlApi = process.env.REACT_APP_API_URL || `${window.location.origin}/api`; - -export const getJDocExchange = (body: string) => { +export const convert = (body = '', format: string) => { const uuid = localStorage.getItem('uuid') || ''; if (!uuid) { @@ -16,5 +14,9 @@ export const getJDocExchange = (body: string) => { 'Content-Type': 'text/plain', }; - return runRequest(baseUrlApi, {body, headers}); + return runRequest( + `${convertJsightUrl}?to=openapi-3.0.3&format=${format}`, + {body, headers}, + {responseAsText: true} + ); }; diff --git a/src/api/convertJsight.ts b/src/api/convertJsight.ts new file mode 100644 index 0000000..f4bab22 --- /dev/null +++ b/src/api/convertJsight.ts @@ -0,0 +1,29 @@ +import {ConvertDestinationType, OpenApiFormatType} from 'types/converter'; +import {runRequest} from 'utils/runRequest'; +import {v4 as uuidv4} from 'uuid'; +import {convertJsightUrl} from './baseUrl'; + +export const convertJsight = ( + body = '', + to: ConvertDestinationType, + format?: OpenApiFormatType +) => { + const uuid = localStorage.getItem('uuid') || ''; + + if (!uuid) { + localStorage.setItem('uuid', uuidv4()); + } + + const isOpenApi = to === 'openapi-3.0.3'; + + const headers = { + 'X-Browser-UUID': uuid, + 'Content-Type': 'text/plain', + }; + + let url = `${convertJsightUrl}?to=${to}`; + + if (format) url += `&format=${format}`; + + return runRequest(url, {body, headers}, {responseAsText: isOpenApi}); +}; diff --git a/src/assets/images/icons/contents.svg b/src/assets/images/icons/contents.svg new file mode 100644 index 0000000..ae60378 --- /dev/null +++ b/src/assets/images/icons/contents.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/images/icons/copy.svg b/src/assets/images/icons/copy.svg new file mode 100644 index 0000000..4fec64d --- /dev/null +++ b/src/assets/images/icons/copy.svg @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/icons/htmldoc.svg b/src/assets/images/icons/htmldoc.svg new file mode 100644 index 0000000..861479b --- /dev/null +++ b/src/assets/images/icons/htmldoc.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/images/icons/openapi.svg b/src/assets/images/icons/openapi.svg new file mode 100644 index 0000000..fd63847 --- /dev/null +++ b/src/assets/images/icons/openapi.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/images/icons/vector.svg b/src/assets/images/icons/vector.svg index b55ade5..b4f452c 100644 --- a/src/assets/images/icons/vector.svg +++ b/src/assets/images/icons/vector.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/components/ApiInfo/ApiInfo.styles.scss b/src/components/ApiInfo/ApiInfo.styles.scss index 104944b..27c7240 100644 --- a/src/components/ApiInfo/ApiInfo.styles.scss +++ b/src/components/ApiInfo/ApiInfo.styles.scss @@ -17,4 +17,8 @@ .description { color: $secondary-text; } + + &.hidden { + display: none; + } } diff --git a/src/components/ApiInfo/index.tsx b/src/components/ApiInfo/index.tsx index 497f78d..fb2a5af 100644 --- a/src/components/ApiInfo/index.tsx +++ b/src/components/ApiInfo/index.tsx @@ -1,4 +1,5 @@ import React, {useEffect} from 'react'; +import clsx from 'clsx'; import {ApiInfoType} from 'types/exchange'; import {Description} from '../Description'; import './ApiInfo.styles.scss'; @@ -7,9 +8,10 @@ const DEFAULT_TITLE = 'JSight Online Editor'; interface ApiInfoProps { apiInfo?: ApiInfoType; + hidden?: boolean; } -export const ApiInfo = ({apiInfo}: ApiInfoProps) => { +export const ApiInfo = ({apiInfo, hidden}: ApiInfoProps) => { useEffect(() => { document.title = apiInfo?.title || DEFAULT_TITLE; return () => { @@ -18,7 +20,7 @@ export const ApiInfo = ({apiInfo}: ApiInfoProps) => { }, [apiInfo?.title]); return apiInfo ? ( -
+

{apiInfo.title}

{apiInfo.version && (
diff --git a/src/components/Button/Button.styles.scss b/src/components/Button/Button.styles.scss index 098e5b9..83f2747 100644 --- a/src/components/Button/Button.styles.scss +++ b/src/components/Button/Button.styles.scss @@ -13,14 +13,17 @@ border-bottom-right-radius: 0; } - .btn+.btn { + .btn + .btn { margin-left: -1px; } } .btn { - padding-left: 2.4rem; - padding-right: 2.4rem; + display: flex; + align-items: center; + justify-content: center; + padding-left: 1.4rem; + padding-right: 1.4rem; border-radius: 4px; font-weight: 500; font-size: 1.4rem; diff --git a/src/components/CodeView/Code.tsx b/src/components/CodeView/Code.tsx index 399e861..e28caaa 100644 --- a/src/components/CodeView/Code.tsx +++ b/src/components/CodeView/Code.tsx @@ -68,7 +68,12 @@ export const Code = ({schema, tab, codeViewRef, keyBlock}: CodeProps) => { const [height, setHeight] = useState(0); const {selectedLine, setSchemasData, schemasData} = useContext(MainContext); const {expandedTypes} = useContext(SchemaViewContext); - const {currentDocSidebar, setCurrentDocSidebar} = useContext(SidebarContext); + const { + currentDocSidebar, + setCurrentDocSidebar, + currentHtmlDocPanel, + setCurrentHtmlDocPanel, + } = useContext(SidebarContext); const [isFirst, setIsFirst] = useState(true); const schemaData = useMemo(() => { @@ -197,8 +202,8 @@ export const Code = ({schema, tab, codeViewRef, keyBlock}: CodeProps) => { // when active live with rules is changed useEffect(() => { - if (selectedLine && currentDocSidebar !== 'content') { - setCurrentDocSidebar('rules'); + if (selectedLine && currentHtmlDocPanel !== 'content') { + setCurrentHtmlDocPanel('rules'); } else { setHeight((codeViewRef.current?.getBoundingClientRect().height || 0) + 65); } diff --git a/src/components/CodeView/DetailCard/index.tsx b/src/components/CodeView/DetailCard/index.tsx index 4ed5acb..3abe632 100644 --- a/src/components/CodeView/DetailCard/index.tsx +++ b/src/components/CodeView/DetailCard/index.tsx @@ -33,7 +33,7 @@ export const DetailCard = ({ keyBlock, }: DetailCardProps) => { const divRef = useRef(null); - const {setCurrentDocSidebar} = useContext(SidebarContext); + const {setCurrentHtmlDocPanel} = useContext(SidebarContext); const {setSelectedLine} = useContext(MainContext); const renderRule = (rule: RuleType, key: string, rulesLength: number, index: number) => { @@ -80,7 +80,7 @@ export const DetailCard = ({ }; const closeDetailCard = () => { - setCurrentDocSidebar(null); + setCurrentHtmlDocPanel('none'); setSelectedLine(null); }; diff --git a/src/components/CodeView/Line/ShortcutLines.tsx b/src/components/CodeView/Line/ShortcutLines.tsx index c036d58..5bc6fcb 100644 --- a/src/components/CodeView/Line/ShortcutLines.tsx +++ b/src/components/CodeView/Line/ShortcutLines.tsx @@ -21,7 +21,7 @@ export const ShortcutLines = ({ tab, disableOpenBracket, }: ShortcutLinesProps) => { - const jdocData = useContext(JDocContext); + const {jdocExchange: jdocData} = useContext(JDocContext); const userType = useMemo(() => { const schemaName = schemasNames.slice(-1).pop(); diff --git a/src/components/CodeView/RightRules.tsx b/src/components/CodeView/RightRules.tsx index 6af6775..8b3ff3c 100644 --- a/src/components/CodeView/RightRules.tsx +++ b/src/components/CodeView/RightRules.tsx @@ -24,7 +24,7 @@ export const RightRules: FC = ({ isFirst, }) => { const divRulesRef = useRef(null); - const {currentDocSidebar} = useContext(SidebarContext); + const {currentHtmlDocPanel} = useContext(SidebarContext); const {selectedLine} = useContext(MainContext); const [topOffset, setTopOffset] = useState(0); const [rightOffset, setRightOffset] = useState(0); @@ -43,7 +43,7 @@ export const RightRules: FC = ({ const rightOffset = wrapper ? parseInt(getComputedStyle(wrapper).paddingRight) : 0; setRightOffset(rightOffset); - if (selectedLine?.keyBlock === keyBlock && currentDocSidebar === 'rules') { + if (selectedLine?.keyBlock === keyBlock && currentHtmlDocPanel === 'rules') { const detailActiveElements = divRulesRef.current?.querySelectorAll( `[data-name="line-${selectedLine.numberLine}"]` ); @@ -82,10 +82,10 @@ export const RightRules: FC = ({ updateHeight(detailActiveElement, codeLineDocumentOffset); } } - }, [selectedLine, currentDocSidebar, keyBlock]); + }, [selectedLine, currentHtmlDocPanel, keyBlock]); const updateDetailWrapperHeight = () => { - if (selectedLine?.keyBlock === keyBlock && currentDocSidebar === 'rules') { + if (selectedLine?.keyBlock === keyBlock && currentHtmlDocPanel === 'rules') { const detailActiveElements = divRulesRef.current?.querySelectorAll( `[data-name="line-${selectedLine.numberLine}"]` ); @@ -138,7 +138,7 @@ export const RightRules: FC = ({
{ if (prev?.numberLine === numberLine && prev?.keyBlock === keyBlock) { - setCurrentDocSidebar(null); + setCurrentDocSidebar('htmldoc'); return null; } - setCurrentDocSidebar('rules'); + setCurrentHtmlDocPanel('rules'); return { numberLine, keyBlock, @@ -67,7 +67,7 @@ export function useSelectionLine({ }); } else { setSelectedLine(null); - setCurrentDocSidebar(null); + setCurrentDocSidebar('htmldoc'); } }; diff --git a/src/components/CodeView/index.tsx b/src/components/CodeView/index.tsx index c359ba9..831348c 100644 --- a/src/components/CodeView/index.tsx +++ b/src/components/CodeView/index.tsx @@ -14,7 +14,7 @@ interface UsedUserElementProps { } const UsedUserType = ({value, keyBlock}: UsedUserElementProps) => { - const jdocData = useContext(JDocContext); + const {jdocExchange: jdocData} = useContext(JDocContext); const userType = useMemo( () => (jdocData?.userTypes ? getUserType(value, jdocData.userTypes) : null), @@ -36,7 +36,7 @@ const UsedUserType = ({value, keyBlock}: UsedUserElementProps) => { }; const UsedUserEnum = ({value, keyBlock}: UsedUserElementProps) => { - const jdocData = useContext(JDocContext); + const {jdocExchange: jdocData} = useContext(JDocContext); const userEnum = useMemo( () => (jdocData?.userEnums ? getUserEnum(value, jdocData.userEnums) : null), diff --git a/src/components/Dropdown/DropdownMenu/index.tsx b/src/components/Dropdown/DropdownMenu/index.tsx index 0348952..9e1204a 100644 --- a/src/components/Dropdown/DropdownMenu/index.tsx +++ b/src/components/Dropdown/DropdownMenu/index.tsx @@ -1,5 +1,6 @@ import React, {useContext} from 'react'; import {Popper} from 'react-popper'; +import {Placement} from '@popperjs/core/lib'; import clsx from 'clsx'; import {DropdownContext} from '../index'; import {DropdownMenuComponent} from '../DropdownMenuComponent'; @@ -9,15 +10,21 @@ interface DropdownMenuProps { offsetY?: number; offsetX?: number; children?: React.ReactNode; + placement?: Placement; } -export const DropdownMenu = ({children, offsetY, offsetX}: DropdownMenuProps) => { +export const DropdownMenu = ({ + children, + offsetY, + offsetX, + placement = 'bottom-start', +}: DropdownMenuProps) => { const {isOpen} = useContext(DropdownContext); return ( {}} modifiers={[ { diff --git a/src/components/Header/Header.styles.scss b/src/components/Header/Header.styles.scss index 57d3356..88f534d 100644 --- a/src/components/Header/Header.styles.scss +++ b/src/components/Header/Header.styles.scss @@ -120,34 +120,34 @@ bottom: 2px; } } + } + + .dropdown { + display: flex; + align-items: center; + } - .dropdown { + .dropdown-items { + li { display: flex; align-items: center; - } + padding: 1rem; + cursor: pointer; - .dropdown-items { - li { - display: flex; - align-items: center; - padding: 1rem; - cursor: pointer; - - &:hover { - background: $main-stroke; - } - img { - margin-left: 10px; - } + &:hover { + background: $main-stroke; + } + img { + margin-left: 10px; } } + } - .dropdown-menu { - border: 0; - border-radius: 0; - background: $background; - top: 0.5rem !important; - } + .dropdown-menu { + border: 0; + border-radius: 0; + background: $background; + top: 0.5rem !important; } .logo { diff --git a/src/components/Header/HeaderDoc.export.tsx b/src/components/Header/HeaderDoc.export.tsx index 17cd3cf..7354a60 100644 --- a/src/components/Header/HeaderDoc.export.tsx +++ b/src/components/Header/HeaderDoc.export.tsx @@ -10,8 +10,8 @@ interface HeaderDocProps { } export const HeaderDoc = ({setViewMode}: HeaderDocProps) => { - const jdocData = useContext(JDocContext); - const [saveHtml] = useExport(); + const {jdocExchange: jdocData} = useContext(JDocContext); + const {saveHtml} = useExport(); return (
diff --git a/src/components/Header/HeaderDoc.tsx b/src/components/Header/HeaderDoc.tsx index 99b76c9..69da81f 100644 --- a/src/components/Header/HeaderDoc.tsx +++ b/src/components/Header/HeaderDoc.tsx @@ -10,8 +10,8 @@ interface HeaderDocProps { } export const HeaderDoc = ({setViewMode}: HeaderDocProps) => { - const jdocData = useContext(JDocContext); - const [saveHtml] = useExport(); + const {jdocExchange: jdocData} = useContext(JDocContext); + const {saveHtml} = useExport(); const title = jdocData?.info?.title; return ( diff --git a/src/components/Header/MenuItems/DownloadMenu/DownloadMenu.styles.scss b/src/components/Header/MenuItems/DownloadMenu/DownloadMenu.styles.scss new file mode 100644 index 0000000..1a3caf7 --- /dev/null +++ b/src/components/Header/MenuItems/DownloadMenu/DownloadMenu.styles.scss @@ -0,0 +1,33 @@ +@import 'src/styles/variables'; + +.app-header .dropdown { + button { + font-weight: 500; + } + + i { + margin-left: 8px; + } + + .icon { + margin-right: 8px; + } + + .dropdown-items li.item { + display: flex; + flex-direction: row; + align-items: flex-start; + } + + .name { + font-size: 14px; + font-weight: 500; + color: $main-text; + } + + .desc { + font-size: 12px; + font-weight: 400; + color: $secondary-text; + } +} diff --git a/src/components/Header/MenuItems/DownloadMenu/index.tsx b/src/components/Header/MenuItems/DownloadMenu/index.tsx new file mode 100644 index 0000000..e17ffe2 --- /dev/null +++ b/src/components/Header/MenuItems/DownloadMenu/index.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import {Dropdown} from 'components/Dropdown'; +import {DropdownToggle} from 'components/Dropdown/DropdownToggle'; +import {DropdownMenu} from 'components/Dropdown/DropdownMenu'; + +import IconOpenAPI from 'assets/images/icons/openapi.svg'; +import IconHTMLDoc from 'assets/images/icons/htmldoc.svg'; + +import './DownloadMenu.styles.scss'; +import {useExport} from 'hooks/useExport'; + +interface MenuType { + icon: string; + name: string; + desc: string; + action: () => Promise; +} + +interface DocsMenuProps { + isMenuOpened: boolean; + setIsMenuOpened: React.Dispatch>; +} + +interface DocsMenuItemsProps { + setIsMenuOpened: React.Dispatch>; +} + +export const DownloadMenu = ({isMenuOpened, setIsMenuOpened}: DocsMenuProps) => { + const {saveHtml, saveJson, saveYaml} = useExport(); + + const menu: MenuType[] = [ + { + icon: IconHTMLDoc, + name: 'HTML page', + desc: 'Download the prettified documentation page', + action: saveHtml, + }, + { + icon: IconOpenAPI, + name: 'OpenAPI JSON', + desc: 'Download the converted OpenAPI JSON code', + action: saveJson, + }, + { + icon: IconOpenAPI, + name: 'OpenAPI YAML', + desc: 'Download the converted OpenAPI YAML code', + action: saveYaml, + }, + ]; + + const DownloadMenuItems = ({setIsMenuOpened}: DocsMenuItemsProps) => ( +
    + {menu.map((v, key) => ( +
    { + v.action(); + setIsMenuOpened(false); + }} + > +
  • + +
    +
    {v.name}
    +
    {v.desc}
    +
    +
  • +
    + ))} +
+ ); + + return ( + + + Download + + + + + + + ); +}; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index f9398fc..c867310 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,15 +1,18 @@ -import React, {useState} from 'react'; +import React, {useContext, useState} from 'react'; import clsx from 'clsx'; import {Button} from '../Button'; import {HeaderLogo} from './HeaderLogo'; import {DocsMenu} from './MenuItems/DocsMenu'; import {FileMenu} from './MenuItems/FileMenu'; -import {useExport} from 'hooks/useExport'; import {editorModeType, ExamplesType} from 'types'; import {ShareButton} from 'components/ShareButton'; -import './Header.styles.scss'; import {Example} from '../Modals/Example'; import Modal from 'react-modal'; +import {Toggle} from 'components/Toggle'; +import {DownloadMenu} from './MenuItems/DownloadMenu'; +import {SidebarContext} from 'store'; + +import './Header.styles.scss'; interface HeaderProps { setInitialContent(content: ExamplesType): void; @@ -28,10 +31,19 @@ export const Header = ({ }: HeaderProps) => { const [docsMenuVisible, setDocsMenuVisible] = useState(false); const [fileMenuVisible, setFileMenuVisible] = useState(false); + const [downloadMenuVisible, setDownloadMenuVisible] = useState(false); const [examplePopup, setExampleMenuPopup] = useState(null); + + const { + currentDocSidebar, + setCurrentDocSidebar, + currentOpenApiFormat, + setCurrentOpenApiFormat, + } = useContext(SidebarContext); + const switchDocsMenu = () => setDocsMenuVisible(!docsMenuVisible); const switchFileMenu = () => setFileMenuVisible(!fileMenuVisible); - const [saveHtml] = useExport(); + const switchDownloadMenu = () => setDownloadMenuVisible(!downloadMenuVisible); if (examplePopup) { Modal.setAppElement('#root'); @@ -60,25 +72,35 @@ export const Header = ({
  • - +
  • + {currentDocSidebar === 'openapi' && ( + + setCurrentOpenApiFormat && setCurrentOpenApiFormat(value ? 'yaml' : 'json') + } + /> + )} +
    diff --git a/src/components/HeaderMetaTags/index.tsx b/src/components/HeaderMetaTags/index.tsx index 44adf34..3b072fa 100644 --- a/src/components/HeaderMetaTags/index.tsx +++ b/src/components/HeaderMetaTags/index.tsx @@ -4,7 +4,7 @@ import {JDocContext} from 'store'; import {shorten} from 'utils/shorten'; export const HeaderMetaTags = () => { - const jdocData = useContext(JDocContext); + const {jdocExchange: jdocData} = useContext(JDocContext); const getTitle = (limit: number) => { if (jdocData?.info?.title) { diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 1a07530..9d31356 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -9,8 +9,10 @@ interface LayoutProps { } export const Layout = ({children, isEditor}: LayoutProps) => { - const {currentDocSidebar} = useContext(SidebarContext); - const isShowSidebar = isEditor ? currentDocSidebar === 'content' : true; + const {currentDocSidebar, currentHtmlDocPanel} = useContext(SidebarContext); + const isShowSidebar = isEditor + ? currentHtmlDocPanel === 'content' && currentDocSidebar === 'htmldoc' + : true; const side = isEditor ? 'right' : 'left'; return ( diff --git a/src/components/MainContent/MainContent.styles.scss b/src/components/MainContent/MainContent.styles.scss index a85e5bd..e00bb5a 100644 --- a/src/components/MainContent/MainContent.styles.scss +++ b/src/components/MainContent/MainContent.styles.scss @@ -23,7 +23,12 @@ } } - .reusable-header, .resource-header { + .wrapper { + position: relative; + } + + .reusable-header, + .resource-header { font-size: 2.8rem; line-height: 3.6rem; font-weight: 600; @@ -36,7 +41,7 @@ .resource-header { padding: 0 3rem 1.2rem; - +.resource-wrapper { + + .resource-wrapper { padding-bottom: 3.2rem; } } @@ -68,3 +73,43 @@ .main-resources { height: 100%; } + +.openapi-wrapper { + display: flex; +} + +.openapi-lines { + padding: 20px 0; + width: 54px; + font-family: 'JetBrainsMono', serif; + font-size: 14px; + text-align: center; + background-color: $secondary-grey; + color: $main-grey; + flex-shrink: 0; +} + +.openapi-content { + margin: 0; + padding: 20px 24px; + font-family: 'JetBrainsMono', serif; + font-size: 14px; + flex: 1; +} + +.openapi-copy.btn { + position: absolute; + bottom: 20px; + right: 50px; + width: auto; + padding: 11px 23px; + border-radius: 4px; + border: 1px solid $secondary-stroke; + background: $main-stroke; + color: $main-text; + opacity: 0.8; +} + +.openapi-copy img { + margin-left: 8px; +} diff --git a/src/components/MainContent/index.tsx b/src/components/MainContent/index.tsx index 4792388..30ce256 100644 --- a/src/components/MainContent/index.tsx +++ b/src/components/MainContent/index.tsx @@ -1,6 +1,6 @@ -import React, {useRef, useState, useEffect, useContext, useMemo} from 'react'; +import React, {useRef, useState, useEffect, useContext, useMemo, Fragment} from 'react'; import {Virtuoso, VirtuosoHandle} from 'react-virtuoso'; -import {compact, each, groupBy, isEqual, map, mapValues} from 'lodash'; +import {compact, each, groupBy, map, mapValues} from 'lodash'; import {useParams} from 'react-router-dom'; import {MainRouterParams} from 'types/router'; import {ApiInfo} from 'components/ApiInfo'; @@ -15,6 +15,15 @@ import {HttpResource} from 'components/Resource/Http'; import './MainContent.styles.scss'; import {JsonRpcHeader} from 'components/Resource/JsonRpc/JsonRpcHeader'; import {JsonRpcResource} from 'components/Resource/JsonRpc'; +import clsx from 'clsx'; +import {editorModeType, ErrorType} from 'types'; +import {Button} from 'components/Button'; + +import IconCopy from 'assets/images/icons/copy.svg'; +import {toast} from 'react-toastify'; +import {notificationIds} from 'utils/notificationIds'; +import {convertJsight} from 'api/convertJsight'; +import {showEditorError} from 'utils/showEditorError'; type SchemaPropertyType = | 'collapsedRules' @@ -25,298 +34,408 @@ type SchemaPropertyType = interface MainContentProps { jdocExchange?: JDocType; + jdocExchangeError?: boolean; + isJdocLoading?: boolean; + jsightCode?: string; + viewMode?: editorModeType; + setScrollToRow?: React.Dispatch>; + setErrorRow?: React.Dispatch>; } -export const MainContent = React.memo( - ({jdocExchange}: MainContentProps) => { - const divRef = useRef(null); - const virtuosoRef = useRef(null); - const virtuosoScrollerRef = useRef(null); - const [selectedLine, setSelectedLine] = useState(null); - const [jdocList, setJdocList] = useState([]); - const [jdocPositions, setJdocPositions] = useState([]); - const {path} = useParams(); - const {headersBodiesTypesCode, pathQueriesCode, typesExpand, rulesExpand} = useContext( - GlobalSettingsContext - ); - const {currentUrl, setCurrentUrl} = useContext(CurrentUrlContext); - const {currentDocSidebar, setCurrentDocSidebar} = useContext(SidebarContext); - const [overscan, setOverscan] = useState(480); - const [schemasView, setSchemasView] = useState([]); - const [schemasData, setSchemasData] = useState<{[key: string]: SchemaData[]}>({}); - const [resourceState, setResourceState] = useState([]); - // @ts-ignore - window['mainContent'] = virtuosoRef; - - const showRightSidebar = useMemo(() => !!currentDocSidebar, [currentDocSidebar]); - - useEffect(() => { - if (!currentUrl && path) { - const currentPath = path[0] !== '@' ? '/' + path : path; - const index = jdocPositions.indexOf(`${currentPath.replace(/({|})/gi, '-')}`); - - if (~index && virtuosoRef?.current) { - virtuosoRef.current.scrollToIndex({ - index: index + 1, - align: 'start', - behavior: 'auto', - }); - setCurrentUrl(path); - } - } - }, [path, jdocPositions]); - - useEffect(() => { - setSchemasView((prev) => { - return prev.map((item) => { - if (item.typeBlock === 'header-body') { - item['viewType'] = headersBodiesTypesCode ? 'code' : 'table'; - } - return item; - }); +export const MainContent = React.memo((props: MainContentProps) => { + const { + jdocExchange, + jdocExchangeError, + isJdocLoading, + jsightCode, + viewMode, + setScrollToRow, + setErrorRow, + } = props; + const virtuosoRef = useRef(null); + const virtuosoScrollerRef = useRef(null); + const [selectedLine, setSelectedLine] = useState(null); + const [jdocList, setJdocList] = useState([]); + const [jdocPositions, setJdocPositions] = useState([]); + const {path} = useParams(); + const {headersBodiesTypesCode, pathQueriesCode, typesExpand, rulesExpand} = useContext( + GlobalSettingsContext + ); + const {currentUrl, setCurrentUrl} = useContext(CurrentUrlContext); + const { + currentDocSidebar, + currentOpenApiFormat, + currentHtmlDocPanel, + setCurrentHtmlDocPanel, + } = useContext(SidebarContext); + const [overscan, setOverscan] = useState(480); + const [schemasView, setSchemasView] = useState([]); + const [schemasData, setSchemasData] = useState<{[key: string]: SchemaData[]}>({}); + const [resourceState, setResourceState] = useState([]); + + const [openApiContent, setOpenApiContent] = useState(''); + const [openApiLinesCount, setOpenApiLinesCount] = useState(); + const [isOpenApiContentLoading, setIsOpenApiContentLoading] = useState(false); + + // @ts-ignore + window['mainContent'] = virtuosoRef; + + const showRightSidebar = useMemo(() => !!currentDocSidebar, [currentDocSidebar]); + + const copyToClipboard = () => { + if (navigator && navigator.clipboard && navigator.clipboard.writeText && openApiContent) { + navigator.clipboard.writeText(openApiContent); + + toast.success('OpenAPI copied', { + toastId: notificationIds.SUCCESS_MESSAGE_DEFAULT_ID, + position: 'bottom-center', + className: 'notification-success success', + autoClose: 3000, + hideProgressBar: true, + closeButton: false, + closeOnClick: false, + pauseOnHover: false, + draggable: false, + progress: undefined, + icon: , }); - }, [headersBodiesTypesCode]); - useEffect(() => { - setSchemasView((prev) => { - return prev.map((item) => { - if (item.typeBlock === 'path-query') { - item['viewType'] = pathQueriesCode ? 'code' : 'table'; - } - return item; - }); - }); - }, [pathQueriesCode]); + return Promise.resolve(); + } - useEffect(() => { - setSchemasView((prev) => { - return prev.map((item) => { - return { - ...item, - expandedTypes: typesExpand, - }; - }); - }); + return Promise.reject('The Clipboard API is not available.'); + }; - if (!typesExpand) { - setSchemasData((prev) => { - return mapValues(prev, () => { - return [emptySchemaData]; - }); - }); - } - }, [typesExpand]); + useEffect(() => { + if (!currentUrl && path) { + const currentPath = path[0] !== '@' ? '/' + path : path; + const index = jdocPositions.indexOf(`${currentPath.replace(/({|})/gi, '-')}`); - useEffect(() => { - setSchemasView((prev) => { - return prev.map((item) => { - return { - ...item, - collapsedRules: !rulesExpand, - }; + if (~index && virtuosoRef?.current) { + virtuosoRef.current.scrollToIndex({ + index: index + 1, + align: 'start', + behavior: 'auto', }); - }); - }, [rulesExpand]); - - useEffect(() => { - setOverscan(window.innerHeight / 2); - }, []); - - useEffect(() => { - if (currentDocSidebar === 'content') { - setSelectedLine(null); + setCurrentUrl(path); } - }, [currentDocSidebar]); - - const updateSchemaView = (keyBlock: string, value: any, property: SchemaPropertyType) => { - setSchemasView((prev) => { - if (prev.find((item) => item.key === keyBlock)) { - return prev.map((item) => { - if (item.key === keyBlock) { - item[property] = value; - return item; - } - return item; - }); - } else { - const schemaView: SchemaViewType = {key: keyBlock}; - schemaView[property] = value; - return [...prev, schemaView]; + } + }, [path, jdocPositions]); + + useEffect(() => { + setSchemasView((prev) => { + return prev.map((item) => { + if (item.typeBlock === 'header-body') { + item['viewType'] = headersBodiesTypesCode ? 'code' : 'table'; } + return item; }); - }; - - const setCollapsedRules = (keyBlock: string, value: boolean) => { - updateSchemaView(keyBlock, value, 'collapsedRules'); - }; - - const setExpandedTypes = (keyBlock: string, value: boolean) => { - updateSchemaView(keyBlock, value, 'expandedTypes'); - }; - - const setViewType = (keyBlock: string, value: string) => { - updateSchemaView(keyBlock, value, 'viewType'); - }; - - const setExpandDetailCard = (keyBlock: string, value: boolean) => { - updateSchemaView(keyBlock, value, 'expandDetailCard'); - }; - - const setTypeBlock = (keyBlock: string, value: string | undefined) => { - updateSchemaView(keyBlock, value, 'typeBlock'); - }; - - useEffect(() => { - if (jdocExchange) { - const {info, servers, tags, interactions, userTypes, userEnums} = jdocExchange; - const jdocList: JSX.Element[] = []; - const jdocPositions: string[] = []; - jdocList.push(
    ); - - if (info) { - jdocList.push(); - jdocPositions.push('info'); - } - - if (servers) { - jdocList.push(); - jdocPositions.push('servers'); + }); + }, [headersBodiesTypesCode]); + + useEffect(() => { + setSchemasView((prev) => { + return prev.map((item) => { + if (item.typeBlock === 'path-query') { + item['viewType'] = pathQueriesCode ? 'code' : 'table'; } + return item; + }); + }); + }, [pathQueriesCode]); + + useEffect(() => { + setSchemasView((prev) => { + return prev.map((item) => { + return { + ...item, + expandedTypes: typesExpand, + }; + }); + }); - if (Object.values(tags).length > 0) { - let index = 0; - each(tags, (tag, tagKey) => { - if (tag.interactionGroups.find((item) => item.protocol === 'json-rpc-2.0')) { - jdocList.push(); - } else { - jdocList.push( -
    -

    {tag.title}

    -
    -
    -
    -
    - ); + if (!typesExpand) { + setSchemasData((prev) => { + return mapValues(prev, () => { + return [emptySchemaData]; + }); + }); + } + }, [typesExpand]); + + useEffect(() => { + setSchemasView((prev) => { + return prev.map((item) => { + return { + ...item, + collapsedRules: !rulesExpand, + }; + }); + }); + }, [rulesExpand]); + + useEffect(() => { + setOverscan(window.innerHeight / 2); + }, []); + + useEffect(() => { + if (currentDocSidebar === 'openapi') { + toast.dismiss(notificationIds.ERROR_MESSAGE_HTMLDOC_ID); + } else if (currentDocSidebar === 'htmldoc') { + toast.dismiss(notificationIds.ERROR_MESSAGE_OPENAPI_ID); + } + }, [currentDocSidebar]); + + useEffect(() => { + if (currentDocSidebar === 'openapi' && currentOpenApiFormat) { + const convert = async () => { + try { + setIsOpenApiContentLoading(true); + const result = await convertJsight(jsightCode, 'openapi-3.0.3', currentOpenApiFormat); + setOpenApiContent(result as string); + setIsOpenApiContentLoading(false); + toast.dismiss(); + setErrorRow && setErrorRow(null); + } catch (error) { + showEditorError(error as ErrorType, notificationIds.ERROR_MESSAGE_OPENAPI_ID, () => { + if (!(error as ErrorType).Line) { + return; } - jdocPositions.push(`resource-${tagKey}`); - - tag.interactionGroups.forEach((interactionGroup) => { - if (interactionGroup.protocol === 'http') { - each( - groupBy( - compact( - interactionGroup.interactions.map((interaction) => - interactions.hasOwnProperty(interaction) ? interactions[interaction] : null - ) - ), - 'path' - ), - (interactionsByPath, interactionPath) => { - const resourceKey = `${interactionPath.replace(/({|})/gi, '-')}`; - jdocList.push( - - ); - jdocPositions.push(resourceKey); - setResourceState((prev) => [...prev, {method: ''}]); - index++; - } - ); - } else if (interactionGroup.protocol === 'json-rpc-2.0') { - interactionGroup.interactions.forEach((interaction) => { - if (interactions.hasOwnProperty(interaction)) { - const resource = interactions[interaction] as JsonRpcInteractionType; - const resourceKey = `${resource.path.replace(/({|})/gi, '-')}-${ - resource.method - }`; - - jdocList.push( - - ); - jdocPositions.push(resourceKey); - } - }); - } - }); + + setScrollToRow && setScrollToRow(true); + setTimeout(() => setScrollToRow && setScrollToRow(false), 500); }); + (error as ErrorType).Line && setErrorRow && setErrorRow((error as ErrorType).Line); } + }; - if (userTypes) { - jdocList.push(

    Types

    ); - jdocPositions.push('types'); + convert(); + } + }, [currentDocSidebar, currentOpenApiFormat, jsightCode, setErrorRow, setScrollToRow]); - map(jdocExchange.userTypes, (userType, key) => { - jdocList.push( - - ); - jdocPositions.push(key); - }); - } + const updateSchemaView = (keyBlock: string, value: any, property: SchemaPropertyType) => { + setSchemasView((prev) => { + if (prev.find((item) => item.key === keyBlock)) { + return prev.map((item) => { + if (item.key === keyBlock) { + item[property] = value; + return item; + } + return item; + }); + } else { + const schemaView: SchemaViewType = {key: keyBlock}; + schemaView[property] = value; + return [...prev, schemaView]; + } + }); + }; + + const setCollapsedRules = (keyBlock: string, value: boolean) => { + updateSchemaView(keyBlock, value, 'collapsedRules'); + }; + + const setExpandedTypes = (keyBlock: string, value: boolean) => { + updateSchemaView(keyBlock, value, 'expandedTypes'); + }; + + const setViewType = (keyBlock: string, value: string) => { + updateSchemaView(keyBlock, value, 'viewType'); + }; + + const setExpandDetailCard = (keyBlock: string, value: boolean) => { + updateSchemaView(keyBlock, value, 'expandDetailCard'); + }; + + const setTypeBlock = (keyBlock: string, value: string | undefined) => { + updateSchemaView(keyBlock, value, 'typeBlock'); + }; + + useEffect(() => { + if (jdocExchange) { + const {info, servers, tags, interactions, userTypes, userEnums} = jdocExchange; + const jdocList: JSX.Element[] = []; + const jdocPositions: string[] = []; + jdocList.push(
    ); + + if (info) { + jdocList.push(); + jdocPositions.push('info'); + } - if (userEnums) { - jdocList.push(

    Enums

    ); + if (servers) { + jdocList.push(); + jdocPositions.push('servers'); + } - map(jdocExchange.userEnums, (userEnum, key) => { + if (Object.values(tags).length > 0) { + let index = 0; + each(tags, (tag, tagKey) => { + if (tag.interactionGroups.find((item) => item.protocol === 'json-rpc-2.0')) { + jdocList.push(); + } else { jdocList.push( - +
    +

    {tag.title}

    +
    +
    +
    +
    ); - jdocPositions.push(key); + } + jdocPositions.push(`resource-${tagKey}`); + + tag.interactionGroups.forEach((interactionGroup) => { + if (interactionGroup.protocol === 'http') { + each( + groupBy( + compact( + interactionGroup.interactions.map((interaction) => + interactions.hasOwnProperty(interaction) ? interactions[interaction] : null + ) + ), + 'path' + ), + (interactionsByPath, interactionPath) => { + const resourceKey = `${interactionPath.replace(/({|})/gi, '-')}`; + jdocList.push( + + ); + jdocPositions.push(resourceKey); + setResourceState((prev) => [...prev, {method: ''}]); + index++; + } + ); + } else if (interactionGroup.protocol === 'json-rpc-2.0') { + interactionGroup.interactions.forEach((interaction) => { + if (interactions.hasOwnProperty(interaction)) { + const resource = interactions[interaction] as JsonRpcInteractionType; + const resourceKey = `${resource.path.replace(/({|})/gi, '-')}-${resource.method}`; + + jdocList.push( + + ); + jdocPositions.push(resourceKey); + } + }); + } }); - } - setJdocList(jdocList); - setJdocPositions(jdocPositions); + }); + } - // @ts-ignore - window['jdocPositions'] = jdocPositions; + if (userTypes) { + jdocList.push(

    Types

    ); + jdocPositions.push('types'); + + map(jdocExchange.userTypes, (userType, key) => { + jdocList.push( + + ); + jdocPositions.push(key); + }); } - return () => { - setResourceState([]); - }; - }, [jdocExchange]); - - const value = useMemo( - () => ({ - showRightSidebar, - selectedLine, - schemasView, - schemasData, - resourceState, - setSelectedLine, - setExpandDetailCard, - setCollapsedRules, - setExpandedTypes, - setViewType, - setResourceState, - setTypeBlock, - setSchemasData, - }), - [selectedLine, schemasView, schemasData, resourceState] - ); - - return ( -
    -
    + + if (userEnums) { + jdocList.push(

    Enums

    ); + + map(jdocExchange.userEnums, (userEnum, key) => { + jdocList.push( + + ); + jdocPositions.push(key); + }); + } + setJdocList(jdocList); + setJdocPositions(jdocPositions); + + // @ts-ignore + window['jdocPositions'] = jdocPositions; + } + return () => { + setResourceState([]); + }; + }, [jdocExchange]); + + useEffect(() => { + const count = openApiContent.match(new RegExp('\n', 'g'))?.length || 0; + setOpenApiLinesCount(count + 1); + }, [openApiContent]); + + const value = useMemo( + () => ({ + showRightSidebar, + selectedLine, + schemasView, + schemasData, + resourceState, + setSelectedLine, + setExpandDetailCard, + setCollapsedRules, + setExpandedTypes, + setViewType, + setResourceState, + setTypeBlock, + setSchemasData, + }), + [selectedLine, schemasView, schemasData, resourceState] + ); + + const mainContentClasses = clsx('main-content', { + scrollable: currentDocSidebar === 'openapi', + disabled: + (currentDocSidebar === 'openapi' && isOpenApiContentLoading) || + (currentDocSidebar === 'htmldoc' && jdocExchangeError) || + (currentDocSidebar === 'htmldoc' && isJdocLoading), + }); + + return ( +
    +
    + {currentDocSidebar === 'openapi' && viewMode !== 'doc' && ( +
    +
    + )} + {(currentDocSidebar !== 'openapi' || viewMode === 'doc') && ( - {currentDocSidebar === 'rules' && ( + {currentHtmlDocPanel === 'rules' && (
    + )}
    - ); - }, - (prev, next) => isEqual(prev.jdocExchange, next.jdocExchange) -); +
    + ); +}); diff --git a/src/components/Notifications/EditorErrorNotification.tsx b/src/components/Notifications/EditorErrorNotification.tsx index 1ee9010..e469453 100644 --- a/src/components/Notifications/EditorErrorNotification.tsx +++ b/src/components/Notifications/EditorErrorNotification.tsx @@ -2,7 +2,7 @@ import React, {useCallback, useEffect, useRef} from 'react'; import './Notifications.styles.scss'; export interface CustomNotificationsProps { - message: string | JSX.Element; + message: string; title?: string; setScrollToRow?: () => void; } @@ -37,7 +37,7 @@ export const EditorErrorNotification = ({ return (
    {title &&
    {title}
    } -
    {message}
    +
    ); }; diff --git a/src/components/Notifications/Notifications.styles.scss b/src/components/Notifications/Notifications.styles.scss index 66baf8b..f1080a0 100644 --- a/src/components/Notifications/Notifications.styles.scss +++ b/src/components/Notifications/Notifications.styles.scss @@ -7,7 +7,8 @@ .notification-success { &.success { background: rgba(255, 255, 255, 0.95); - box-shadow: 0 0 36px rgba(27, 17, 40, 0.08), 0 0 12px rgba(27, 17, 40, 0.04), 0 0 4px rgba(27, 17, 40, 0.02); + box-shadow: 0 0 36px rgba(27, 17, 40, 0.08), 0 0 12px rgba(27, 17, 40, 0.04), + 0 0 4px rgba(27, 17, 40, 0.02); border-radius: 21px; min-height: 0; color: $main-text; @@ -66,6 +67,15 @@ line-height: 2.1rem; } + .message a { + text-decoration: underline; + color: $secondary-yellow; + } + + .message a:hover { + text-decoration: none; + } + .btn-close { padding: 0.75rem 1.6rem; width: 6.4rem; diff --git a/src/components/SidebarContent/index.tsx b/src/components/SidebarContent/index.tsx index 2ed2335..8e38acc 100644 --- a/src/components/SidebarContent/index.tsx +++ b/src/components/SidebarContent/index.tsx @@ -19,8 +19,8 @@ interface SidebarContentProps { export const SidebarContent = ({side, isShowSettings, isShow}: SidebarContentProps) => { const {setIsOpen, isOpen} = useContext(GlobalSettingsContext); - const {setCurrentDocSidebar} = useContext(SidebarContext); - const jdocData = useContext(JDocContext); + const {setCurrentHtmlDocPanel} = useContext(SidebarContext); + const {jdocExchange: jdocData} = useContext(JDocContext); const tags = useMemo(() => jdocData?.tags || {}, [jdocData]); @@ -48,7 +48,7 @@ export const SidebarContent = ({side, isShowSettings, isShow}: SidebarContentPro

    Contents

    {!(side === 'left' || isExport) && ( - )} diff --git a/src/components/TableView/RowsCollection.tsx b/src/components/TableView/RowsCollection.tsx index b0c4805..7ef93e7 100644 --- a/src/components/TableView/RowsCollection.tsx +++ b/src/components/TableView/RowsCollection.tsx @@ -34,16 +34,22 @@ export const RowsCollection = ({ ); } else if (content.tokenType === 'object' || content.tokenType === 'array') { const isArray = content.tokenType === 'array'; - rows.push( - ' : String(key)} - level={level + 1} - property={content} - isNestedChild={isNestedChild} - isArrayLastItem={isArrayLastItem} - /> - ); + + if (block === 'path' && level === 0) { + level--; + } else { + rows.push( + ' : String(key)} + level={level + 1} + property={content} + isNestedChild={isNestedChild} + isArrayLastItem={isArrayLastItem} + /> + ); + } + content.children?.forEach((item, index) => { const childrenRows = RowsCollection({ block, @@ -58,8 +64,8 @@ export const RowsCollection = ({ } else if (['number', 'string', 'boolean', 'null'].includes(content.tokenType)) { rows.push( ' : String(key)} + key={`${level}-${level === 0 && block !== 'path' ? 'root' : key}`} + keyValue={level === 0 && block !== 'path' ? '' : String(key)} level={level + 1} property={content} isNestedChild={isNestedChild} diff --git a/src/components/TableView/index.tsx b/src/components/TableView/index.tsx index 0675cda..78cd6fc 100644 --- a/src/components/TableView/index.tsx +++ b/src/components/TableView/index.tsx @@ -20,7 +20,7 @@ export const TableView = ({keyBlock, schema, format, block}: TableViewProps) => block, level: 0, }); - } else if ('regex') { + } else { return ( '} @@ -34,8 +34,6 @@ export const TableView = ({keyBlock, schema, format, block}: TableViewProps) => }} /> ); - } else { - return null; } }; diff --git a/src/components/Toggle/Toggle.styles.scss b/src/components/Toggle/Toggle.styles.scss new file mode 100644 index 0000000..71665a8 --- /dev/null +++ b/src/components/Toggle/Toggle.styles.scss @@ -0,0 +1,76 @@ +@import '/src/styles/variables'; + +.toggle { + display: flex; + align-items: center; + color: $main-text; + font-size: 14px; + font-weight: 500; +} + +.toggle-switch { + position: relative; + display: inline-block; + width: 36px; + height: 20px; + margin: 0 12px; +} + +/* Hide default HTML checkbox */ +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.toggle-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.toggle-slider.equivalent { + background-color: $secondary-blue; +} + +.toggle-slider:before { + position: absolute; + content: ''; + height: 12px; + width: 12px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:checked + .toggle-slider { + background-color: $secondary-blue; +} + +input:focus + .toggle-slider { + box-shadow: 0 0 1px $secondary-blue; +} + +input:checked + .toggle-slider:before { + -webkit-transform: translateX(16px); + -ms-transform: translateX(16px); + transform: translateX(16px); +} + +/* Rounded sliders */ +.toggle-slider.round { + border-radius: 34px; +} + +.toggle-slider.round:before { + border-radius: 50%; +} diff --git a/src/components/Toggle/index.tsx b/src/components/Toggle/index.tsx new file mode 100644 index 0000000..c5d71b3 --- /dev/null +++ b/src/components/Toggle/index.tsx @@ -0,0 +1,36 @@ +import React, {useEffect, useState} from 'react'; +import clsx from 'clsx'; +import './Toggle.styles.scss'; + +interface ToggleProps { + leftOption?: string; + rightOption?: string; + defaultOption?: string | boolean; + isEquivalent?: boolean; + onChange?: (value: boolean) => void; +} + +export const Toggle = ({ + leftOption = 'OFF', + rightOption = 'ON', + defaultOption = false, + isEquivalent, + onChange, +}: ToggleProps) => { + const [checked, setChecked] = useState(defaultOption === true || defaultOption === rightOption); + + useEffect(() => { + onChange && onChange(checked); + }, [checked, onChange]); + + return ( +
    + {leftOption} + + {rightOption} +
    + ); +}; diff --git a/src/hooks/useExport.ts b/src/hooks/useExport.ts index 3e02541..baf2d50 100644 --- a/src/hooks/useExport.ts +++ b/src/hooks/useExport.ts @@ -1,9 +1,10 @@ import {useContext} from 'react'; import {find} from 'lodash'; import {JDocContext} from 'store'; +import {convertJsight} from 'api/convertJsight'; export function useExport() { - const jdocData = useContext(JDocContext); + const {jdocExchange: jdocData, jsightCode} = useContext(JDocContext); const renderHtml: () => Promise = async () => { const styles = document.querySelectorAll('style'); @@ -16,6 +17,7 @@ export function useExport() { let scriptLink = ''; let styleLink = ''; + if (process.env.NODE_ENV !== 'production') { const scripts = document.querySelectorAll('script'); scriptLink = find(scripts, (script) => script.src.indexOf('static/js') !== -1)?.src || ''; @@ -63,7 +65,7 @@ export function useExport() { - + @@ -74,7 +76,7 @@ export function useExport() { - + @@ -98,19 +100,37 @@ export function useExport() { return null; }; + const save = (text: string, title: string, type: string) => { + const content = [text]; + const bl = new Blob(content, {type}); + const a = document.createElement('a'); + a.href = URL.createObjectURL(bl); + a.download = title; + a.hidden = true; + document.body.appendChild(a); + a.click(); + }; + const saveHtml = async () => { - const documentHtml = await renderHtml(); - if (documentHtml) { - const htmlContent = [documentHtml]; - const bl = new Blob(htmlContent, {type: 'text/html'}); - const a = document.createElement('a'); - a.href = URL.createObjectURL(bl); - a.download = 'jsight-document.html'; - a.hidden = true; - document.body.appendChild(a); - a.click(); + const html = await renderHtml(); + if (html) { + save(html, 'jsight-document.html', 'text/html'); + } + }; + + const saveJson = async () => { + const json = await convertJsight(jsightCode, 'openapi-3.0.3', 'json'); + if (json) { + save(json as string, 'jsight-document.json', 'text/json'); + } + }; + + const saveYaml = async () => { + const yaml = await convertJsight(jsightCode, 'openapi-3.0.3', 'yaml'); + if (yaml) { + save(yaml as string, 'jsight-document.yaml', 'application/yaml'); } }; - return [saveHtml]; + return {saveHtml, saveJson, saveYaml}; } diff --git a/src/screens/Editor/Editor.styles.scss b/src/screens/Editor/Editor.styles.scss index afc4e51..b4239fb 100644 --- a/src/screens/Editor/Editor.styles.scss +++ b/src/screens/Editor/Editor.styles.scss @@ -58,7 +58,17 @@ margin-left: 0; margin-right: 0; flex: 1; - /* overflow-y: scroll; */ + + &.scrollable { + overflow-y: scroll; + } + + &.disabled { + overflow: hidden; + opacity: 0.5; + pointer-events: none; + user-select: none; + } .main-body { padding: 0; @@ -119,6 +129,11 @@ margin-bottom: 1rem; font-size: 1rem; } + + &.disabled { + opacity: 0.5; + cursor: not-allowed; + } } } } diff --git a/src/screens/Editor/EditorScreen.export.tsx b/src/screens/Editor/EditorScreen.export.tsx index bebf117..0e41290 100644 --- a/src/screens/Editor/EditorScreen.export.tsx +++ b/src/screens/Editor/EditorScreen.export.tsx @@ -8,12 +8,13 @@ import './Editor.styles.scss'; import 'react-toastify/dist/ReactToastify.css'; import {Contacts} from 'components/Modals/Contacts'; import {screenWidthMultiplier} from 'utils/screenWidthMultiplier'; -import {editorModeType, ErrorType, SidebarDocType} from 'types'; +import {editorModeType, ErrorType, HtmlDocPanelType, SidebarDocType} from 'types'; import {JDocContext, SidebarContext, EditorContext, CurrentUrlProvider} from 'store'; -import {getJDocExchange} from 'api/getJDocExchange'; import {showEditorError} from 'utils/showEditorError'; import {useDebounce} from 'hooks/useDebounce'; import {initCats} from 'screens/Editor/initCats'; +import {convertJsight} from 'api/convertJsight'; +import {notificationIds} from 'utils/notificationIds'; const {isExport} = window as any; @@ -25,7 +26,8 @@ export const EditorScreen = () => { const codeContentsSidebar = false; //documentation sidebar on the right - const [currentDocSidebar, setCurrentDocSidebar] = useState(null); + const [currentDocSidebar, setCurrentDocSidebar] = useState('htmldoc'); + const [currentHtmlDocPanel, setCurrentHtmlDocPanel] = useState('none'); const [jdocExchange, setJdocExchange] = useState(); const jsightCodeDebounced = useDebounce(jsightCode, 600); const [contactModalVisible, setContactModalVisible] = useState(false); @@ -54,11 +56,11 @@ export const EditorScreen = () => { (async () => { if (!isExport) { try { - const jdocExchange = await getJDocExchange(jsightCodeDebounced); - startTransition(() => setJdocExchange(jdocExchange)); + const jdocExchange = await convertJsight(jsightCodeDebounced, 'jdoc-2.0'); + startTransition(() => setJdocExchange(jdocExchange as JDocType)); toast.dismiss(); } catch (error) { - showEditorError(error as ErrorType, () => { + showEditorError(error as ErrorType, notificationIds.ERROR_MESSAGE_DEFAULT_ID, () => { if (!(error as ErrorType).Line) { return; } @@ -78,23 +80,33 @@ export const EditorScreen = () => { () => clsx({ 'editor-wrapper-inner': true, - 'rules-sidebar': currentDocSidebar === 'rules', - 'content-sidebar': currentDocSidebar === 'content', + 'rules-sidebar': currentHtmlDocPanel === 'rules', + 'content-sidebar': currentHtmlDocPanel === 'content', 'code-sidebar': codeContentsSidebar, }), - [codeContentsSidebar, currentDocSidebar] + [codeContentsSidebar, currentHtmlDocPanel] ); - const handleCurrentDocSidebar = useCallback((sidebar: SidebarDocType) => { - setCurrentDocSidebar((prev) => (prev === sidebar ? null : sidebar)); + const handleCurrentHtmlDocPanel = useCallback((htmldocpanel: HtmlDocPanelType) => { + setCurrentHtmlDocPanel((prev) => (prev === htmldocpanel ? 'none' : htmldocpanel)); }, []); + const jdocValue = useMemo( + () => ({ + jdocExchange, + jsightCode: jsightCodeDebounced, + }), + [jdocExchange, jsightCodeDebounced] + ); + const sidebarValue = useMemo( () => ({ currentDocSidebar, setCurrentDocSidebar, + currentHtmlDocPanel, + setCurrentHtmlDocPanel, }), - [currentDocSidebar] + [currentDocSidebar, currentHtmlDocPanel] ); const editorValue = useMemo( @@ -106,7 +118,7 @@ export const EditorScreen = () => { ); return ( - +
    { {isEditor && (
    handleCurrentDocSidebar('content')} + onClick={() => handleCurrentHtmlDocPanel('content')} className={clsx('side-panel-element', { - active: currentDocSidebar === 'content', + active: currentHtmlDocPanel === 'content', })} > Contents diff --git a/src/screens/Editor/index.tsx b/src/screens/Editor/index.tsx index ee306ef..a91e122 100644 --- a/src/screens/Editor/index.tsx +++ b/src/screens/Editor/index.tsx @@ -4,7 +4,6 @@ import {toast, ToastContainer} from 'react-toastify'; import {Resizable} from 're-resizable'; import {Editor} from 'components/Editor'; import {useDebounce} from 'hooks/useDebounce'; -import {getJDocExchange} from 'api/getJDocExchange'; import {JDocType} from 'types/exchange'; import {MainContent} from 'components/MainContent'; import {Layout} from 'components/Layout'; @@ -15,14 +14,27 @@ import {initCats, initDogs, initPigs, initJsonRpc} from './init'; import {Contacts} from 'components/Modals/Contacts'; import {HeaderDoc} from 'components/Header/HeaderDoc'; import {screenWidthMultiplier} from 'utils/screenWidthMultiplier'; -import {editorModeType, ExamplesType, SidebarDocType} from 'types'; +import { + editorModeType, + ExamplesType, + OpenApiFormatType, + SidebarDocType, + HtmlDocPanelType, +} from 'types'; import {EditorContext, JDocContext, SidebarContext, SharingContext} from 'store'; import {CurrentUrlProvider} from 'store/CurrentUrlStore'; import {onOrientationChange} from 'utils/onOrientationChange'; +import {notificationIds} from 'utils/notificationIds'; import {ErrorScreen} from 'screens/Error'; import {SharingForm} from 'components/Modals/SharingForm'; import 'react-toastify/dist/ReactToastify.css'; import {AnnouncementBar} from 'components/AnnouncementBar'; +import {convertJsight} from 'api/convertJsight'; + +import IconOpenAPI from 'assets/images/icons/openapi.svg'; +import IconHTMLDoc from 'assets/images/icons/htmldoc.svg'; +import IconContents from 'assets/images/icons/contents.svg'; + import './Editor.styles.scss'; const {isExport} = window as any; @@ -35,10 +47,13 @@ export const EditorScreen = () => { const [jsightCode, setJsightCode] = useState( key ? '' : localStorage.getItem('jsightCode') || initCats ); + const [jdocExchangeError, setJdocExchangeError] = useState(false); // left sidebar const [codeContentsSidebar] = useState(false); //documentation sidebar on the right - const [currentDocSidebar, setCurrentDocSidebar] = useState(null); + const [currentDocSidebar, setCurrentDocSidebar] = useState('htmldoc'); + const [currentOpenApiFormat, setCurrentOpenApiFormat] = useState('yaml'); + const [currentHtmlDocPanel, setCurrentHtmlDocPanel] = useState('none'); const [jdocExchange, setJdocExchange] = useState(); const [errorRow, setErrorRow] = useState(null); const [scrollToRow, setScrollToRow] = useState(false); @@ -49,6 +64,7 @@ export const EditorScreen = () => { const [error, setError] = useState(null); const [disableSharing, setDisableSharing] = useState(true); const isEditor = useMemo(() => viewMode === 'editor', [viewMode]); + const [isJdocLoading, setIsJdocLoading] = useState(false); const [isShowAnnouncementBar, setIsShowAnnouncementBar] = useState( !localStorage.getItem('announcement.bar.hidden') ); @@ -87,6 +103,15 @@ export const EditorScreen = () => { setEditorWidth(finalNewWidth); }; + useEffect(() => { + const isMobile = /Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ); + if (isMobile) { + setCurrentDocSidebar('openapi'); + } + }, []); + useEffect(() => { const changeWidth = () => { const width = getEditorWidth(screenWidth); @@ -98,16 +123,19 @@ export const EditorScreen = () => { }); useEffect(() => { - if (jsightCodeDebounced !== undefined) { + if (currentDocSidebar === 'htmldoc' && jsightCodeDebounced !== undefined) { (async () => { if (!isExport) { try { - const jdocExchange = await getJDocExchange(jsightCodeDebounced); - startTransition(() => setJdocExchange(jdocExchange)); + setIsJdocLoading(true); + const jdocExchange = await convertJsight(jsightCodeDebounced, 'jdoc-2.0'); + startTransition(() => setJdocExchange(jdocExchange as JDocType)); toast.dismiss(); setErrorRow(null); + setJdocExchangeError(false); + setIsJdocLoading(false); } catch (error) { - showEditorError(error as ErrorType, () => { + showEditorError(error as ErrorType, notificationIds.ERROR_MESSAGE_HTMLDOC_ID, () => { if (!(error as ErrorType).Line) { return; } @@ -116,6 +144,7 @@ export const EditorScreen = () => { setTimeout(() => setScrollToRow(false), 500); }); (error as ErrorType).Line && setErrorRow((error as ErrorType).Line); + setJdocExchangeError(true); } finally { localStorage.setItem('jsightCode', jsightCodeDebounced); } @@ -125,18 +154,17 @@ export const EditorScreen = () => { } })(); } - // eslint-disable-next-line - }, [jsightCodeDebounced]); + }, [currentDocSidebar, jsightCodeDebounced]); const classes = useMemo( () => clsx({ 'editor-wrapper-inner': true, - 'rules-sidebar': currentDocSidebar === 'rules', - 'content-sidebar': currentDocSidebar === 'content', + 'rules-sidebar': currentHtmlDocPanel === 'rules', + 'content-sidebar': currentHtmlDocPanel === 'content', 'code-sidebar': codeContentsSidebar, }), - [codeContentsSidebar, currentDocSidebar] + [codeContentsSidebar, currentHtmlDocPanel] ); const setInitJsightCode = (code: string) => { @@ -175,7 +203,11 @@ export const EditorScreen = () => { }; const handleCurrentDocSidebar = useCallback((sidebar: SidebarDocType) => { - setCurrentDocSidebar((prev) => (prev === sidebar ? null : sidebar)); + setCurrentDocSidebar((prev) => (prev === sidebar ? 'htmldoc' : sidebar)); + }, []); + + const handleCurrentHtmlDocPanel = useCallback((htmldocpanel: HtmlDocPanelType) => { + setCurrentHtmlDocPanel((prev) => (prev === htmldocpanel ? 'none' : htmldocpanel)); }, []); const handleJsightCode = useCallback((code: string) => setJsightCode(code), []); @@ -183,12 +215,24 @@ export const EditorScreen = () => { const handleError = useCallback((error) => setError(error), []); const handleReloadedEditor = useCallback(() => reloadedEditor(), []); + const jdocValue = useMemo( + () => ({ + jdocExchange, + jsightCode: jsightCodeDebounced, + }), + [jdocExchange, jsightCodeDebounced] + ); + const sidebarValue = useMemo( () => ({ currentDocSidebar, setCurrentDocSidebar, + currentOpenApiFormat, + setCurrentOpenApiFormat, + currentHtmlDocPanel, + setCurrentHtmlDocPanel, }), - [currentDocSidebar] + [currentDocSidebar, currentOpenApiFormat, currentHtmlDocPanel] ); const editorValue = useMemo( @@ -206,7 +250,7 @@ export const EditorScreen = () => { } return ( - + { {!isExport ? (
    {isEditor ? ( -
    + +
    + ) : ( )} @@ -271,19 +317,53 @@ export const EditorScreen = () => { - + {isEditor && (
    handleCurrentDocSidebar('content')} + onClick={() => + currentDocSidebar !== 'openapi' && handleCurrentDocSidebar('openapi') + } + className={clsx('side-panel-element', { + active: currentDocSidebar === 'openapi', + })} + > + OpenAPI OpenAPI +
    +
    handleCurrentDocSidebar('htmldoc')} + className={clsx('side-panel-element', { + active: + currentDocSidebar === 'htmldoc' && + (currentHtmlDocPanel === 'rules' || currentHtmlDocPanel === 'content'), + })} + > + HTMLDoc HTML Doc +
    +
    + currentDocSidebar !== 'openapi' && handleCurrentHtmlDocPanel('content') + } className={clsx('side-panel-element', { - active: currentDocSidebar === 'content', + active: currentHtmlDocPanel === 'content', + disabled: currentDocSidebar === 'openapi', })} + title={ + currentDocSidebar === 'openapi' ? 'Not available while in OpenAPI view' : '' + } > - Contents + Contents Contents
    )} diff --git a/src/store/JDocStore.ts b/src/store/JDocStore.ts index 79f50fb..abe0912 100644 --- a/src/store/JDocStore.ts +++ b/src/store/JDocStore.ts @@ -1,4 +1,9 @@ import {createContext} from 'react'; import {JDocType} from 'types/exchange'; -export const JDocContext = createContext({} as JDocType | undefined); +export interface JDocContextType { + jdocExchange: JDocType | undefined; + jsightCode: string; +} + +export const JDocContext = createContext({} as JDocContextType); diff --git a/src/store/SidebarStore.ts b/src/store/SidebarStore.ts index 64304f1..211aec9 100644 --- a/src/store/SidebarStore.ts +++ b/src/store/SidebarStore.ts @@ -1,9 +1,14 @@ import React, {createContext} from 'react'; -import {SidebarDocType} from 'types'; +import {OpenApiFormatType, SidebarDocType} from 'types'; +import {HtmlDocPanelType} from 'types/htmldocpanel'; interface SidebarContextInterface { currentDocSidebar: SidebarDocType; setCurrentDocSidebar: React.Dispatch>; + currentHtmlDocPanel: HtmlDocPanelType; + setCurrentHtmlDocPanel: React.Dispatch>; + currentOpenApiFormat?: OpenApiFormatType; + setCurrentOpenApiFormat?: React.Dispatch>; } export const SidebarContext = createContext({} as SidebarContextInterface); diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 7d7acf9..51354b3 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -18,6 +18,7 @@ $secondary-azure: #6acccb; $secondary-red: #c83b27; $secondary-oxley: #729682; $secondary-stroke: #dbdbe1; +$secondary-grey: #f5f5f7; /** CODE HIGHLIGHT **/ $code-rule: #54a4d3; diff --git a/src/types/converter.ts b/src/types/converter.ts new file mode 100644 index 0000000..d316c9b --- /dev/null +++ b/src/types/converter.ts @@ -0,0 +1,2 @@ +export type ConvertDestinationType = 'jdoc-2.0' | 'openapi-3.0.3'; +export type OpenApiFormatType = 'json' | 'yaml'; diff --git a/src/types/htmldocpanel.ts b/src/types/htmldocpanel.ts new file mode 100644 index 0000000..f61cd56 --- /dev/null +++ b/src/types/htmldocpanel.ts @@ -0,0 +1 @@ +export type HtmlDocPanelType = 'none' | 'content' | 'rules'; diff --git a/src/types/index.ts b/src/types/index.ts index f78b14b..1cc8d08 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,5 +2,7 @@ export * from './editor'; export * from './error'; export * from './router'; export * from './sidebar'; +export * from './htmldocpanel'; export * from './exchange'; export * from './header'; +export * from './converter'; diff --git a/src/types/sidebar.ts b/src/types/sidebar.ts index 6b3269a..72dc8d1 100644 --- a/src/types/sidebar.ts +++ b/src/types/sidebar.ts @@ -1 +1 @@ -export type SidebarDocType = 'content' | 'rules' | null; +export type SidebarDocType = 'openapi' | 'htmldoc'; diff --git a/src/utils/getError.ts b/src/utils/getError.ts index 4ae3d60..3416cb8 100644 --- a/src/utils/getError.ts +++ b/src/utils/getError.ts @@ -6,12 +6,10 @@ export const getErrorTitle = (error: ErrorType | undefined) => { }; export const getError = (error: ErrorType) => { - if (!error?.Line) { - return 'Server error, try again later'; - } - const errorMessage = error.Message; - return errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1); + return errorMessage + ? errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1) + : 'No connection'; }; export const getDefaultErrorMessages = (status: number) => { diff --git a/src/utils/notificationIds.ts b/src/utils/notificationIds.ts index 64d1071..c727452 100644 --- a/src/utils/notificationIds.ts +++ b/src/utils/notificationIds.ts @@ -2,4 +2,6 @@ export const notificationIds = { ERROR_MESSAGE_DEFAULT_ID: 1, ERROR_MESSAGE_SHARING_ID: 2, SUCCESS_MESSAGE_DEFAULT_ID: 3, + ERROR_MESSAGE_HTMLDOC_ID: 4, + ERROR_MESSAGE_OPENAPI_ID: 5, }; diff --git a/src/utils/runRequest.ts b/src/utils/runRequest.ts index dd0e169..4f9a1c1 100644 --- a/src/utils/runRequest.ts +++ b/src/utils/runRequest.ts @@ -8,13 +8,19 @@ const defaultError: ErrorType = { Status: 'Error', }; +interface RequestAddParams { + responseAsText?: boolean; + timeout?: number; + times?: number; +} + export const runRequest = async ( action: string, {body, headers, method}: RequestInit = {}, - timeout = 0, - times = 0 + addParams?: RequestAddParams ): Promise => { const fetchParams: RequestInit = {headers, body, method: method || 'POST'}; + const {responseAsText = false, timeout = 0, times = 0} = addParams || {}; let id: any; if (timeout > 0) { @@ -31,7 +37,7 @@ export const runRequest = async ( if (!window.navigator.onLine && timeout > 0) { return new Promise((resolve) => { setTimeout(() => { - resolve(runRequest(action, fetchParams, timeout, times + 1)); + resolve(runRequest(action, fetchParams, {responseAsText, timeout, times: times + 1})); }, 1000); }); } @@ -40,8 +46,12 @@ export const runRequest = async ( if (response.ok) { const text = await response.text(); try { - const json = JSON.parse(text); - return json; + if (responseAsText) { + return text; + } else { + const json = JSON.parse(text); + return json; + } } catch (err) { return text; } diff --git a/src/utils/showEditorError.tsx b/src/utils/showEditorError.tsx index 766c254..547c423 100644 --- a/src/utils/showEditorError.tsx +++ b/src/utils/showEditorError.tsx @@ -5,6 +5,7 @@ import {getError, getErrorTitle} from 'utils/getError'; import {notificationIds} from 'utils/notificationIds'; import {EditorErrorNotification} from 'components/Notifications/EditorErrorNotification'; import {IconError} from 'components/Notifications/IconError'; +import {updateMessageWithUrl} from './updateMessageWithUrl'; const {ERROR_MESSAGE_DEFAULT_ID} = notificationIds; @@ -19,19 +20,28 @@ const showErrorOptions: ToastOptions = { icon: IconError, }; -export const showEditorError = (error: ErrorType, setScrollToRow: () => void) => { +export const showEditorError = (error: ErrorType, toastId: number, setScrollToRow: () => void) => { const title = getErrorTitle(error); - const message = getError(error); + const errorMessage = getError(error); + const messageWithLink = updateMessageWithUrl(errorMessage); - if (!toast.isActive(ERROR_MESSAGE_DEFAULT_ID)) { + if (!toast.isActive(toastId)) { toast.warning( - , - showErrorOptions + , + {...showErrorOptions, toastId} ); } else { - toast.update(ERROR_MESSAGE_DEFAULT_ID, { + toast.update(toastId, { render: ( - + ), }); } diff --git a/src/utils/updateMessageWithUrl.ts b/src/utils/updateMessageWithUrl.ts new file mode 100644 index 0000000..26d3c1d --- /dev/null +++ b/src/utils/updateMessageWithUrl.ts @@ -0,0 +1,5 @@ +export const updateMessageWithUrl = (message: string) => { + const pattern = /(https?:\/\/[^\s]+)/gi; + + return message.replace(pattern, '$1'); +}; diff --git a/tests/manual-tests/!BEFORE-TESTING.md b/tests/manual-tests/!BEFORE-TESTING.md new file mode 100644 index 0000000..3942e84 --- /dev/null +++ b/tests/manual-tests/!BEFORE-TESTING.md @@ -0,0 +1,3 @@ +# Before manual testing + +1. Update all your browsers to the latest stable versions. \ No newline at end of file diff --git a/tests/manual-tests/all-features-test.jst b/tests/manual-tests/all-features-test.jst index ebe96e8..7680ccd 100644 --- a/tests/manual-tests/all-features-test.jst +++ b/tests/manual-tests/all-features-test.jst @@ -1,442 +1,442 @@ -JSIGHT 0.3 - -INFO - Title "\"Catsbook\" social network API" - Description - ( - **Catsbook** social network API. - - # API purpose - - This example uses all the features of - *JSight API 0.3* language. - - Among others: - - - one, - - two, - - three. - - And lets look on the table: - - ## The table - - | Header | Header | - | ------- | ------- | - | Cell | Cell | - | Cell | Cell | - ) - Version 1.0 - -SERVER @DEV // Developers server. - BaseUrl "192.168.0.100" - -SERVER @PROD - BaseUrl "https://{env}.catsbook.com/api/{version}/{locale}/" - -#------------------------------------------- CATS ------------------------------------------------------ - -URL /cats - - GET /* Get all - the cats. */ - Query "page=1&per_page=50&filter[size]=XXL" - { - "page": 1, - "per_page": 50, // {optional: true, max: 100} - "filter": { // {optional: true} - "size": "XXL", // {optional: true } - Cat size filter. # TODO: enum: "@catSizeEnum" - "age" : 12 // {optional: true } - Cat age filter. - } - } - - 200 // Cat’s list. - Headers - { # we shouldn't forget to add "additionalProperties: true" to JDoc Exchange Schema - "content-type": "application/json" // {const: true} - } - Body [@cat] - - 500 - { # Body omited - "ErrorMessage" : "Something is terribly wrong!" - } - - Headers - {} // {allOf: "@standardHeaders"} - - 500 - @serverError # Body omited - - POST // Create a new cat. - Description - You must be very carefull - when creating a new cat. - - Do not be disturbed! - - Request @cat - - 200 @cat - - PASTE @commonErrors - -URL /cats/{id} -( - Path - { - "id": @catId // Cat’s id. - } - - GET // Get a cat by it’s id. - Description - Returns a cat’s definition by it’s id. - - 200 @cat // Take your cat! - - 500 @serverError - - 404 empty - - PUT // Update cat completely. - - Request jsight - @cat - - 200 - Body @cat - - 500 any - - PATCH // Change cat’s mood status. - Request - { # Request and Body are omited. - "moodStatus": "happy" // {enum: ["happy", "gloomy"]} - } - - 200 - Body # Body without parameters. - { - "moodStatus": "happy" // {enum: ["happy", "gloomy"]} - } - - 500 - Body regex - /Error/ - - DELETE // Delete a cat. - Query "force=1" htmlFormEncoded - { - "force": 1 // {optional: true, enum: [0,1]} - Means, should API delete a cat if it has some friends. - } - - # we do not specify responses, which mean that any response is possible. -) - -URL /cats/friends - GET - 200 [@cat] - #TODO: @COMMON_ERRORS_DIR - -URL "/cats/enemies" - GET - 200 - [ // {type: "array", maxItems: 100} - @pig, - @dog | @pig - ] - - -URL /cats/astronauts - GET // Get all the cats-astronauts. - 200 - Body jsight - {} // {allOf: ["@cat", "@astronaut"]} - -#------------------------------------------- PETS ------------------------------------------------------ - -GET /pets // Get all pets. - Query htmlFormEncoded - @standardQuery - - 200 [@pet] - -GET /pets/{passportNumber} // Get all pets by passport number. - Path - @petPathVariables - - 200 [@pet] - -#------------------------------------------- DOGS ------------------------------------------------------ - -URL /dogs - - GET // Get all dogs (paged list). - Query - { // {allOf: "@standardQuery"} - "page": 1, - "per_page": 50, // {type: "integer", max: 100, optional: true} - "filter": { // {optional: true} - "age" : 12, // {optional: true} - Dog age filter. - "canHunt": true /* {type: "boolean", optional: true} - - Hunting ability filter. */ - } - } - - 200 - [ // {minItems: 0, maxItems: 100} - @dog - ] - - POST // Create a new dog. - Request - Headers - @standardHeaders - Body @dog - - # no responses specified — it means that any response is possible. - -GET /dogs/{id} // Get a dog by it’s id. - 200 @dog - -POST /dogs/{id} // Replace a dog completely (exact copy of PUT /dogs/{id} method). - Request - @dog - - 200 @dog - -PATCH /dogs/{id} - Request - { # Body is omited. - "moodStatus": "happy" - } - Headers - { // {allOf: "@standardHeaders"} - "content-type": "application/json" - } - - 200 // Status is successfully changed. - "OK" // {const: true} - -URL /dogs/{id} - # Path is not specified deliberately. - - PUT # Annotation is not specified. - Request - Headers - { - "content-type": "application/json" // {const: true} - } - - Body - { // {allOf: "@pet"} - "id" : "DOG-123", // {type: "string", regex: "DOG-\\d+"} - "canHunt" : false, - "maxToothLength" : 0.013 - } - - 200 @dog - -GET /extremely-wierd-endpoint - Query "myVeryStrange[way]of%20query=string\\encoding" noFormat - { - "myVeryStrange": [ - "way" - ], - "of query": "string\\encoding" - } - - 200 - @very_strange_type_123 - - -#------------------------------------------- JSON RPC --------------------------------------------------- - -URL /json-rpc-api - Protocol json-rpc-2.0 - Method getCat /* Get a cat by its id. */ - Params - { - "id": @catId // Cat's id. - } - Result - @cat - Method getAllcats // Returns all cats. - Params - { - "limit": 30, // {min: 1, max: 1000} - "offset": 0 // {min: 0} - } - Result - { - "itemsCount": 30, - "offset": 0, - "items": [@cat] - } - -#------------------------------------------- TYPES ------------------------------------------------------ - -TYPE @cat // A cat. -{ // {allOf: "@pet"} - "id" : "CAT-123", // {type: "@catId"} - Cat’s id. - "size" : "XXL", // # TODO: {enum: "@catSizeEnum"} - "tailHeight" : 0.743, // {type: "float"} - "friends" : @catList, // {optional: true} # here we check recursive types. - "enemies": { - "dogs": { // {additionalProperties: true} - @dogId: @dog // {optional: true} - } - }, - "playing": { - "likesToPlay" : true, - "prefferedMouseColor": "white" // {enum: ["white", "gray"]} - } -} - -TYPE @dog /* A dog. */ -{ // {allOf: "@pet"} - "id" : "DOG-123", // {type: "string", regex: "DOG-\\d+"} - "canHunt" : false, - "maxToothLength" : 0.013 -} - -TYPE @pig // A pig. -{ // {allOf: "@animal"} - "id": "PIG-123", - "lastWashTime" : "2006-01-02T15:04:05+07:00" // { type: "datetime"} -} - -TYPE @pet -{ // {allOf: "@animal"} - "passportNumber": 1234123212, /* {or: [ - {type: "integer"}, - {type: "string" , minLength: 10, maxLength: 10} - ]} */ - "name" : "Thomas the Great", - "email" : "thomas@catsbook.com", // {type: "email"} - "website" : "http://www.thomas-the-great.com", // {type: "uri", nullable: true} - # One pet can have several owners. - "owners" : [ // Owners - { // Owner - "id" : "550e8400-e29b-41d4-a716-446655440000", // {type: "uuid"} - Owners id. # Some text comment. - "name" : "John" - } - ] -} - -TYPE @animal -{ - "type" : @animalType, - "moodStatus" : "happy", // {type: "enum", enum: ["happy", "gloomy"]} - ### - `age` is calculated by `birthday`. - ### - "age" : 12, // {min: 0, max: 600} - "weight" : 2.5, /* {min: 0, exclusiveMinimum: true, - max: 6000, exclusiveMaximum: false} */ - "birthday" : "2006-01-02" // {type: "date"} -} - -TYPE @astronaut jsight // An astronaut. -{ // {type: "object"} - "spaceSuitSize": 4, // {min: 1, max: 5} - "salary" : 422.34, // {precision: 2} - "taxes" : 12.34 // {type: "decimal", precision: 2} -} - -TYPE @catId regex - /CAT-\d+/ - -TYPE @dogId - "DOG-12341" // {regex: "DOG-\\d+", minLength: 4, maxLength: 255} - -TYPE @animalType jsight - "cat" /* {enum: [ - "cat", - "dog", - "pig", - "frog" - ]} - */ - -### -TODO: - "cat" /* - {enum: [ - // Domestic - "cat", // Cat - "dog", // Dog - "pig", // Pig - /* Wild */ - "frog" /* Frog */ - ]} - */ -### - -TYPE @very_strange_type_123 jsight -{ - "nullField" : null, - "wierdEnum" : 1 , // {enum: [1, 2, "a", "b", true, false, null]} - "anyField" : {}, // {type: "any"} - "astronautHtmlForm" : "spaceSuitSize=4&salary=422.34&taxes=12.34" # TODO: /* - # {serializeFormat: "htmlFormEncoded", serializedType: "@astronaut"} */ -} - -TYPE @serverError -{ - "errorCode" : 123, - "errorMessage": "All is bad" -} - -TYPE @standardQuery -{ - "debug": 1 // {const: true, optional: true} -} - -TYPE @standardHeaders -{ - "Authorization": "bearer asdfasdfasdfasdf" // {optional: true} -} - -### -Here we check block comments. -TODO: We need more perfect tests of block comments. -### - -TYPE @petPathVariables -{ - "passportNumber": 12312312 -} - -TYPE @catList -[ - @cat -] - -#------------------------------------------------- ENUMS ----------------------------------------------- - -### -TODO: -ENUM @catSizeEnum // Cat’s sizes. -[ - // Small cats. - "S", - "M", - // Normal cats. - "L", - /* Huge - cats */ - "XL", - "XXL" -] -### - - -#--------------------------------------------------- MACROS ------------------------------------ -MACRO @commonErrors -( - 404 - Body regex - /Bad request./ - 500 any +JSIGHT 0.3 + +INFO + Title "\"Catsbook\" social network API (jtest)" + Description + ( + **Catsbook** social network API. + + # API purpose + + This example uses all the features of + *JSight API 0.3* language. + + Among others: + + - one, + - two, + - three. + + And lets look on the table: + + ## The table + + | Header | Header | + | ------- | ------- | + | Cell | Cell | + | Cell | Cell | + ) + Version 1.0 + +SERVER @DEV // Developers server. + BaseUrl "192.168.0.100" + +SERVER @PROD + BaseUrl "https://{env}.catsbook.com/api/{version}/{locale}/" + +#------------------------------------------- CATS ------------------------------------------------------ + +URL /cats + + GET /* Get all + the cats. */ + Query "page=1&per_page=50&filter[size]=XXL" + { + "page": 1, + "per_page": 50, // {optional: true, max: 100} + "filter": { // {optional: true} + "size": "XXL", // {optional: true } - Cat size filter. # TODO: enum: "@catSizeEnum" + "age" : 12 // {optional: true } - Cat age filter. + } + } + + 200 // Cat’s list. + Headers + { # we shouldn't forget to add "additionalProperties: true" to JDoc Exchange Schema + "content-type": "application/json" // {const: true} + } + Body [@cat] + + 500 + { # Body omited + "ErrorMessage" : "Something is terribly wrong!" + } + + Headers + {} // {allOf: "@standardHeaders"} + + 500 + @serverError # Body omited + + POST // Create a new cat. + Description + You must be very carefull + when creating a new cat. + + Do not be disturbed! + + Request @cat + + 200 @cat + + PASTE @commonErrors + +URL /cats/{id} +( + Path + { + "id": @catId // Cat’s id. + } + + GET // Get a cat by it’s id. + Description + Returns a cat’s definition by it’s id. + + 200 @cat // Take your cat! + + 500 @serverError + + 404 empty + + PUT // Update cat completely. + + Request jsight + @cat + + 200 + Body @cat + + 500 any + + PATCH // Change cat’s mood status. + Request + { # Request and Body are omited. + "moodStatus": "happy" // {enum: ["happy", "gloomy"]} + } + + 200 + Body # Body without parameters. + { + "moodStatus": "happy" // {enum: ["happy", "gloomy"]} + } + + 500 + Body regex + /Error/ + + DELETE // Delete a cat. + Query "force=1" htmlFormEncoded + { + "force": 1 // {optional: true, enum: [0,1]} - Means, should API delete a cat if it has some friends. + } + + # we do not specify responses, which mean that any response is possible. +) + +URL /cats/friends + GET + 200 [@cat] + #TODO: @COMMON_ERRORS_DIR + +URL "/cats/enemies" + GET + 200 + [ // {type: "array", maxItems: 100} + @pig, + @dog | @pig + ] + + +URL /cats/astronauts + GET // Get all the cats-astronauts. + 200 + Body jsight + {} // {allOf: ["@cat", "@astronaut"]} + +#------------------------------------------- PETS ------------------------------------------------------ + +GET /pets // Get all pets. + Query htmlFormEncoded + @standardQuery + + 200 [@pet] + +GET /pets/{passportNumber} // Get all pets by passport number. + Path + @petPathVariables + + 200 [@pet] + +#------------------------------------------- DOGS ------------------------------------------------------ + +URL /dogs + + GET // Get all dogs (paged list). + Query + { // {allOf: "@standardQuery"} + "page": 1, + "per_page": 50, // {type: "integer", max: 100, optional: true} + "filter": { // {optional: true} + "age" : 12, // {optional: true} - Dog age filter. + "canHunt": true /* {type: "boolean", optional: true} + - Hunting ability filter. */ + } + } + + 200 + [ // {minItems: 0, maxItems: 100} + @dog + ] + + POST // Create a new dog. + Request + Headers + @standardHeaders + Body @dog + + # no responses specified — it means that any response is possible. + +GET /dogs/{id} // Get a dog by it’s id. + 200 @dog + +POST /dogs/{id} // Replace a dog completely (exact copy of PUT /dogs/{id} method). + Request + @dog + + 200 @dog + +PATCH /dogs/{id} + Request + { # Body is omited. + "moodStatus": "happy" + } + Headers + { // {allOf: "@standardHeaders"} + "content-type": "application/json" + } + + 200 // Status is successfully changed. + "OK" // {const: true} + +URL /dogs/{id} + # Path is not specified deliberately. + + PUT # Annotation is not specified. + Request + Headers + { + "content-type": "application/json" // {const: true} + } + + Body + { // {allOf: "@pet"} + "id" : "DOG-123", // {type: "string", regex: "DOG-\\d+"} + "canHunt" : false, + "maxToothLength" : 0.013 + } + + 200 @dog + +GET /extremely-wierd-endpoint + Query "myVeryStrange[way]of%20query=string\\encoding" noFormat + { + "myVeryStrange": [ + "way" + ], + "of query": "string\\encoding" + } + + 200 + @very_strange_type_123 + + +#------------------------------------------- JSON RPC --------------------------------------------------- + +URL /json-rpc-api + Protocol json-rpc-2.0 + Method getCat /* Get a cat by its id. */ + Params + { + "id": @catId // Cat's id. + } + Result + @cat + Method getAllcats // Returns all cats. + Params + { + "limit": 30, // {min: 1, max: 1000} + "offset": 0 // {min: 0} + } + Result + { + "itemsCount": 30, + "offset": 0, + "items": [@cat] + } + +#------------------------------------------- TYPES ------------------------------------------------------ + +TYPE @cat // A cat. +{ // {allOf: "@pet"} + "id" : "CAT-123", // {type: "@catId"} - Cat’s id. + "size" : "XXL", // # TODO: {enum: "@catSizeEnum"} + "tailHeight" : 0.743, // {type: "float"} + "friends" : @catList, // {optional: true} # here we check recursive types. + "enemies": { + "dogs": { // {additionalProperties: true} + @dogId: @dog // {optional: true} + } + }, + "playing": { + "likesToPlay" : true, + "prefferedMouseColor": "white" // {enum: ["white", "gray"]} + } +} + +TYPE @dog /* A dog. */ +{ // {allOf: "@pet"} + "id" : "DOG-123", // {type: "string", regex: "DOG-\\d+"} + "canHunt" : false, + "maxToothLength" : 0.013 +} + +TYPE @pig // A pig. +{ // {allOf: "@animal"} + "id": "PIG-123", + "lastWashTime" : "2006-01-02T15:04:05+07:00" // { type: "datetime"} +} + +TYPE @pet +{ // {allOf: "@animal"} + "passportNumber": 1234123212, /* {or: [ + {type: "integer"}, + {type: "string" , minLength: 10, maxLength: 10} + ]} */ + "name" : "Thomas the Great", + "email" : "thomas@catsbook.com", // {type: "email"} + "website" : "http://www.thomas-the-great.com", // {type: "uri", nullable: true} + # One pet can have several owners. + "owners" : [ // Owners + { // Owner + "id" : "550e8400-e29b-41d4-a716-446655440000", // {type: "uuid"} - Owners id. # Some text comment. + "name" : "John" + } + ] +} + +TYPE @animal +{ + "type" : @animalType, + "moodStatus" : "happy", // {type: "enum", enum: ["happy", "gloomy"]} + ### + `age` is calculated by `birthday`. + ### + "age" : 12, // {min: 0, max: 600} + "weight" : 2.5, /* {min: 0, exclusiveMinimum: true, + max: 6000, exclusiveMaximum: false} */ + "birthday" : "2006-01-02" // {type: "date"} +} + +TYPE @astronaut jsight // An astronaut. +{ // {type: "object"} + "spaceSuitSize": 4, // {min: 1, max: 5} + "salary" : 422.34, // {precision: 2} + "taxes" : 12.34 // {type: "decimal", precision: 2} +} + +TYPE @catId regex + /CAT-\d+/ + +TYPE @dogId + "DOG-12341" // {regex: "DOG-\\d+", minLength: 4, maxLength: 255} + +TYPE @animalType jsight + "cat" /* {enum: [ + "cat", + "dog", + "pig", + "frog" + ]} + */ + +### +TODO: + "cat" /* + {enum: [ + // Domestic + "cat", // Cat + "dog", // Dog + "pig", // Pig + /* Wild */ + "frog" /* Frog */ + ]} + */ +### + +TYPE @very_strange_type_123 jsight +{ + "nullField" : null, + "wierdEnum" : 1 , // {enum: [1, 2, "a", "b", true, false, null]} + "anyField" : {}, // {type: "any"} + "astronautHtmlForm" : "spaceSuitSize=4&salary=422.34&taxes=12.34" # TODO: /* + # {serializeFormat: "htmlFormEncoded", serializedType: "@astronaut"} */ +} + +TYPE @serverError +{ + "errorCode" : 123, + "errorMessage": "All is bad" +} + +TYPE @standardQuery +{ + "debug": 1 // {const: true, optional: true} +} + +TYPE @standardHeaders +{ + "Authorization": "bearer asdfasdfasdfasdf" // {optional: true} +} + +### +Here we check block comments. +TODO: We need more perfect tests of block comments. +### + +TYPE @petPathVariables +{ + "passportNumber": 12312312 +} + +TYPE @catList +[ + @cat +] + +#------------------------------------------------- ENUMS ----------------------------------------------- + +### +TODO: +ENUM @catSizeEnum // Cat’s sizes. +[ + // Small cats. + "S", + "M", + // Normal cats. + "L", + /* Huge + cats */ + "XL", + "XXL" +] +### + + +#--------------------------------------------------- MACROS ------------------------------------ +MACRO @commonErrors +( + 404 + Body regex + /Bad request./ + 500 any ) \ No newline at end of file diff --git a/tests/manual-tests/code-view-tests/code-view-tests.jst b/tests/manual-tests/code-view-tests/code-view-tests.jst index 6f74b60..2af577f 100644 --- a/tests/manual-tests/code-view-tests/code-view-tests.jst +++ b/tests/manual-tests/code-view-tests/code-view-tests.jst @@ -376,7 +376,7 @@ TYPE @testAllOfInside } } -TYPE @testAllOfRedundandComma /* Check that there is no redundand comma after last @cat property. +TYPE @testAllOfRedundandComma /* Check that there is no redundand comma after the last property. TODO: add to auto tests. */ {} // {allOf: "@allOfBase"} diff --git a/tests/manual-tests/common-exported-html-ui-tests.md b/tests/manual-tests/common-exported-html-ui-tests.md index f640571..6179eff 100644 --- a/tests/manual-tests/common-exported-html-ui-tests.md +++ b/tests/manual-tests/common-exported-html-ui-tests.md @@ -1,59 +1,59 @@ -# Run all the tests for editor view - -1. Annotations tests -2. Code View Tests. -3. Directive TYPE tests. -4. Frontend load tests. -5. HTTP-method tests. -6. JSON-RPC tests. -7. INFO and SERVER tests. -8. Common Syntax tests. -9. Markdown tests. -10. Details card tests. -11. Table View tests. -12. Settings tests. - -# Contents Sidebar tests - -1. Check positioning after item click. -2. Check that after click on some content item you can refresh page by Ctrl+Shift+R. Especially - check it on paths with parameters, e. g. `/cats/{id}`. -3. Check that Contents works fine when there are a huge amount of items (vertical scroll must work). - -# Logo - -1. Check that "Powered by JSight" panel click directs to https://jsight.io/ (must be opened in the - new tab). - -# Upload - -1. Review all resources, which are uploaded during opening the HTML file. Only fonts and images must - be loaded. No code, no css (both code and css must be embedded in the file). - -## Virtual Scroll - -### Check smooth scroll up and down - -### Check scroll up and down using Pg Up, Pg Down, Cursor Down Arrow, Cursor Up Arrow - -### Schema section must remember its state - -1. Change something in doc field, e. g.: - - schema section mode (e. g. Expand Types, Table View, Code View); - - expand some type; - - switch to some method (e. g. from GET to POST). -2. Scroll up or down and come back. -3. Check that schema section is in the same state as it was before scroll. - -### Servers section must remember its state - -1. Open Servers section. -2. Scroll down deeply and come back. -3. Check Servers section is still opened. - -### Details card - -1. Check that you can close details card using either cross-mark near the top right corner of card - or with the cross-mark at the top right corner of rendered document panel. -2. Check that these two cross-marks (see above) do not interfere each other when scrolling document +# Run all the tests for editor view + +1. Annotations tests +2. Code View Tests. +3. Directive TYPE tests. +4. Frontend load tests. +5. HTTP-method tests. +6. JSON-RPC tests. +7. INFO and SERVER tests. +8. Common Syntax tests. +9. Markdown tests. +10. Details card tests. +11. Table View tests. +12. Settings tests. + +# Contents Sidebar tests + +1. Check positioning after item click. +2. Check that after click on some content item you can refresh page by Ctrl+Shift+R. Especially + check it on paths with parameters, e. g. `/cats/{id}`. +3. Check that Contents works fine when there are a huge amount of items (vertical scroll must work). + +# Logo + +1. Check that "Powered by JSight" panel click directs to https://jsight.io/ (must be opened in the + new tab). + +# Upload + +1. Review all resources, which are uploaded during opening the HTML file. Only fonts and images must + be loaded. No code, no css (both code and css must be embedded in the file). + +## Virtual Scroll + +### Check smooth scroll up and down + +### Check scroll up and down using Pg Up, Pg Down, Cursor Down Arrow, Cursor Up Arrow + +### Schema section must remember its state + +1. Change something in doc field, e. g.: + - schema section mode (e. g. Expand Types, Table View, Code View); + - expand some type; + - switch to some method (e. g. from GET to POST). +2. Scroll up or down and come back. +3. Check that schema section is in the same state as it was before scroll. + +### Servers section must remember its state + +1. Open Servers section. +2. Scroll down deeply and come back. +3. Check Servers section is still opened. + +### Details card + +1. Check that you can close details card using either cross-mark near the top right corner of card + or with the cross-mark at the top right corner of rendered document panel. +2. Check that these two cross-marks (see above) do not interfere each other when scrolling document up and down. \ No newline at end of file diff --git a/tests/manual-tests/common-syntax-test.jst b/tests/manual-tests/common-syntax-test.jst index bc36c93..a0301c6 100644 --- a/tests/manual-tests/common-syntax-test.jst +++ b/tests/manual-tests/common-syntax-test.jst @@ -2,7 +2,7 @@ JSIGHT 0.3 GET /test-directive/parameters-escaping Query "check quotation mark escaping \" and backslash escaping \\ " - null + {} GET /test-directive/explicit-boundaries Description diff --git a/tests/manual-tests/common-ui-tests.md b/tests/manual-tests/common-ui-tests.md index c492ea2..bd0dbf1 100644 --- a/tests/manual-tests/common-ui-tests.md +++ b/tests/manual-tests/common-ui-tests.md @@ -73,8 +73,10 @@ Code content should not be changed. something in the code. Check that documentation stays in the same place after refresh and do not scroll back to the previous position. 6. Check that Contents works fine when there are a huge amount of items (vertical scroll must work). +7. Try to change a JSight code, when the Contents tab is opened, and check, that the HTML doc is + updated. -# Give us a star popup panel +## Give us a star popup panel 1. Check the github link (must be opened in the new tab). 2. Close the popup. diff --git a/tests/manual-tests/directives-tests/directive-title-test.jst b/tests/manual-tests/directives-tests/directive-title-test.jst index 87801f4..42d0a7a 100644 --- a/tests/manual-tests/directives-tests/directive-title-test.jst +++ b/tests/manual-tests/directives-tests/directive-title-test.jst @@ -8,6 +8,6 @@ JSIGHT 0.3 INFO - Title "My API" + Title "My API (jtest)" # always use `jtest` in test titles for the sake of statistics GET /cats \ No newline at end of file diff --git a/tests/manual-tests/directives-tests/directives-info-and-server-tests.jst b/tests/manual-tests/directives-tests/directives-info-and-server-tests.jst index 4f3e7a2..60dd829 100644 --- a/tests/manual-tests/directives-tests/directives-info-and-server-tests.jst +++ b/tests/manual-tests/directives-tests/directives-info-and-server-tests.jst @@ -1,22 +1,22 @@ -JSIGHT 0.3 - -INFO - Title "Check API title here" - Version "Check API Version here (1.0)." - Description - Check API description here. - -SERVER @testCommon - BaseUrl "https://pets.com/api/1.0" - -SERVER @testAnnotation // Check server's annotation. - BaseUrl "https://192.168.0.100/1.0" - -SERVER @testMutilineAnnotation /* Check server's - multiline annotation. */ - BaseUrl "https://192.168.0.100/1.0" - -SERVER @testLongAnnotation /* Check server's - multiline annotation which is so long, so extremely long. - so perfectly long, so longly long. */ +JSIGHT 0.3 + +INFO + Title "Check API title here (jtest)" # always use `jtest` in test titles for the sake of statistics + Version "Check API Version here (1.0)." + Description + Check API description here. + +SERVER @testCommon + BaseUrl "https://pets.com/api/1.0" + +SERVER @testAnnotation // Check server's annotation. + BaseUrl "https://192.168.0.100/1.0" + +SERVER @testMutilineAnnotation /* Check server's + multiline annotation. */ + BaseUrl "https://192.168.0.100/1.0" + +SERVER @testLongAnnotation /* Check server's + multiline annotation which is so long, so extremely long. + so perfectly long, so longly long. */ BaseUrl "https://192.168.0.100/1.0" \ No newline at end of file diff --git a/tests/manual-tests/documentation-panel-tests/bottom-margin-test-1.jst b/tests/manual-tests/documentation-panel-tests/bottom-margin-test-1.jst index 9121661..216eef2 100644 --- a/tests/manual-tests/documentation-panel-tests/bottom-margin-test-1.jst +++ b/tests/manual-tests/documentation-panel-tests/bottom-margin-test-1.jst @@ -1,7 +1,7 @@ JSIGHT 0.3 INFO - Title "Bottom margin tests" + Title "Bottom margin tests (jtest)" # always use `jtest` in test titles for the sake of statistics Description ( Remove directives one by one from the bottom and check their bottom margin. @@ -46,16 +46,13 @@ INFO SERVER @checkServerBottomMargin // Check SERVER bottom margin BaseUrl http://check-BaseUrl-bottom-margin -GET /check-method-bottom-margin/{var} +GET /check-method-bottom-margin/ Description ( Check method description bottom margin. ) -Path - { - "var": "Check path bottom margin" // Check path bottom margin. - } +GET /check-path-bottom-margin/{var} Query { "Check query bottom margin": 0 diff --git a/tests/manual-tests/error-tests/error-messages-test.jst b/tests/manual-tests/error-tests/error-messages-test.jst new file mode 100644 index 0000000..f0da4c8 --- /dev/null +++ b/tests/manual-tests/error-tests/error-messages-test.jst @@ -0,0 +1,196 @@ +### + +Uncomment pieces of code and see the corresponding error message: +1. Check the message text. +2. Check the message link to documentation (if exists). + +### + +# GET / # DirectiveJSIGHTShouldBeTheFirst +# JSIGHT 0.4 # UnsupportedVersion +JSIGHT 0.3 +# JSIGHT 0.3 + +INFO + Title "Error test (jtest)" # always use `jtest` in test titles for the sake of statistics + # Title "bbb" # NotUniqueDirective + +SERVER @a + BaseUrl "url" + # BaseUrl "url" # DirectiveBaseURLAlreadyDefined + +# GET # Path not found + + +URL /1 +# URL /1 # NotUniquePath + +# INFO # DirectiveINFOGottaBeOnlyOneTime + +# INCLUDE /par # IncludeRootErr +# INCLUDE ../ # IncludeUpErr +# INCLUDE one\two # IncludeSeparatorErr + + +# Body @scalar # incorrect directive context + +GET /{id} + Path + { + "id": 1 + } + # Request empty // annotation is not allowed + # Tags # RequiredParameterNotSpecified + # Tags @tag # Tag not found + # 200 @not_found # Type not found + # 200 @scalar jsight # CannotUseTheTypeAndSchemaNotationParametersTogether + 200 @scalar + # Body @scalar # ParametersAreForbiddenForTheDirective + # 201 @scalar @scalar # ParametersIsAlreadyDefined + ### + Headers + @scalar # BodyMustBeObject + ### + # ) # ThereIsNoExplicitContextForClosure + # () # ApartFromTheOpeningParenthesis + ### + Description # DescriptionIsEmpty + ( + ) + ### + +# DELETE /{id}/{id} # PathParameterIsDuplicatedInThePath + +# GET /{name} + +### +TYPE @scalar # DuplicateNames + "" +### + +POST /{id}/{par2} + Path + { + # "id": 1, # The parameter %q has already been defined earlier + "par2": 2 + } + +### +GET /1 + Path + # @scalar # PathObjectErr + # {} # PathEmptyErr + # {} // {additionalProperties: true} # PathAdditionalPropertiesErr + # {} // {nullable: true} # PathNullableErr +### + +# GET /{} # PathEmptyParameter + + + + +# ErrUserTypeFound TODO: test for ErrUserTypeFound + + +TYPE @test +{ # // {additionalProperties: "unknown_type"} # ErrUnknownJSchemaType + # "key": "value" // {type: "unknown_type"} # ErrUnknownValueOfTheTypeRule + # "recursion": @test # ErrInfinityRecursionDetected + # "or": 0 // {or: [ {type: "integer", min: 1}, {type: "string"} ]} # ErrOrRuleSetValidation + # bad_key # ErrInvalidCharacter + # "key": bad_value # ErrInvalidCharacter + # "enum": 1 // {enum: [invalid_value]} # ErrInvalidCharacter + # "regex": "abc" // {regex: invalid_regex} # ErrInvalidCharacter + # "key": "value" // {wrong key} # ErrInvalidCharacterInAnnotationObjectKey + # "key": "value" // {type: "string", type: "string"} # ErrDuplicateRule + # "duplicate-key": 1, "duplicate-key": 2 # ErrDuplicateKeysInSchema + # "key": "value" // {"invalid_rule": ""} # ErrUnknownRule + # "key": 42 // {min: 43, max: 44} # ErrConstraintValidation + # "key": 1.23 // {precision: 1} # ErrConstraintValidation + # "key": "value" // {minLength: 10} # ErrConstraintStringLengthValidation + # "key": "value" // {minLength: "invalid_value"} # ErrInvalidValueOfConstraint + # "key": 1 // {precision: 0} # ErrZeroPrecision + # "key": "value" // {enum: ["one"]} # ErrDoesNotMatchAnyOfTheEnumValues + # "key": "value" // {regex: "\\d"} # ErrDoesNotMatchRegularExpression + # "key": 0 // {min: 0, max: -1} # ErrValueOfOneConstraintGreaterOrEqualToAnother + # "key": "" // {rule: @scalar} # ErrIncorrectRuleValueType + # "key": {"foo": "bar"} // {const: true} # ErrIncorrectRuleForSeveralNode + # "key": 1 // {or: [ {min: []}, {type: "string"} ]} # ErrLiteralValueExpected + # "key": "value" // {enum: [[]]} # ErrIncorrectArrayItemTypeInEnumRule + # "key": "value" // {enum: ["value", "value"]} # ErrDuplicationInEnumRule + # "key": "value" // {or: "value"} # ErrArrayWasExpectedInOrRule + # "key": "value" // {or: []} # ErrEmptyArrayInOrRule + # "key": "value" // {or: ["string"]} # ErrOneElementInArrayInOrRule + # "key": "value" // {or: [1,2]} # ErrIncorrectArrayItemTypeInOrRule + # "key": "value" // {or: [{}]} # ErrEmptyRuleSet + # "key": "value" // {or: [{"minLength": 1}]} # ErrTypIsRequiredInsideOr + # "key": @scalar // {minLength: 2} # ErrCannotSpecifyOtherRulesWithTypeReference + # "key": 1 // {or: ["integer", "string"], min: 1} # ErrShouldBeNoOtherRulesInSetWithOr + # "key": 1 // {enum: [1], min: 0} # ErrShouldBeNoOtherRulesInSetWithEnum + # "key": 1 // {type: "any", min: 0} # ErrShouldBeNoOtherRulesInSetWithAny + # "key": {} // {type: "@scalar"} # ErrInvalidChildNodeTogetherWithTypeReference + # "key": {} // {or: ["@scalar", "@scalar"]} # ErrInvalidChildNodeTogetherWithOrRule + # "key": 1 // {exclusiveMinimum: true} # ErrConstraintMinNotFound + # "key": 2 // {exclusiveMaximum: true} # ErrConstraintMaxNotFound + # "key": 3 // {type: "integer", enum: [1,2]} # ErrInvalidValueInTheTypeRule + # "key": 3 // {type: "decimal"} # ErrNotFoundRulePrecision + # "key": 3 // {type: "mixed"} # ErrNotFoundRuleOr + # "key": 3 // {type: "enum"} # ErrNotFoundRuleEnum + # "key": 3 // {type: "string"} # ErrIncompatibleTypes + # "key": 1 // {precision: 1} # ErrUnexpectedConstraint + # "key": [] // {minItems: 1} # ErrIncorrectConstraintValueForEmptyArray + # "key": 1 // {or: ["@string1", "@string2"]} # ErrIncorrectUserType + # "key": @not_found # ErrUserTypeNotFoun + # @any1: "" # ErrInvalidKeyShortcutType + # "key": 1 // {enum: true} + # "key": "550e8400e29b41d4a716446655440000 " // {type: "uuid"} # ErrUUIDLength + # "key": "z50e8400-e29b-41d4-a716-446655440000" // {type: "uuid"} # ErrUUIDFormat + # "key": "value" "key" // invalide character after the object property +} + + +TYPE @string1 + "" + +TYPE @string2 + "" + + +TYPE @any1 +[ # // {type: "any"} # ErrInvalidNestedElementsFoundForTypeAny + 1 +] + + +TYPE @allOfWrong +{ # // {allOf: []} # ErrTypeNameNotFoundInAllOfRule +} + +TYPE @allOf +{ # // {allOf: "42"} # ErrInvalidSchemaNameInAllOfRule +} + +TYPE @allOfRecursion +{ // # {allOf: "@allOfRecursion"} # ErrUnacceptableRecursionInAllOfRule +} + +TYPE @allOfScalar +{ # // {allOf: "@scalar"} +} + +TYPE @scalar + "" // # {optional: true} # ErrRuleOptionalAppliesOnlyToObjectProperties + +TYPE @minItems +[ # // {minItems: 3} # ErrConstraintMinItemsValidation + 1, 2 +] + +TYPE @cat +# ( # ContextNotClosed + { + "id": 1 + } + +# TYPE @endOfFile # ErrUnexpectedEOF +# { diff --git a/tests/manual-tests/wrong-rules-conjunctions-tests.jst b/tests/manual-tests/error-tests/wrong-rules-conjunctions-tests.jst similarity index 94% rename from tests/manual-tests/wrong-rules-conjunctions-tests.jst rename to tests/manual-tests/error-tests/wrong-rules-conjunctions-tests.jst index 3f9e05a..cb86690 100644 --- a/tests/manual-tests/wrong-rules-conjunctions-tests.jst +++ b/tests/manual-tests/error-tests/wrong-rules-conjunctions-tests.jst @@ -1,1683 +1,1683 @@ -JSIGHT 0.3 - -INFO - Description - Delete types one by one and check that every type raises an - error in a right line. - - Version 1.0 - -# Object - -TYPE @testWrongRules1 -{ - "object": {} /* {type: "object", - enum: ["white", "black"] - }*/ -} - -TYPE @testWrongRules2 -{ - "object": {} /* {type: "object", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules3 -{ - "object": {} /* {type: "object", - minItems: 0, - }*/ -} - -TYPE @testWrongRules4 -{ - "object": {} /* {type: "object", - regex: "^[A-Za-z]+$", - }*/ -} - -TYPE @testWrongRules5 -{ - "object": {} /* {type: "object", - maxLength: 100 - }*/ -} - -TYPE @testWrongRules6 -{ - "object": {} /* {type: "object", - minLength: 0, - }*/ -} - -TYPE @testWrongRules7 -{ - "object": {} /* {type: "object", - precision: 2, - }*/ -} - -TYPE @testWrongRules8 -{ - "object": {} /* {type: "object", - max: 1, - }*/ -} - -TYPE @testWrongRules9 -{ - "object": {} /* {type: "object", - min: 0, - }*/ -} - -TYPE @testWrongRules10 -{ - "object": {} /* {type: "object", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules11 -{ - "object": {} /* {type: "object", - const: true - }*/ -} - -# array -TYPE @testWrongRules12 -{ - "array" : [ /* {type: "array", - const: true, - }*/ - "item" - ] -} - -TYPE @testWrongRules13 -{ - "array" : [ /* {type: "array", - min: 0, - }*/ - "item" - ] -} - -TYPE @testWrongRules14 -{ - "array" : [ /* {type: "array", - max: 1, - }*/ - "item" - ] -} - -TYPE @testWrongRules15 -{ - "array" : [ /* {type: "array", - exclusiveMinimum: true, - }*/ - "item" - ] -} - -TYPE @testWrongRules16 -{ - "array" : [ /* {type: "array", - exclusiveMaximum: true, - }*/ - "item" - ] -} - -TYPE @testWrongRules17 -{ - "array" : [ /* {type: "array", - precision: 2, - }*/ - "item" - ] -} - -TYPE @testWrongRules18 -{ - "array" : [ /* {type: "array", - minLength: 0, - }*/ - "item" - ] -} - -TYPE @testWrongRules19 -{ - "array" : [ /* {type: "array", - maxLength: 100, - }*/ - "item" - ] -} - -TYPE @testWrongRules20 -{ - "array" : [ /* {type: "array", - regex: "^[A-Za-z]+$", - }*/ - "item" - ] -} - -TYPE @testWrongRules21 -{ - "array" : [ /* {type: "array", - or: [{type: "string"}, {type: "integer"}], - }*/ - "item" - ] -} - -TYPE @testWrongRules22 -{ - "array" : [ /* {type: "array", - additionalProperties: true, - }*/ - "item" - ] -} - -TYPE @testWrongRules23 -{ - "array" : [ /* {type: "array", - allOf: "@cat", - }*/ - "item" - ] -} - -TYPE @testWrongRules24 -{ - "array" : [ /* {type: "array", - enum: ["white", "black"] - }*/ - "item" - ] -} - -# integer - -TYPE @testWrongRules25 -{ - "integer": 1 /* {type: "integer", - precision: 2, - }*/ -} - -TYPE @testWrongRules26 -{ - "integer": 1 /* {type: "integer", - minLength: 0, - }*/ -} - -TYPE @testWrongRules27 -{ - "integer": 1 /* {type: "integer", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules28 -{ - "integer": 1 /* {type: "integer", - regex: "^[A-Za-z]+$", - }*/ -} - -TYPE @testWrongRules29 -{ - "integer": 1 /* {type: "integer", - minItems: 0, - }*/ -} - -TYPE @testWrongRules30 -{ - "integer": 1 /* {type: "integer", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules31 -{ - "integer": 1 /* {type: "integer", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules32 -{ - "integer": 1 /* {type: "integer", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules33 -{ - "integer": 1 /* {type: "integer", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules34 -{ - "integer": 1 /* {type: "integer", - enum: ["white", "black"] - }*/ -} - -# float - -TYPE @testWrongRules35 -{ - "float": 1.2 /* {type: "float", - precision: 1 - }*/ -} - -TYPE @testWrongRules36 -{ - "float": 1.2 /* {type: "float", - minLength: 0 - }*/ -} - -TYPE @testWrongRules37 -{ - "float": 1.2 /* {type: "float", - maxLength: 100 - }*/ -} - -TYPE @testWrongRules38 -{ - "float": 1.2 /* {type: "float", - regex: "^[A-Za-z]+$" - }*/ -} - -TYPE @testWrongRules39 -{ - "float": 1.2 /* {type: "float", - minItems: 0 - }*/ -} - -TYPE @testWrongRules40 -{ - "float": 1.2 /* {type: "float", - maxItems: 10 - }*/ -} - -TYPE @testWrongRules41 -{ - "float": 1.2 /* {type: "float", - or: [{type: "string"}, {type: "float"}] - }*/ -} - -TYPE @testWrongRules42 -{ - "float": 1.2 /* {type: "float", - additionalProperties: true - }*/ -} - -TYPE @testWrongRules43 -{ - "float": 1.2 /* {type: "float", - allOf: "@cat" - }*/ -} - -TYPE @testWrongRules44 -{ - "float": 1.2 /* {type: "float", - enum: [1.2, 1.3] - }*/ -} - -# decimal - -TYPE @testWrongRules45 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - minLength: 0, - }*/ -} - -TYPE @testWrongRules46 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - maxLength: 100, - }*/ -} - -TYPE @testWrongRules47 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - regex: "^[A-Za-z]+$", - }*/ -} - -TYPE @testWrongRules48 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - minItems: 0, - }*/ -} - -TYPE @testWrongRules49 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - maxItems: 10, - }*/ -} - -TYPE @testWrongRules50 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules51 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules52 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules53 -{ - "decimal": 1.23 /* {type: "decimal", precision: 2, - enum: ["white", "black"] - }*/ -} - -# boolean - -TYPE @testWrongRules54 -{ - "boolean": true /* {type: "boolean", - min: 0, - }*/ -} - -TYPE @testWrongRules55 -{ - "boolean": true /* {type: "boolean", - max: 1, - }*/ -} - -TYPE @testWrongRules56 -{ - "boolean": true /* {type: "boolean", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules57 -{ - "boolean": true /* {type: "boolean", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules58 -{ - "boolean": true /* {type: "boolean", - precision: 2, - }*/ -} - -TYPE @testWrongRules59 -{ - "boolean": true /* {type: "boolean", - minLength: 0, - }*/ -} - -TYPE @testWrongRules60 -{ - "boolean": true /* {type: "boolean", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules61 -{ - "boolean": true /* {type: "boolean", - regex: "^[A-Za-z]+$", - }*/ -} - -TYPE @testWrongRules62 -{ - "boolean": true /* {type: "boolean", - minItems: 0, - }*/ -} - -TYPE @testWrongRules63 -{ - "boolean": true /* {type: "boolean", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules64 -{ - "boolean": true /* {type: "boolean", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules65 -{ - "boolean": true /* {type: "boolean", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules66 -{ - "boolean": true /* {type: "boolean", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules67 -{ - "boolean": true /* {type: "boolean", - enum: ["white", "black"] - }*/ -} - -# string - -TYPE @testWrongRules68 -{ - "string": "value" /* {type: "string", - min: 0, - }*/ -} - -TYPE @testWrongRules69 -{ - "string": "value" /* {type: "string", - max: 1, - }*/ -} - -TYPE @testWrongRules70 -{ - "string": "value" /* {type: "string", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules71 -{ - "string": "value" /* {type: "string", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules72 -{ - "string": "value" /* {type: "string", - precision: 2, - }*/ -} - -TYPE @testWrongRules73 -{ - "string": "value" /* {type: "string", - minItems: 0, - }*/ -} - -TYPE @testWrongRules74 -{ - "string": "value" /* {type: "string", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules75 -{ - "string": "value" /* {type: "string", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules76 -{ - "string": "value" /* {type: "string", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules77 -{ - "string": "value" /* {type: "string", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules78 -{ - "string": "value" /* {type: "string", - enum: ["white", "black"] - }*/ -} - -# email - -TYPE @testWrongRules79 -{ - "email": "t@t.com" /* {type: "email", - min: 0, - }*/ -} - -TYPE @testWrongRules80 -{ - "email": "t@t.com" /* {type: "email", - max: 1, - }*/ -} - -TYPE @testWrongRules81 -{ - "email": "t@t.com" /* {type: "email", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules82 -{ - "email": "t@t.com" /* {type: "email", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules83 -{ - "email": "t@t.com" /* {type: "email", - precision: 2, - }*/ -} - -TYPE @testWrongRules84 -{ - "email": "t@t.com" /* {type: "email", - minLength: 0, - }*/ -} - -TYPE @testWrongRules85 -{ - "email": "t@t.com" /* {type: "email", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules86 -{ - "email": "t@t.com" /* {type: "email", - regex: ".*", - }*/ -} - -TYPE @testWrongRules87 -{ - "email": "t@t.com" /* {type: "email", - minItems: 0, - }*/ -} - -TYPE @testWrongRules88 -{ - "email": "t@t.com" /* {type: "email", - maxItems: 10, - }*/ -} - -# uri - -TYPE @testWrongRules89 -{ - "uri": "http://t.com" /* {type: "uri", - min: 0, - }*/ -} - -TYPE @testWrongRules90 -{ - "uri": "http://t.com" /* {type: "uri", - max: 1, - }*/ -} - -TYPE @testWrongRules91 -{ - "uri": "http://t.com" /* {type: "uri", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules92 -{ - "uri": "http://t.com" /* {type: "uri", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules93 -{ - "uri": "http://t.com" /* {type: "uri", - precision: 2, - }*/ -} - -TYPE @testWrongRules94 -{ - "uri": "http://t.com" /* {type: "uri", - minLength: 0, - }*/ -} - -TYPE @testWrongRules95 -{ - "uri": "http://t.com" /* {type: "uri", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules96 -{ - "uri": "http://t.com" /* {type: "uri", - regex: ".*", - }*/ -} - -TYPE @testWrongRules97 -{ - "uri": "http://t.com" /* {type: "uri", - minItems: 0, - }*/ -} - -TYPE @testWrongRules98 -{ - "uri": "http://t.com" /* {type: "uri", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules99 -{ - "uri": "http://t.com" /* {type: "uri", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules100 -{ - "uri": "http://t.com" /* {type: "uri", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules101 -{ - "uri": "http://t.com" /* {type: "uri", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules102 -{ - "uri": "http://t.com" /* {type: "uri", - enum: ["white", "black"] - }*/ -} - -# date - -TYPE @testWrongRules103 -{ - "date": "2021-12-16" /* {type: "date", - min: 0, - }*/ -} - -TYPE @testWrongRules104 -{ - "date": "2021-12-16" /* {type: "date", - max: 1, - }*/ -} - -TYPE @testWrongRules105 -{ - "date": "2021-12-16" /* {type: "date", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules106 -{ - "date": "2021-12-16" /* {type: "date", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules107 -{ - "date": "2021-12-16" /* {type: "date", - precision: 2, - }*/ -} - -TYPE @testWrongRules108 -{ - "date": "2021-12-16" /* {type: "date", - minLength: 0, - }*/ -} - -TYPE @testWrongRules109 -{ - "date": "2021-12-16" /* {type: "date", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules110 -{ - "date": "2021-12-16" /* {type: "date", - regex: ".*", - }*/ -} - -TYPE @testWrongRules111 -{ - "date": "2021-12-16" /* {type: "date", - minItems: 0, - }*/ -} - -TYPE @testWrongRules112 -{ - "date": "2021-12-16" /* {type: "date", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules113 -{ - "date": "2021-12-16" /* {type: "date", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules114 -{ - "date": "2021-12-16" /* {type: "date", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules115 -{ - "date": "2021-12-16" /* {type: "date", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules116 -{ - "date": "2021-12-16" /* {type: "date", - enum: ["white", "black"] - }*/ -} - -# datetime - -TYPE @testWrongRules117 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - min: 0, - }*/ -} - -TYPE @testWrongRules118 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - max: 1, - }*/ -} - -TYPE @testWrongRules119 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules120 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules121 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - precision: 2, - }*/ -} - -TYPE @testWrongRules122 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - minLength: 0, - }*/ -} - -TYPE @testWrongRules123 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules124 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - regex: ".*", - }*/ -} - -TYPE @testWrongRules125 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - minItems: 0, - }*/ -} - -TYPE @testWrongRules126 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules127 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules128 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules129 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules130 -{ - "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", - enum: ["white", "black"] - }*/ -} - -# uuid - -TYPE @testWrongRules131 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - min: 0, - }*/ -} - -TYPE @testWrongRules132 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - max: 1, - }*/ -} - -TYPE @testWrongRules133 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules134 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules135 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - precision: 2, - }*/ -} - -TYPE @testWrongRules136 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - minLength: 0, - }*/ -} - -TYPE @testWrongRules137 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules138 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - regex: ".*", - }*/ -} - -TYPE @testWrongRules139 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - minItems: 0, - }*/ -} - -TYPE @testWrongRules140 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules141 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules142 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules143 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules144 -{ - "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", - enum: ["white", "black"] - }*/ -} - -# enum - -TYPE @testWrongRules145 -{ - "enum": "white" /* {enum: ["white", "black"], - min: 0, - }*/ -} - -TYPE @testWrongRules146 -{ - "enum": "white" /* {enum: ["white", "black"], - max: 1, - }*/ -} - -TYPE @testWrongRules147 -{ - "enum": "white" /* {enum: ["white", "black"], - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules148 -{ - "enum": "white" /* {enum: ["white", "black"], - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules149 -{ - "enum": "white" /* {enum: ["white", "black"], - precision: 2, - }*/ -} - -TYPE @testWrongRules150 -{ - "enum": "white" /* {enum: ["white", "black"], - minLength: 0, - }*/ -} - -TYPE @testWrongRules151 -{ - "enum": "white" /* {enum: ["white", "black"], - maxLength: 100, - }*/ -} - -TYPE @testWrongRules152 -{ - "enum": "white" /* {enum: ["white", "black"], - regex: ".*", - }*/ -} - -TYPE @testWrongRules153 -{ - "enum": "white" /* {enum: ["white", "black"], - minItems: 0, - }*/ -} - -TYPE @testWrongRules154 -{ - "enum": "white" /* {enum: ["white", "black"], - maxItems: 10, - }*/ -} - -TYPE @testWrongRules155 -{ - "enum": "white" /* {enum: ["white", "black"], - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules156 -{ - "enum": "white" /* {enum: ["white", "black"], - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules157 -{ - "enum": "white" /* {enum: ["white", "black"], - allOf: "@cat", - }*/ -} - -# mixed - -TYPE @testWrongRules158 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - const: true, - }*/ -} - -TYPE @testWrongRules159 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - min: 0, - }*/ -} - -TYPE @testWrongRules160 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - max: 1, - }*/ -} - -TYPE @testWrongRules161 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules162 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules163 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - precision: 2, - }*/ -} - -TYPE @testWrongRules164 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - minLength: 0, - }*/ -} - -TYPE @testWrongRules165 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - maxLength: 100, - }*/ -} - -TYPE @testWrongRules166 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - regex: ".*", - }*/ -} - -TYPE @testWrongRules167 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - minItems: 0, - }*/ -} - -TYPE @testWrongRules168 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - maxItems: 10, - }*/ -} - -TYPE @testWrongRules169 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules170 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules171 -{ - "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], - enum: ["white", "black"] - }*/ -} - -# any - -TYPE @testWrongRules172 -{ - "any": 456 /* {type: "any", - min: 0, - }*/ -} - -TYPE @testWrongRules173 -{ - "any": 456 /* {type: "any", - max: 1, - }*/ -} - -TYPE @testWrongRules174 -{ - "any": 456 /* {type: "any", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules175 -{ - "any": 456 /* {type: "any", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules176 -{ - "any": 456 /* {type: "any", - precision: 2, - }*/ -} - -TYPE @testWrongRules177 -{ - "any": 456 /* {type: "any", - minLength: 0, - }*/ -} - -TYPE @testWrongRules178 -{ - "any": 456 /* {type: "any", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules179 -{ - "any": 456 /* {type: "any", - regex: ".*", - }*/ -} - -TYPE @testWrongRules180 -{ - "any": 456 /* {type: "any", - minItems: 0, - }*/ -} - -TYPE @testWrongRules181 -{ - "any": 456 /* {type: "any", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules182 -{ - "any": 456 /* {type: "any", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules183 -{ - "any": 456 /* {type: "any", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules184 -{ - "any": 456 /* {type: "any", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules185 -{ - "any": 456 /* {type: "any", - enum: ["white", "black"] - }*/ -} - -# null - -TYPE @testWrongRules186 -{ - "null": null /* {type: "null", - min: 0, - }*/ -} - -TYPE @testWrongRules187 -{ - "null": null /* {type: "null", - max: 1, - }*/ -} - -TYPE @testWrongRules188 -{ - "null": null /* {type: "null", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules189 -{ - "null": null /* {type: "null", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules190 -{ - "null": null /* {type: "null", - precision: 2, - }*/ -} - -TYPE @testWrongRules191 -{ - "null": null /* {type: "null", - minLength: 0, - }*/ -} - -TYPE @testWrongRules192 -{ - "null": null /* {type: "null", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules193 -{ - "null": null /* {type: "null", - regex: ".*", - }*/ -} - -TYPE @testWrongRules194 -{ - "null": null /* {type: "null", - minItems: 0, - }*/ -} - -TYPE @testWrongRules195 -{ - "null": null /* {type: "null", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules196 -{ - "null": null /* {type: "null", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules197 -{ - "null": null /* {type: "null", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules198 -{ - "null": null /* {type: "null", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules199 -{ - "null": null /* {type: "null", - enum: ["white", "black"] - }*/ -} - -# user type reference - -TYPE @testWrongRules200 -{ - "userType1": @cat /* { - type: "@cat", - }*/ -} - -TYPE @testWrongRules201 -{ - "userType1": @cat /* { - min: 0, - }*/ -} - -TYPE @testWrongRules202 -{ - "userType1": @cat /* { - max: 1, - }*/ -} - -TYPE @testWrongRules203 -{ - "userType1": @cat /* { - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules204 -{ - "userType1": @cat /* { - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules205 -{ - "userType1": @cat /* { - precision: 2, - }*/ -} - -TYPE @testWrongRules206 -{ - "userType1": @cat /* { - minLength: 0, - }*/ -} - -TYPE @testWrongRules207 -{ - "userType1": @cat /* { - maxLength: 100, - }*/ -} - -TYPE @testWrongRules208 -{ - "userType1": @cat /* { - regex: ".*", - }*/ -} - -TYPE @testWrongRules209 -{ - "userType1": @cat /* { - minItems: 0, - }*/ -} - -TYPE @testWrongRules210 -{ - "userType1": @cat /* { - maxItems: 10, - }*/ -} - -TYPE @testWrongRules211 -{ - "userType1": @cat /* { - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules212 -{ - "userType1": @cat /* { - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules213 -{ - "userType1": @cat /* { - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules214 -{ - "userType1": @cat /* { - enum: ["white", "black"] - }*/ -} - -# user type - -TYPE @testWrongRules215 -{ - "userType2": 12 /* {type: "@catId", - const: true, - }*/ -} - -TYPE @testWrongRules216 -{ - "userType2": 12 /* {type: "@catId", - min: 0, - }*/ -} - -TYPE @testWrongRules217 -{ - "userType2": 12 /* {type: "@catId", - max: 1, - }*/ -} - -TYPE @testWrongRules218 -{ - "userType2": 12 /* {type: "@catId", - exclusiveMinimum: true, - }*/ -} - -TYPE @testWrongRules219 -{ - "userType2": 12 /* {type: "@catId", - exclusiveMaximum: true, - }*/ -} - -TYPE @testWrongRules220 -{ - "userType2": 12 /* {type: "@catId", - precision: 2, - }*/ -} - -TYPE @testWrongRules221 -{ - "userType2": 12 /* {type: "@catId", - minLength: 0, - }*/ -} - -TYPE @testWrongRules222 -{ - "userType2": 12 /* {type: "@catId", - maxLength: 100, - }*/ -} - -TYPE @testWrongRules223 -{ - "userType2": 12 /* {type: "@catId", - regex: ".*", - }*/ -} - -TYPE @testWrongRules224 -{ - "userType2": 12 /* {type: "@catId", - minItems: 0, - }*/ -} - -TYPE @testWrongRules225 -{ - "userType2": 12 /* {type: "@catId", - maxItems: 10, - }*/ -} - -TYPE @testWrongRules226 -{ - "userType2": 12 /* {type: "@catId", - or: [{type: "string"}, {type: "integer"}], - }*/ -} - -TYPE @testWrongRules227 -{ - "userType2": 12 /* {type: "@catId", - additionalProperties: true, - }*/ -} - -TYPE @testWrongRules228 -{ - "userType2": 12 /* {type: "@catId", - allOf: "@cat", - }*/ -} - -TYPE @testWrongRules229 -{ - "userType2": 12 /* {type: "@catId", - enum: ["white", "black"] - }*/ -} - -# Helpfull types - -TYPE @cat -{ - "catId": @catId, - "catName": "Tom" -} - -TYPE @catId - 12 // {min: 1} +JSIGHT 0.3 + +INFO + Description + Delete types one by one and check that every type raises an + error in a right line. + + Version 1.0 + +# Object + +TYPE @testWrongRules1 +{ + "object": {} /* {type: "object", + enum: ["white", "black"] + }*/ +} + +TYPE @testWrongRules2 +{ + "object": {} /* {type: "object", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules3 +{ + "object": {} /* {type: "object", + minItems: 0, + }*/ +} + +TYPE @testWrongRules4 +{ + "object": {} /* {type: "object", + regex: "^[A-Za-z]+$", + }*/ +} + +TYPE @testWrongRules5 +{ + "object": {} /* {type: "object", + maxLength: 100 + }*/ +} + +TYPE @testWrongRules6 +{ + "object": {} /* {type: "object", + minLength: 0, + }*/ +} + +TYPE @testWrongRules7 +{ + "object": {} /* {type: "object", + precision: 2, + }*/ +} + +TYPE @testWrongRules8 +{ + "object": {} /* {type: "object", + max: 1, + }*/ +} + +TYPE @testWrongRules9 +{ + "object": {} /* {type: "object", + min: 0, + }*/ +} + +TYPE @testWrongRules10 +{ + "object": {} /* {type: "object", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules11 +{ + "object": {} /* {type: "object", + const: true + }*/ +} + +# array +TYPE @testWrongRules12 +{ + "array" : [ /* {type: "array", + const: true, + }*/ + "item" + ] +} + +TYPE @testWrongRules13 +{ + "array" : [ /* {type: "array", + min: 0, + }*/ + "item" + ] +} + +TYPE @testWrongRules14 +{ + "array" : [ /* {type: "array", + max: 1, + }*/ + "item" + ] +} + +TYPE @testWrongRules15 +{ + "array" : [ /* {type: "array", + exclusiveMinimum: true, + }*/ + "item" + ] +} + +TYPE @testWrongRules16 +{ + "array" : [ /* {type: "array", + exclusiveMaximum: true, + }*/ + "item" + ] +} + +TYPE @testWrongRules17 +{ + "array" : [ /* {type: "array", + precision: 2, + }*/ + "item" + ] +} + +TYPE @testWrongRules18 +{ + "array" : [ /* {type: "array", + minLength: 0, + }*/ + "item" + ] +} + +TYPE @testWrongRules19 +{ + "array" : [ /* {type: "array", + maxLength: 100, + }*/ + "item" + ] +} + +TYPE @testWrongRules20 +{ + "array" : [ /* {type: "array", + regex: "^[A-Za-z]+$", + }*/ + "item" + ] +} + +TYPE @testWrongRules21 +{ + "array" : [ /* {type: "array", + or: [{type: "string"}, {type: "integer"}], + }*/ + "item" + ] +} + +TYPE @testWrongRules22 +{ + "array" : [ /* {type: "array", + additionalProperties: true, + }*/ + "item" + ] +} + +TYPE @testWrongRules23 +{ + "array" : [ /* {type: "array", + allOf: "@cat", + }*/ + "item" + ] +} + +TYPE @testWrongRules24 +{ + "array" : [ /* {type: "array", + enum: ["white", "black"] + }*/ + "item" + ] +} + +# integer + +TYPE @testWrongRules25 +{ + "integer": 1 /* {type: "integer", + precision: 2, + }*/ +} + +TYPE @testWrongRules26 +{ + "integer": 1 /* {type: "integer", + minLength: 0, + }*/ +} + +TYPE @testWrongRules27 +{ + "integer": 1 /* {type: "integer", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules28 +{ + "integer": 1 /* {type: "integer", + regex: "^[A-Za-z]+$", + }*/ +} + +TYPE @testWrongRules29 +{ + "integer": 1 /* {type: "integer", + minItems: 0, + }*/ +} + +TYPE @testWrongRules30 +{ + "integer": 1 /* {type: "integer", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules31 +{ + "integer": 1 /* {type: "integer", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules32 +{ + "integer": 1 /* {type: "integer", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules33 +{ + "integer": 1 /* {type: "integer", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules34 +{ + "integer": 1 /* {type: "integer", + enum: ["white", "black"] + }*/ +} + +# float + +TYPE @testWrongRules35 +{ + "float": 1.2 /* {type: "float", + precision: 1 + }*/ +} + +TYPE @testWrongRules36 +{ + "float": 1.2 /* {type: "float", + minLength: 0 + }*/ +} + +TYPE @testWrongRules37 +{ + "float": 1.2 /* {type: "float", + maxLength: 100 + }*/ +} + +TYPE @testWrongRules38 +{ + "float": 1.2 /* {type: "float", + regex: "^[A-Za-z]+$" + }*/ +} + +TYPE @testWrongRules39 +{ + "float": 1.2 /* {type: "float", + minItems: 0 + }*/ +} + +TYPE @testWrongRules40 +{ + "float": 1.2 /* {type: "float", + maxItems: 10 + }*/ +} + +TYPE @testWrongRules41 +{ + "float": 1.2 /* {type: "float", + or: [{type: "string"}, {type: "float"}] + }*/ +} + +TYPE @testWrongRules42 +{ + "float": 1.2 /* {type: "float", + additionalProperties: true + }*/ +} + +TYPE @testWrongRules43 +{ + "float": 1.2 /* {type: "float", + allOf: "@cat" + }*/ +} + +TYPE @testWrongRules44 +{ + "float": 1.2 /* {type: "float", + enum: [1.2, 1.3] + }*/ +} + +# decimal + +TYPE @testWrongRules45 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + minLength: 0, + }*/ +} + +TYPE @testWrongRules46 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + maxLength: 100, + }*/ +} + +TYPE @testWrongRules47 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + regex: "^[A-Za-z]+$", + }*/ +} + +TYPE @testWrongRules48 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + minItems: 0, + }*/ +} + +TYPE @testWrongRules49 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + maxItems: 10, + }*/ +} + +TYPE @testWrongRules50 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules51 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules52 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules53 +{ + "decimal": 1.23 /* {type: "decimal", precision: 2, + enum: ["white", "black"] + }*/ +} + +# boolean + +TYPE @testWrongRules54 +{ + "boolean": true /* {type: "boolean", + min: 0, + }*/ +} + +TYPE @testWrongRules55 +{ + "boolean": true /* {type: "boolean", + max: 1, + }*/ +} + +TYPE @testWrongRules56 +{ + "boolean": true /* {type: "boolean", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules57 +{ + "boolean": true /* {type: "boolean", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules58 +{ + "boolean": true /* {type: "boolean", + precision: 2, + }*/ +} + +TYPE @testWrongRules59 +{ + "boolean": true /* {type: "boolean", + minLength: 0, + }*/ +} + +TYPE @testWrongRules60 +{ + "boolean": true /* {type: "boolean", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules61 +{ + "boolean": true /* {type: "boolean", + regex: "^[A-Za-z]+$", + }*/ +} + +TYPE @testWrongRules62 +{ + "boolean": true /* {type: "boolean", + minItems: 0, + }*/ +} + +TYPE @testWrongRules63 +{ + "boolean": true /* {type: "boolean", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules64 +{ + "boolean": true /* {type: "boolean", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules65 +{ + "boolean": true /* {type: "boolean", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules66 +{ + "boolean": true /* {type: "boolean", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules67 +{ + "boolean": true /* {type: "boolean", + enum: ["white", "black"] + }*/ +} + +# string + +TYPE @testWrongRules68 +{ + "string": "value" /* {type: "string", + min: 0, + }*/ +} + +TYPE @testWrongRules69 +{ + "string": "value" /* {type: "string", + max: 1, + }*/ +} + +TYPE @testWrongRules70 +{ + "string": "value" /* {type: "string", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules71 +{ + "string": "value" /* {type: "string", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules72 +{ + "string": "value" /* {type: "string", + precision: 2, + }*/ +} + +TYPE @testWrongRules73 +{ + "string": "value" /* {type: "string", + minItems: 0, + }*/ +} + +TYPE @testWrongRules74 +{ + "string": "value" /* {type: "string", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules75 +{ + "string": "value" /* {type: "string", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules76 +{ + "string": "value" /* {type: "string", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules77 +{ + "string": "value" /* {type: "string", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules78 +{ + "string": "value" /* {type: "string", + enum: ["white", "black"] + }*/ +} + +# email + +TYPE @testWrongRules79 +{ + "email": "t@t.com" /* {type: "email", + min: 0, + }*/ +} + +TYPE @testWrongRules80 +{ + "email": "t@t.com" /* {type: "email", + max: 1, + }*/ +} + +TYPE @testWrongRules81 +{ + "email": "t@t.com" /* {type: "email", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules82 +{ + "email": "t@t.com" /* {type: "email", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules83 +{ + "email": "t@t.com" /* {type: "email", + precision: 2, + }*/ +} + +TYPE @testWrongRules84 +{ + "email": "t@t.com" /* {type: "email", + minLength: 0, + }*/ +} + +TYPE @testWrongRules85 +{ + "email": "t@t.com" /* {type: "email", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules86 +{ + "email": "t@t.com" /* {type: "email", + regex: ".*", + }*/ +} + +TYPE @testWrongRules87 +{ + "email": "t@t.com" /* {type: "email", + minItems: 0, + }*/ +} + +TYPE @testWrongRules88 +{ + "email": "t@t.com" /* {type: "email", + maxItems: 10, + }*/ +} + +# uri + +TYPE @testWrongRules89 +{ + "uri": "http://t.com" /* {type: "uri", + min: 0, + }*/ +} + +TYPE @testWrongRules90 +{ + "uri": "http://t.com" /* {type: "uri", + max: 1, + }*/ +} + +TYPE @testWrongRules91 +{ + "uri": "http://t.com" /* {type: "uri", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules92 +{ + "uri": "http://t.com" /* {type: "uri", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules93 +{ + "uri": "http://t.com" /* {type: "uri", + precision: 2, + }*/ +} + +TYPE @testWrongRules94 +{ + "uri": "http://t.com" /* {type: "uri", + minLength: 0, + }*/ +} + +TYPE @testWrongRules95 +{ + "uri": "http://t.com" /* {type: "uri", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules96 +{ + "uri": "http://t.com" /* {type: "uri", + regex: ".*", + }*/ +} + +TYPE @testWrongRules97 +{ + "uri": "http://t.com" /* {type: "uri", + minItems: 0, + }*/ +} + +TYPE @testWrongRules98 +{ + "uri": "http://t.com" /* {type: "uri", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules99 +{ + "uri": "http://t.com" /* {type: "uri", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules100 +{ + "uri": "http://t.com" /* {type: "uri", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules101 +{ + "uri": "http://t.com" /* {type: "uri", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules102 +{ + "uri": "http://t.com" /* {type: "uri", + enum: ["white", "black"] + }*/ +} + +# date + +TYPE @testWrongRules103 +{ + "date": "2021-12-16" /* {type: "date", + min: 0, + }*/ +} + +TYPE @testWrongRules104 +{ + "date": "2021-12-16" /* {type: "date", + max: 1, + }*/ +} + +TYPE @testWrongRules105 +{ + "date": "2021-12-16" /* {type: "date", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules106 +{ + "date": "2021-12-16" /* {type: "date", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules107 +{ + "date": "2021-12-16" /* {type: "date", + precision: 2, + }*/ +} + +TYPE @testWrongRules108 +{ + "date": "2021-12-16" /* {type: "date", + minLength: 0, + }*/ +} + +TYPE @testWrongRules109 +{ + "date": "2021-12-16" /* {type: "date", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules110 +{ + "date": "2021-12-16" /* {type: "date", + regex: ".*", + }*/ +} + +TYPE @testWrongRules111 +{ + "date": "2021-12-16" /* {type: "date", + minItems: 0, + }*/ +} + +TYPE @testWrongRules112 +{ + "date": "2021-12-16" /* {type: "date", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules113 +{ + "date": "2021-12-16" /* {type: "date", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules114 +{ + "date": "2021-12-16" /* {type: "date", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules115 +{ + "date": "2021-12-16" /* {type: "date", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules116 +{ + "date": "2021-12-16" /* {type: "date", + enum: ["white", "black"] + }*/ +} + +# datetime + +TYPE @testWrongRules117 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + min: 0, + }*/ +} + +TYPE @testWrongRules118 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + max: 1, + }*/ +} + +TYPE @testWrongRules119 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules120 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules121 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + precision: 2, + }*/ +} + +TYPE @testWrongRules122 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + minLength: 0, + }*/ +} + +TYPE @testWrongRules123 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules124 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + regex: ".*", + }*/ +} + +TYPE @testWrongRules125 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + minItems: 0, + }*/ +} + +TYPE @testWrongRules126 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules127 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules128 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules129 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules130 +{ + "datetime": "2006-01-02T15:04:05+07:00" /* {type: "datetime", + enum: ["white", "black"] + }*/ +} + +# uuid + +TYPE @testWrongRules131 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + min: 0, + }*/ +} + +TYPE @testWrongRules132 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + max: 1, + }*/ +} + +TYPE @testWrongRules133 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules134 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules135 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + precision: 2, + }*/ +} + +TYPE @testWrongRules136 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + minLength: 0, + }*/ +} + +TYPE @testWrongRules137 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules138 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + regex: ".*", + }*/ +} + +TYPE @testWrongRules139 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + minItems: 0, + }*/ +} + +TYPE @testWrongRules140 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules141 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules142 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules143 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules144 +{ + "uuid": "550e8400-e29b-41d4-a716-446655440000" /* {type: "uuid", + enum: ["white", "black"] + }*/ +} + +# enum + +TYPE @testWrongRules145 +{ + "enum": "white" /* {enum: ["white", "black"], + min: 0, + }*/ +} + +TYPE @testWrongRules146 +{ + "enum": "white" /* {enum: ["white", "black"], + max: 1, + }*/ +} + +TYPE @testWrongRules147 +{ + "enum": "white" /* {enum: ["white", "black"], + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules148 +{ + "enum": "white" /* {enum: ["white", "black"], + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules149 +{ + "enum": "white" /* {enum: ["white", "black"], + precision: 2, + }*/ +} + +TYPE @testWrongRules150 +{ + "enum": "white" /* {enum: ["white", "black"], + minLength: 0, + }*/ +} + +TYPE @testWrongRules151 +{ + "enum": "white" /* {enum: ["white", "black"], + maxLength: 100, + }*/ +} + +TYPE @testWrongRules152 +{ + "enum": "white" /* {enum: ["white", "black"], + regex: ".*", + }*/ +} + +TYPE @testWrongRules153 +{ + "enum": "white" /* {enum: ["white", "black"], + minItems: 0, + }*/ +} + +TYPE @testWrongRules154 +{ + "enum": "white" /* {enum: ["white", "black"], + maxItems: 10, + }*/ +} + +TYPE @testWrongRules155 +{ + "enum": "white" /* {enum: ["white", "black"], + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules156 +{ + "enum": "white" /* {enum: ["white", "black"], + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules157 +{ + "enum": "white" /* {enum: ["white", "black"], + allOf: "@cat", + }*/ +} + +# mixed + +TYPE @testWrongRules158 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + const: true, + }*/ +} + +TYPE @testWrongRules159 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + min: 0, + }*/ +} + +TYPE @testWrongRules160 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + max: 1, + }*/ +} + +TYPE @testWrongRules161 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules162 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules163 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + precision: 2, + }*/ +} + +TYPE @testWrongRules164 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + minLength: 0, + }*/ +} + +TYPE @testWrongRules165 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + maxLength: 100, + }*/ +} + +TYPE @testWrongRules166 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + regex: ".*", + }*/ +} + +TYPE @testWrongRules167 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + minItems: 0, + }*/ +} + +TYPE @testWrongRules168 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + maxItems: 10, + }*/ +} + +TYPE @testWrongRules169 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules170 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules171 +{ + "mixed": "abc" /* {or: [{type: "string"}, {type: "integer"}], + enum: ["white", "black"] + }*/ +} + +# any + +TYPE @testWrongRules172 +{ + "any": 456 /* {type: "any", + min: 0, + }*/ +} + +TYPE @testWrongRules173 +{ + "any": 456 /* {type: "any", + max: 1, + }*/ +} + +TYPE @testWrongRules174 +{ + "any": 456 /* {type: "any", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules175 +{ + "any": 456 /* {type: "any", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules176 +{ + "any": 456 /* {type: "any", + precision: 2, + }*/ +} + +TYPE @testWrongRules177 +{ + "any": 456 /* {type: "any", + minLength: 0, + }*/ +} + +TYPE @testWrongRules178 +{ + "any": 456 /* {type: "any", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules179 +{ + "any": 456 /* {type: "any", + regex: ".*", + }*/ +} + +TYPE @testWrongRules180 +{ + "any": 456 /* {type: "any", + minItems: 0, + }*/ +} + +TYPE @testWrongRules181 +{ + "any": 456 /* {type: "any", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules182 +{ + "any": 456 /* {type: "any", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules183 +{ + "any": 456 /* {type: "any", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules184 +{ + "any": 456 /* {type: "any", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules185 +{ + "any": 456 /* {type: "any", + enum: ["white", "black"] + }*/ +} + +# null + +TYPE @testWrongRules186 +{ + "null": null /* {type: "null", + min: 0, + }*/ +} + +TYPE @testWrongRules187 +{ + "null": null /* {type: "null", + max: 1, + }*/ +} + +TYPE @testWrongRules188 +{ + "null": null /* {type: "null", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules189 +{ + "null": null /* {type: "null", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules190 +{ + "null": null /* {type: "null", + precision: 2, + }*/ +} + +TYPE @testWrongRules191 +{ + "null": null /* {type: "null", + minLength: 0, + }*/ +} + +TYPE @testWrongRules192 +{ + "null": null /* {type: "null", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules193 +{ + "null": null /* {type: "null", + regex: ".*", + }*/ +} + +TYPE @testWrongRules194 +{ + "null": null /* {type: "null", + minItems: 0, + }*/ +} + +TYPE @testWrongRules195 +{ + "null": null /* {type: "null", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules196 +{ + "null": null /* {type: "null", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules197 +{ + "null": null /* {type: "null", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules198 +{ + "null": null /* {type: "null", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules199 +{ + "null": null /* {type: "null", + enum: ["white", "black"] + }*/ +} + +# user type reference + +TYPE @testWrongRules200 +{ + "userType1": @cat /* { + type: "@cat", + }*/ +} + +TYPE @testWrongRules201 +{ + "userType1": @cat /* { + min: 0, + }*/ +} + +TYPE @testWrongRules202 +{ + "userType1": @cat /* { + max: 1, + }*/ +} + +TYPE @testWrongRules203 +{ + "userType1": @cat /* { + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules204 +{ + "userType1": @cat /* { + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules205 +{ + "userType1": @cat /* { + precision: 2, + }*/ +} + +TYPE @testWrongRules206 +{ + "userType1": @cat /* { + minLength: 0, + }*/ +} + +TYPE @testWrongRules207 +{ + "userType1": @cat /* { + maxLength: 100, + }*/ +} + +TYPE @testWrongRules208 +{ + "userType1": @cat /* { + regex: ".*", + }*/ +} + +TYPE @testWrongRules209 +{ + "userType1": @cat /* { + minItems: 0, + }*/ +} + +TYPE @testWrongRules210 +{ + "userType1": @cat /* { + maxItems: 10, + }*/ +} + +TYPE @testWrongRules211 +{ + "userType1": @cat /* { + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules212 +{ + "userType1": @cat /* { + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules213 +{ + "userType1": @cat /* { + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules214 +{ + "userType1": @cat /* { + enum: ["white", "black"] + }*/ +} + +# user type + +TYPE @testWrongRules215 +{ + "userType2": 12 /* {type: "@catId", + const: true, + }*/ +} + +TYPE @testWrongRules216 +{ + "userType2": 12 /* {type: "@catId", + min: 0, + }*/ +} + +TYPE @testWrongRules217 +{ + "userType2": 12 /* {type: "@catId", + max: 1, + }*/ +} + +TYPE @testWrongRules218 +{ + "userType2": 12 /* {type: "@catId", + exclusiveMinimum: true, + }*/ +} + +TYPE @testWrongRules219 +{ + "userType2": 12 /* {type: "@catId", + exclusiveMaximum: true, + }*/ +} + +TYPE @testWrongRules220 +{ + "userType2": 12 /* {type: "@catId", + precision: 2, + }*/ +} + +TYPE @testWrongRules221 +{ + "userType2": 12 /* {type: "@catId", + minLength: 0, + }*/ +} + +TYPE @testWrongRules222 +{ + "userType2": 12 /* {type: "@catId", + maxLength: 100, + }*/ +} + +TYPE @testWrongRules223 +{ + "userType2": 12 /* {type: "@catId", + regex: ".*", + }*/ +} + +TYPE @testWrongRules224 +{ + "userType2": 12 /* {type: "@catId", + minItems: 0, + }*/ +} + +TYPE @testWrongRules225 +{ + "userType2": 12 /* {type: "@catId", + maxItems: 10, + }*/ +} + +TYPE @testWrongRules226 +{ + "userType2": 12 /* {type: "@catId", + or: [{type: "string"}, {type: "integer"}], + }*/ +} + +TYPE @testWrongRules227 +{ + "userType2": 12 /* {type: "@catId", + additionalProperties: true, + }*/ +} + +TYPE @testWrongRules228 +{ + "userType2": 12 /* {type: "@catId", + allOf: "@cat", + }*/ +} + +TYPE @testWrongRules229 +{ + "userType2": 12 /* {type: "@catId", + enum: ["white", "black"] + }*/ +} + +# Helpfull types + +TYPE @cat +{ + "catId": @catId, + "catName": "Tom" +} + +TYPE @catId + 12 // {min: 1} diff --git a/tests/manual-tests/http-method-tests/http-method-replace-test-2.jst b/tests/manual-tests/http-method-tests/http-method-replace-test-2.jst index 5872971..066c2a6 100644 --- a/tests/manual-tests/http-method-tests/http-method-replace-test-2.jst +++ b/tests/manual-tests/http-method-tests/http-method-replace-test-2.jst @@ -1,11 +1,11 @@ -JSIGHT 0.3 - -INFO - Description - 1. Select POST tab in rendered documentation. - 2. Select POST directory in the code and remove it by `Delete` button. - 3. Check, that in rendered documentation GET tab is automatically selected. - -URL /1 - GET +JSIGHT 0.3 + +INFO + Description + 1. Select the POST tab in the rendered documentation. + 2. Select the POST directory in the code and remove it by the `Delete` button. + 3. Check, that in the rendered documentation the GET tab is automatically selected. + +URL /1 + GET POST \ No newline at end of file diff --git a/tests/manual-tests/http-method-tests/http-method-tests.jst b/tests/manual-tests/http-method-tests/http-method-tests.jst index 591f048..eb6e7dc 100644 --- a/tests/manual-tests/http-method-tests/http-method-tests.jst +++ b/tests/manual-tests/http-method-tests/http-method-tests.jst @@ -59,6 +59,8 @@ PATCH DELETE + GET /test-keywords/any-path-parameters/{par1} // Check the `par1` parameter in the "Path parameters" section. + URL /test-keywords/path-parameters/{par1} Path { diff --git a/tests/manual-tests/mobile-ui-tests.md b/tests/manual-tests/mobile-ui-tests.md index 9edd61d..7367a76 100644 --- a/tests/manual-tests/mobile-ui-tests.md +++ b/tests/manual-tests/mobile-ui-tests.md @@ -1,5 +1,9 @@ # Mobile UI tests +## OpenAPI mode as default + +1. Check, that on mobile devices the OpenAPI mode is by default. + ## Run common UI tests on mobile screen Note: right now we should not check all the functions, but just simple things, which can satisfy a diff --git a/tests/manual-tests/openapi-converter-tests/openapi-converter-tests.md b/tests/manual-tests/openapi-converter-tests/openapi-converter-tests.md new file mode 100644 index 0000000..062d9e5 --- /dev/null +++ b/tests/manual-tests/openapi-converter-tests/openapi-converter-tests.md @@ -0,0 +1,10 @@ +# OpenAPI converter tests + +- Check the OpenAPI tab in the right tab panel. +- Check the YAML and JSON OpenAPI formats. + - Check the line numbering from the first to the last (!) line. + - Check Horizontal and vertical scrolling of the OpenAPI panel. +- Check the OpenAPI menu item in the main menu. +- Check the "Copy All" button for the JSON and YAML formats. Check the copied code in the https://editor.swagger.io site. +- Check copying the OpenAPI code with manual selection and Ctrl+C. +- Check Download OpenAPI buttons: JSON and YAML. \ No newline at end of file diff --git a/tests/manual-tests/openapi-converter-tests/openapi-specific-error-test.jst b/tests/manual-tests/openapi-converter-tests/openapi-specific-error-test.jst new file mode 100644 index 0000000..11b76f0 --- /dev/null +++ b/tests/manual-tests/openapi-converter-tests/openapi-specific-error-test.jst @@ -0,0 +1,13 @@ +### + +Test: +1. Check that this code is successfully rendered in the HTML Doc mode. +2. Check that this code is rendered with the error in the OpenAPI mode. + +### + +JSIGHT 0.3 + +GET / + Query + "" diff --git a/tests/manual-tests/share-link-tests.md b/tests/manual-tests/share-link-tests.md index 8a16994..e46855f 100644 --- a/tests/manual-tests/share-link-tests.md +++ b/tests/manual-tests/share-link-tests.md @@ -80,6 +80,8 @@ all steps in the same browser window without refresh. 10. **Update not last version.** Get link with several versions (e.g. 3). Try to reproduce basic share test cases 2 and 3, but each time use not last version of the link (e.g. 1). +11. **OpenAPI**. Repeat all the basic tests, but each time before pressing the Share button, switch + to the OpenAPI tab. # Error cases diff --git a/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all-sections.jst b/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all-sections.jst index e6ee686..75f7192 100644 --- a/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all-sections.jst +++ b/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all-sections.jst @@ -1,7 +1,7 @@ JSIGHT 0.3 INFO - Title "Check details cards in different sections" + Title "Check details cards in different sections (jtest)" # always use `jtest` in test titles for the sake of statistics Description ( Check, that you can open details card in all types of sections. diff --git a/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all_rules.jst b/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all_rules.jst index eb77bd4..804fe58 100644 --- a/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all_rules.jst +++ b/tests/manual-tests/sidebar-details-tests/sidebar-details-card-all_rules.jst @@ -72,7 +72,7 @@ TYPE @or ]} */ "orByValue": 123 /* {or: [ - {min: 100}, + {type: "integer", min: 100}, {type: "string"} ]} */ diff --git a/tests/manual-tests/table-view-tests/table-view-root-line.jst b/tests/manual-tests/table-view-tests/table-view-root-line.jst new file mode 100644 index 0000000..8f93c34 --- /dev/null +++ b/tests/manual-tests/table-view-tests/table-view-root-line.jst @@ -0,0 +1,32 @@ +JSIGHT 0.3 + +INFO + Title "Test table view root line (jtest)" + +GET /{id} + Path + { + "id": 1 // MUST NOT be line above ONLY HERE! + } + Query "query_par=1" + { + "query_par": 1 // MUST be line above + } + 201 + Headers + { + "Switch to the Table View →": "" // MUST be line above + } + Body any + 200 + Headers + @headers + Body + { + "Switch to the Table View ": "" // MUST be line above + } + +TYPE @headers + { + "Switch to the Table View ": "" // MUST be line above + } \ No newline at end of file diff --git a/tests/running-automated-tests-ci.md b/tests/running-automated-tests-ci.md new file mode 100644 index 0000000..2f19267 --- /dev/null +++ b/tests/running-automated-tests-ci.md @@ -0,0 +1,23 @@ +## How to run e2e automated tests remotely (CI/CD) + +1. Open the **[CircleCI Dashboard](https://app.circleci.com/pipelines/github/jsightapi)**, **"All Pipelines"** section. +2. In the filter section... + 1. Select the **Project** = "**online-editor-e2e**". + 2. Select the **Branch** (e.g. "**main**"). +3. Click **"Trigger Pipeline"** button above. +4. In the "**Add Parameters**" section... + - (Required) Add `boolean` parameter `fire` with value `true`. + - (Optional) Add another parameter(s), e.g.: + + | Type | Name | Value | Description | + |---------|-------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| + | string | env | `dev` (or `stage`) | To run tests on the corresponding environment. | + | string | maven-test | internal name of the test class/test method | To run a specific test(s), e.g. `CodeViewTest`. | + | integer | junit-parallelism | `2` (`3`, `4`, etc) | For a number of threads, to run tests in parallel. | + | string | add-params | comma-separated list of `-Dkey=value` | Additional parameters for the Maven run,
    e.g. `-Dselenide.browser=firefox, -Dselenide.browserVersion=123.0, -Dselenide.timeout=8000`. | + + _Note: the job with only default `fire = true` parameter will run all the tests on the default DEV environment. See `.circleci/config.yml` for more details._ +5. Click **"Trigger Pipeline"** button in the modal window. +6. Wait for the pipeline to finish, and open the **"run-e2e"** job. +7. Check the failed tests list (if any) in the "**Tests**" tab. +8. Check the **Allure Report** in the "**Artifacts**" tab (open the one with `target/site/allure-maven-plugin/index.html` link) \ No newline at end of file