From a5568f9e1642f5ce286cd2b4927a4ce3fa663ba2 Mon Sep 17 00:00:00 2001 From: kyle Date: Fri, 7 Dec 2018 20:54:29 +0100 Subject: [PATCH] improve: OAuth2 UI and test suite (via #5066) * create `features` folder * add base oauth2 server * continue implementing OAuth tests * WIP * add password flow tests * modify Password flow credential types * remove query string credential type * add test case for Authorization flow * add specific Authorization value for Password flow test * WIP * fix linter issues --- package-lock.json | 98 ++++++++---- package.json | 4 + src/core/components/auth/oauth2.jsx | 13 +- src/core/components/live-response.jsx | 2 +- src/core/plugins/auth/actions.js | 29 ++-- test/e2e-cypress/{tests => }/.eslintrc | 0 .../helpers/oauth2-server/index.js | 50 +++++++ .../helpers/oauth2-server/model.js | 141 ++++++++++++++++++ .../helpers/oauth2-server/swagger.yaml | 36 +++++ test/e2e-cypress/plugins/index.js | 2 + test/e2e-cypress/support/index.js | 7 + .../tests/{ => features}/deep-linking.js | 0 .../features/oauth2-flows/application.js | 55 +++++++ .../tests/features/oauth2-flows/password.js | 122 +++++++++++++++ 14 files changed, 504 insertions(+), 55 deletions(-) rename test/e2e-cypress/{tests => }/.eslintrc (100%) create mode 100644 test/e2e-cypress/helpers/oauth2-server/index.js create mode 100644 test/e2e-cypress/helpers/oauth2-server/model.js create mode 100644 test/e2e-cypress/helpers/oauth2-server/swagger.yaml rename test/e2e-cypress/tests/{ => features}/deep-linking.js (100%) create mode 100644 test/e2e-cypress/tests/features/oauth2-flows/application.js create mode 100644 test/e2e-cypress/tests/features/oauth2-flows/password.js diff --git a/package-lock.json b/package-lock.json index 9693f942..cb582296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2341,28 +2341,49 @@ "dev": true }, "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "dev": true, "requires": { "bytes": "3.0.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" }, "dependencies": { "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } } } }, @@ -6421,14 +6442,14 @@ } }, "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", "dev": true, "requires": { "accepts": "~1.3.5", "array-flatten": "1.1.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "content-disposition": "0.5.2", "content-type": "~1.0.4", "cookie": "0.3.1", @@ -6445,10 +6466,10 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", @@ -6464,10 +6485,10 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, "statuses": { @@ -9397,9 +9418,9 @@ "dev": true }, "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", "dev": true }, "is-absolute-url": { @@ -15478,6 +15499,23 @@ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, + "oauth2-server": { + "version": "2.4.1", + "resolved": "http://registry.npmjs.org/oauth2-server/-/oauth2-server-2.4.1.tgz", + "integrity": "sha1-2m3QVMAh7JwpQ59dGijeY9ArcWw=", + "dev": true, + "requires": { + "basic-auth": "~0.0.1" + }, + "dependencies": { + "basic-auth": { + "version": "0.0.1", + "resolved": "http://registry.npmjs.org/basic-auth/-/basic-auth-0.0.1.tgz", + "integrity": "sha1-Md22WEP2w1xv6nvrRqmHy4zhiSQ=", + "dev": true + } + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -18364,13 +18402,13 @@ } }, "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", "dev": true, "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.6.0" + "ipaddr.js": "1.8.0" } }, "proxy-agent": { diff --git a/package.json b/package.json index 9f18f9b6..db1f0d45 100644 --- a/package.json +++ b/package.json @@ -95,9 +95,11 @@ "babel-preset-react": "^6.23.0", "babel-preset-stage-0": "^6.22.0", "babel-runtime": "^6.23.0", + "body-parser": "^1.18.3", "bundlesize": "^0.17.0", "chromedriver": "^2.38.3", "copy-webpack-plugin": "^4.0.1", + "cors": "^2.8.4", "css-loader": "^0.28.11", "cypress": "^3.1.0", "dedent": "^0.7.0", @@ -108,6 +110,7 @@ "eslint-plugin-mocha": "^4.11.0", "eslint-plugin-react": "^7.10.0", "expect": "^1.20.2", + "express": "^4.16.4", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.11", "git-describe": "^4.0.1", @@ -124,6 +127,7 @@ "npm-run-all": "^4.1.2", "null-loader": "0.1.1", "nyc": "^11.3.0", + "oauth2-server": "^2.4.1", "open": "0.0.5", "postcss-loader": "^2.1.5", "raw-loader": "0.5.1", diff --git a/src/core/components/auth/oauth2.jsx b/src/core/components/auth/oauth2.jsx index 86e6257b..a4cb6a79 100644 --- a/src/core/components/auth/oauth2.jsx +++ b/src/core/components/auth/oauth2.jsx @@ -24,7 +24,7 @@ export default class Oauth2 extends React.Component { let username = auth && auth.get("username") || "" let clientId = auth && auth.get("clientId") || authConfigs.clientId || "" let clientSecret = auth && auth.get("clientSecret") || authConfigs.clientSecret || "" - let passwordType = auth && auth.get("passwordType") || "request-body" + let passwordType = auth && auth.get("passwordType") || "basic" this.state = { appName: authConfigs.appName, @@ -150,14 +150,13 @@ export default class Oauth2 extends React.Component { } - + { isAuthorized ? { this.state.passwordType } : } @@ -165,7 +164,7 @@ export default class Oauth2 extends React.Component { } { - ( flow === APPLICATION || flow === IMPLICIT || flow === ACCESS_CODE || ( flow === PASSWORD && this.state.passwordType!== "basic") ) && + ( flow === APPLICATION || flow === IMPLICIT || flow === ACCESS_CODE || flow === PASSWORD ) && ( !isAuthorized || isAuthorized && this.state.clientId) && { @@ -183,7 +182,7 @@ export default class Oauth2 extends React.Component { } { - ( flow === APPLICATION || flow === ACCESS_CODE || ( flow === PASSWORD && this.state.passwordType!== "basic") ) && + ( (flow === APPLICATION || flow === ACCESS_CODE || flow === PASSWORD) && { isAuthorized ? ****** @@ -197,7 +196,7 @@ export default class Oauth2 extends React.Component { } - } + )} { !isAuthorized && scopes && scopes.size ?
diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx index 8cc93288..527e24e3 100644 --- a/src/core/components/live-response.jsx +++ b/src/core/components/live-response.jsx @@ -80,7 +80,7 @@ export default class LiveResponse extends React.Component {
}

Server response

- +
diff --git a/src/core/plugins/auth/actions.js b/src/core/plugins/auth/actions.js index 45dbf727..35afbc0d 100644 --- a/src/core/plugins/auth/actions.js +++ b/src/core/plugins/auth/actions.js @@ -74,28 +74,23 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => { let { schema, name, username, password, passwordType, clientId, clientSecret } = auth let form = { grant_type: "password", - scope: auth.scopes.join(scopeSeparator) + scope: auth.scopes.join(scopeSeparator), + username, + password } let query = {} let headers = {} - if ( passwordType === "basic") { - headers.Authorization = "Basic " + btoa(username + ":" + password) - } else { - Object.assign(form, {username}, {password}) - - switch ( passwordType ) { - case "query": - setClientIdAndSecret(query, clientId, clientSecret) - break + switch (passwordType) { + case "request-body": + setClientIdAndSecret(form, clientId, clientSecret) + break - case "request-body": - setClientIdAndSecret(form, clientId, clientSecret) - break - - default: - headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret) - } + case "basic": + headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret) + break + default: + console.warn(`Warning: invalid passwordType ${passwordType} was passed, not including client id and secret`) } return authActions.authorizeRequest({ body: buildFormData(form), url: schema.get("tokenUrl"), name, headers, query, auth}) diff --git a/test/e2e-cypress/tests/.eslintrc b/test/e2e-cypress/.eslintrc similarity index 100% rename from test/e2e-cypress/tests/.eslintrc rename to test/e2e-cypress/.eslintrc diff --git a/test/e2e-cypress/helpers/oauth2-server/index.js b/test/e2e-cypress/helpers/oauth2-server/index.js new file mode 100644 index 00000000..ba779352 --- /dev/null +++ b/test/e2e-cypress/helpers/oauth2-server/index.js @@ -0,0 +1,50 @@ +// from https://github.com/pedroetb/node-oauth2-server-example + +var Http = require("http") +var path = require("path") +var express = require("express") +var bodyParser = require("body-parser") +var oauthserver = require("oauth2-server") +var cors = require("cors") + +var app = express() + +app.use(cors()) + +app.use(bodyParser.urlencoded({ extended: true })) + +app.use(bodyParser.json()) + +app.oauth = oauthserver({ + model: require("./model.js"), + grants: ["password", "client_credentials", "implicit"], + debug: true +}) + +app.all("/oauth/token", app.oauth.grant()) + +app.get("/swagger.yaml", function (req, res) { + res.sendFile(path.join(__dirname, "swagger.yaml")) +}) + +app.get("*", app.oauth.authorise(), function (req, res) { + res.send("Secret secrets are no fun, secret secrets hurt someone.") +}) + +app.use(app.oauth.errorHandler()) + +function startServer() { + var httpServer = Http.createServer(app) + httpServer.listen("3231") + + return function stopServer() { + httpServer.close() + } +} + +module.exports = startServer + +if (require.main === module) { + // for debugging + startServer() +} \ No newline at end of file diff --git a/test/e2e-cypress/helpers/oauth2-server/model.js b/test/e2e-cypress/helpers/oauth2-server/model.js new file mode 100644 index 00000000..f5722bf4 --- /dev/null +++ b/test/e2e-cypress/helpers/oauth2-server/model.js @@ -0,0 +1,141 @@ +// from https://github.com/pedroetb/node-oauth2-server-example + +var config = { + clients: [{ + clientId: "application", + clientSecret: "secret" + }], + confidentialClients: [{ + clientId: "confidentialApplication", + clientSecret: "topSecret" + }], + tokens: [], + users: [{ + id: "123", + username: "swagger", + password: "password" + }] +} + +/** + * Dump the memory storage content (for debug). + */ + +var dump = function () { + + console.log("clients", config.clients) + console.log("confidentialClients", config.confidentialClients) + console.log("tokens", config.tokens) + console.log("users", config.users) +} + +/* + * Methods used by all grant types. + */ + +var getAccessToken = function (bearerToken, callback) { + + var tokens = config.tokens.filter(function (token) { + + return token.accessToken === bearerToken + }) + + return callback(false, tokens[0]) +} + +var getClient = function (clientId, clientSecret, callback) { + + var clients = config.clients.filter(function (client) { + + return client.clientId === clientId && client.clientSecret === clientSecret + }) + + var confidentialClients = config.confidentialClients.filter(function (client) { + + return client.clientId === clientId && client.clientSecret === clientSecret + }) + + callback(false, clients[0] || confidentialClients[0]) +} + +var grantTypeAllowed = function (clientId, grantType, callback) { + + var clientsSource, + clients = [] + + if (grantType === "password") { + clientsSource = config.clients + } else if (grantType === "client_credentials") { + clientsSource = config.confidentialClients + } + + if (clientsSource) { + clients = clientsSource.filter(function (client) { + + return client.clientId === clientId + }) + } + + callback(false, clients.length) +} + +var saveAccessToken = function (accessToken, clientId, expires, user, callback) { + + config.tokens.push({ + accessToken: accessToken, + expires: expires, + clientId: clientId, + user: user + }) + + callback(false) +} + +/* + * Method used only by password grant type. + */ + +var getUser = function (username, password, callback) { + + var users = config.users.filter(function (user) { + + return user.username === username && user.password === password + }) + + callback(false, users[0]) +} + +/* + * Method used only by client_credentials grant type. + */ + +var getUserFromClient = function (clientId, clientSecret, callback) { + + var clients = config.confidentialClients.filter(function (client) { + + return client.clientId === clientId && client.clientSecret === clientSecret + }) + + var user + + if (clients.length) { + user = { + username: clientId + } + } + + callback(false, user) +} + +/** + * Export model definition object. + */ + +module.exports = { + getAccessToken: getAccessToken, + getClient: getClient, + grantTypeAllowed: grantTypeAllowed, + saveAccessToken: saveAccessToken, + getUser: getUser, + getUserFromClient: getUserFromClient +} \ No newline at end of file diff --git a/test/e2e-cypress/helpers/oauth2-server/swagger.yaml b/test/e2e-cypress/helpers/oauth2-server/swagger.yaml new file mode 100644 index 00000000..314829f5 --- /dev/null +++ b/test/e2e-cypress/helpers/oauth2-server/swagger.yaml @@ -0,0 +1,36 @@ +swagger: "2.0" +host: localhost:3231 +paths: + /password: + get: + summary: OAuth2 Password + security: + - oauthPassword: [] + responses: + 200: + description: OK + schema: + type: string + /application: + get: + summary: OAuth2 Application + security: + - oauthApplication: [] + responses: + 200: + description: OK + schema: + type: string +securityDefinitions: + oauthPassword: + type: oauth2 + flow: password + tokenUrl: /oauth/token + oauthApplication: + type: oauth2 + flow: application + tokenUrl: /oauth/token + oauthImplicit: + type: oauth2 + flow: implicit + authorizationUrl: /oauth/token diff --git a/test/e2e-cypress/plugins/index.js b/test/e2e-cypress/plugins/index.js index fd170fba..852cfa69 100644 --- a/test/e2e-cypress/plugins/index.js +++ b/test/e2e-cypress/plugins/index.js @@ -1,3 +1,4 @@ +const startOAuthServer = require("../helpers/oauth2-server") // *********************************************************** // This example plugins/index.js can be used to load plugins // @@ -12,6 +13,7 @@ // the project's config changing) module.exports = (on, config) => { + startOAuthServer() // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config } diff --git a/test/e2e-cypress/support/index.js b/test/e2e-cypress/support/index.js index a80764cb..83d39ac2 100644 --- a/test/e2e-cypress/support/index.js +++ b/test/e2e-cypress/support/index.js @@ -18,3 +18,10 @@ import "./commands" // Alternatively you can use CommonJS syntax: // require('./commands') + + +// Remove fetch, so Cypress can intercept XHRs +// see https://github.com/cypress-io/cypress/issues/95 +Cypress.on("window:before:load", win => { + win.fetch = null +}) \ No newline at end of file diff --git a/test/e2e-cypress/tests/deep-linking.js b/test/e2e-cypress/tests/features/deep-linking.js similarity index 100% rename from test/e2e-cypress/tests/deep-linking.js rename to test/e2e-cypress/tests/features/deep-linking.js diff --git a/test/e2e-cypress/tests/features/oauth2-flows/application.js b/test/e2e-cypress/tests/features/oauth2-flows/application.js new file mode 100644 index 00000000..90446198 --- /dev/null +++ b/test/e2e-cypress/tests/features/oauth2-flows/application.js @@ -0,0 +1,55 @@ +describe("OAuth2 Application flow", function() { + beforeEach(() => { + cy.server() + cy.route({ + url: "**/oauth/*", + method: "POST" + }).as("tokenRequest") + }) + + it("should make an application flow Authorization header request", () => { + cy + .visit("/?url=http://localhost:3231/swagger.yaml") + .get(".btn.authorize") + .click() + + .get("div.modal-ux-content > div:nth-child(2)").within(() => { + cy.get("#client_id") + .clear() + .type("confidentialApplication") + + .get("#client_secret") + .clear() + .type("topSecret") + + .get("button.btn.modal-btn.auth.authorize.button") + .click() + }) + + cy.get("button.close-modal") + .click() + + .get("#operations-default-get_application") + .click() + + .get(".btn.try-out__btn") + .click() + + .get(".btn.execute") + .click() + + cy.get("@tokenRequest") + .its("request") + .its("body") + .should("equal", "grant_type=client_credentials") + + cy.get("@tokenRequest") + .its("request") + .its("headers") + .its("authorization") + .should("equal", "Basic Y29uZmlkZW50aWFsQXBwbGljYXRpb246dG9wU2VjcmV0") + + .get(".live-responses-table .response-col_status") + .contains("200") + }) +}) \ No newline at end of file diff --git a/test/e2e-cypress/tests/features/oauth2-flows/password.js b/test/e2e-cypress/tests/features/oauth2-flows/password.js new file mode 100644 index 00000000..872899a2 --- /dev/null +++ b/test/e2e-cypress/tests/features/oauth2-flows/password.js @@ -0,0 +1,122 @@ +describe("OAuth2 Password flow", function() { + beforeEach(() => { + cy.server() + cy.route({ + url: "**/oauth/*", + method: "POST" + }).as("tokenRequest") + }) + + it("should make a password flow Authorization header request", () => { + cy + .visit("/?url=http://localhost:3231/swagger.yaml") + .get(".btn.authorize") + .click() + + .get("#oauth_username") + .type("swagger") + + .get("#oauth_password") + .type("password") + + .get("#password_type") + .select("basic") + + .get("#client_id") + .clear() + .type("application") + + .get("#client_secret") + .clear() + .type("secret") + + .get("div.modal-ux-content > div:nth-child(1) > div > div:nth-child(2) > div > div.auth-btn-wrapper > button.btn.modal-btn.auth.authorize.button") + .click() + + .get("button.close-modal") + .click() + + .get("#operations-default-get_password") + .click() + + .get(".btn.try-out__btn") + .click() + + .get(".btn.execute") + .click() + + cy.get("@tokenRequest") + .its("request") + .its("body") + .should("include", "grant_type=password") + .should("include", "username=swagger") + .should("include", "password=password") + .should("not.include", "client_id") + .should("not.include", "client_secret") + + cy.get("@tokenRequest") + .its("request") + .its("headers") + .its("authorization") + .should("equal", "Basic YXBwbGljYXRpb246c2VjcmV0") + + .get(".live-responses-table .response-col_status") + .contains("200") + }) + + it("should make a Password flow request-body request", () => { + cy + .visit("/?url=http://localhost:3231/swagger.yaml") + .get(".btn.authorize") + .click() + + .get("#oauth_username") + .type("swagger") + + .get("#oauth_password") + .type("password") + + .get("#password_type") + .select("request-body") + + .get("#client_id") + .clear() + .type("application") + + .get("#client_secret") + .clear() + .type("secret") + + .get("div.modal-ux-content > div:nth-child(1) > div > div:nth-child(2) > div > div.auth-btn-wrapper > button.btn.modal-btn.auth.authorize.button") + .click() + + .get("button.close-modal") + .click() + + .get("#operations-default-get_password") + .click() + + .get(".btn.try-out__btn") + .click() + + .get(".btn.execute") + .click() + + cy.get("@tokenRequest") + .its("request") + .its("body") + .should("include", "grant_type=password") + .should("include", "username=swagger") + .should("include", "password=password") + .should("include", "client_id=application") + .should("include", "client_secret=secret") + + cy.get("@tokenRequest") + .its("request") + .its("headers") + .should("not.have.property", "authorization") + + .get(".live-responses-table .response-col_status") + .contains("200") + }) +}) \ No newline at end of file
Code