From 061c4d694c64c1615e713011e88f9dca39a62658 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Thu, 26 Oct 2023 08:47:12 -0600 Subject: [PATCH 1/2] manual backport piece by piece no header --- changelog/23700.txt | 3 + ui/app/styles/core.scss | 3 + ui/app/styles/utils/swagger.scss | 6 + .../addon/components/swagger-ui.js | 169 ++++++++---------- .../addon/controllers/index.js | 6 - .../open-api-explorer/addon/routes/index.js | 12 +- .../addon/templates/components/swagger-ui.hbs | 29 +-- .../addon/templates/index.hbs | 2 +- ui/mirage/factories/open-api-explorer.js | 45 +++++ ui/package.json | 6 +- .../acceptance/api-explorer/index-test.js | 22 --- ui/yarn.lock | 25 +-- 12 files changed, 157 insertions(+), 171 deletions(-) create mode 100644 changelog/23700.txt create mode 100644 ui/app/styles/utils/swagger.scss delete mode 100644 ui/lib/open-api-explorer/addon/controllers/index.js create mode 100644 ui/mirage/factories/open-api-explorer.js delete mode 100644 ui/tests/acceptance/api-explorer/index-test.js diff --git a/changelog/23700.txt b/changelog/23700.txt new file mode 100644 index 000000000000..59a69fbcffe3 --- /dev/null +++ b/changelog/23700.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Update flat, shell-quote and swagger-ui-dist packages. Remove swagger-ui styling overrides. +``` \ No newline at end of file diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index b93ae859de43..db92b8554324 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -14,6 +14,9 @@ @import 'bulma/bulma'; @import 'bulma/switch'; +// Open-api styling +@import './utils/swagger'; + // Override Bulma details where appropriate @import './core/alert-banner'; @import './core/generic'; diff --git a/ui/app/styles/utils/swagger.scss b/ui/app/styles/utils/swagger.scss new file mode 100644 index 000000000000..0e363b138e28 --- /dev/null +++ b/ui/app/styles/utils/swagger.scss @@ -0,0 +1,6 @@ +// This file defines the scss for open-api-explorer. + +/* align list items with container */ +.swagger-ember .swagger-ui .wrapper { + padding: 0; +} diff --git a/ui/lib/open-api-explorer/addon/components/swagger-ui.js b/ui/lib/open-api-explorer/addon/components/swagger-ui.js index 69e579fa62e1..6a967c05765b 100644 --- a/ui/lib/open-api-explorer/addon/components/swagger-ui.js +++ b/ui/lib/open-api-explorer/addon/components/swagger-ui.js @@ -1,104 +1,87 @@ -import Component from '@ember/component'; +import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import parseURL from 'core/utils/parse-url'; import config from 'open-api-explorer/config/environment'; +import { guidFor } from '@ember/object/internals'; const { APP } = config; -const SearchFilterPlugin = () => { - return { - fn: { - opsFilter: (taggedOps, phrase) => { - // map over the options and filter out operations where the path doesn't match what's typed - return ( - taggedOps - .map((tagObj) => { - const operations = tagObj.get('operations').filter((operationObj) => { - return operationObj.get('path').includes(phrase); - }); - return tagObj.set('operations', operations); - }) - // then traverse again and remove the top level item if there are no operations left after filtering - .filter((tagObj) => !!tagObj.get('operations').size) - ); - }, - }, - }; -}; +export default class SwaggerUiComponent extends Component { + @service auth; + @service namespace; -const CONFIG = (SwaggerUIBundle, componentInstance, initialFilter) => { - return { - dom_id: `#${componentInstance.elementId}-swagger`, - url: '/v1/sys/internal/specs/openapi', - deepLinking: false, - presets: [SwaggerUIBundle.presets.apis], - plugins: [SwaggerUIBundle.plugins.DownloadUrl, SearchFilterPlugin], - // 'list' expands tags, but not operations - docExpansion: 'list', - operationsSorter: 'alpha', - filter: initialFilter || true, - // this makes sure we show the x-vault- options - showExtensions: true, - // we don't have any models defined currently - defaultModelsExpandDepth: -1, - defaultModelExpandDepth: 1, - requestInterceptor: (req) => { - // we need to add vault authorization header - // and namepace headers for things to work properly - req.headers['X-Vault-Token'] = componentInstance.auth.currentToken; + @tracked swaggerLoading = true; - const namespace = componentInstance.namespaceService.path; - if (namespace && !APP.NAMESPACE_ROOT_URLS.some((str) => req.url.includes(str))) { - req.headers['X-Vault-Namespace'] = namespace; - } - // we want to link to the right JSON in swagger UI so - // it's already been pre-pended - if (!req.loadSpec) { - const { protocol, host, pathname, search } = parseURL(req.url); - //paths in the spec don't have /v1 in them, so we need to add that here - // http(s): vlt.io:4200 /sys/mounts - req.url = `${protocol}//${host}/v1${pathname}${search}`; - } - return req; - }, - onComplete: () => { - componentInstance.set('swaggerLoading', false); - }, - }; -}; + inputId = `${guidFor(this)}-swagger`; -export default Component.extend({ - auth: service(), - namespaceService: service('namespace'), - initialFilter: null, - onFilterChange() {}, - swaggerLoading: true, + SearchFilterPlugin() { + return { + fn: { + opsFilter: (taggedOps, phrase) => { + // map over the options and filter out operations where the path doesn't match what's typed + return ( + taggedOps + .map((tagObj) => { + const operations = tagObj.get('operations').filter((operationObj) => { + return operationObj.get('path').includes(phrase); + }); + return tagObj.set('operations', operations); + }) + // then traverse again and remove the top level item if there are no operations left after filtering + .filter((tagObj) => !!tagObj.get('operations').size) + ); + }, + }, + }; + } - async didInsertElement() { - const { default: SwaggerUIBundle } = await import('swagger-ui-dist/swagger-ui-bundle.js'); - this._super(...arguments); - // trim any initial slashes - const initialFilter = this.initialFilter.replace(/^(\/)+/, ''); - SwaggerUIBundle(CONFIG(SwaggerUIBundle, this, initialFilter)); - }, + CONFIG = (SwaggerUIBundle, componentInstance) => { + return { + dom_id: `#${componentInstance.inputId}`, + url: '/v1/sys/internal/specs/openapi', + deepLinking: false, + presets: [SwaggerUIBundle.presets.apis], + plugins: [SwaggerUIBundle.plugins.DownloadUrl, this.SearchFilterPlugin], + // 'list' expands tags, but not operations + docExpansion: 'list', + operationsSorter: 'alpha', + filter: true, + // this makes sure we show the x-vault- options + showExtensions: true, + // we don't have any models defined currently + defaultModelsExpandDepth: -1, + defaultModelExpandDepth: 1, + requestInterceptor: (req) => { + // we need to add vault authorization header + // and namespace headers for things to work properly + req.headers['X-Vault-Token'] = componentInstance.auth.currentToken; + const namespace = componentInstance.namespace.path; + if (namespace && !APP.NAMESPACE_ROOT_URLS.some((str) => req.url.includes(str))) { + req.headers['X-Vault-Namespace'] = namespace; + } + // we want to link to the right JSON in swagger UI so + // it's already been pre-pended + if (!req.loadSpec) { + const { protocol, host, pathname, search } = parseURL(req.url); + //paths in the spec don't have /v1 in them, so we need to add that here + // http(s): vlt.io:4200 /sys/mounts + req.url = `${protocol}//${host}/v1${pathname}${search}`; + } + return req; + }, + onComplete: () => { + componentInstance.swaggerLoading = false; + }, + }; + }; - actions: { - // sets the filter so the query param is updated so we get sharable URLs - updateFilter(e) { - this.onFilterChange(e.target.value || ''); - }, - proxyEvent(e) { - const swaggerInput = this.element.querySelector('.operation-filter-input'); - // if this breaks because of a react upgrade, - // change this to - //let originalSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; - //originalSetter.call(swaggerInput, e.target.value); - // see post on triggering react events externally for an explanation of - // why this works: https://stackoverflow.com/a/46012210 - const evt = new Event('input', { bubbles: true }); - evt.simulated = true; - swaggerInput.value = e.target.value.replace(/^(\/)+/, ''); - swaggerInput.dispatchEvent(evt); - }, - }, -}); + // using an action to bind the correct "this" context + @action async swaggerInit() { + const { default: SwaggerUIBundle } = await import('swagger-ui-dist/swagger-ui-bundle.js'); + // trim any slashes on the filter value + const configSettings = this.CONFIG(SwaggerUIBundle, this); + SwaggerUIBundle(configSettings); + } +} diff --git a/ui/lib/open-api-explorer/addon/controllers/index.js b/ui/lib/open-api-explorer/addon/controllers/index.js deleted file mode 100644 index 704157fa82e8..000000000000 --- a/ui/lib/open-api-explorer/addon/controllers/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import Controller from '@ember/controller'; - -export default Controller.extend({ - queryParams: ['filter'], - filter: '', -}); diff --git a/ui/lib/open-api-explorer/addon/routes/index.js b/ui/lib/open-api-explorer/addon/routes/index.js index 277ae87c3049..f5836255968a 100644 --- a/ui/lib/open-api-explorer/addon/routes/index.js +++ b/ui/lib/open-api-explorer/addon/routes/index.js @@ -1,11 +1,9 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; -export default Route.extend({ - flashMessages: service(), - // without an empty model hook here, ember likes to use the parent model, and then things get weird with - // query params, so here we're no-op'ing the model hook - model() {}, +export default class OpenApiExplorerIndex extends Route { + @service flashMessages; + afterModel() { const warning = `The "Try it out" functionality in this API explorer will make requests to this Vault server on your behalf. @@ -16,5 +14,5 @@ Your token will also be shown on the screen in the example curl command output.` sticky: true, preformatted: true, }); - }, -}); + } +} diff --git a/ui/lib/open-api-explorer/addon/templates/components/swagger-ui.hbs b/ui/lib/open-api-explorer/addon/templates/components/swagger-ui.hbs index b6fd3b95fda8..da9a912d78df 100644 --- a/ui/lib/open-api-explorer/addon/templates/components/swagger-ui.hbs +++ b/ui/lib/open-api-explorer/addon/templates/components/swagger-ui.hbs @@ -1,30 +1,3 @@ - - -

- - Vault API explorer -

-
-
- - -
-

- - -

-
- -
-
-
Requests use the header @@ -34,5 +7,5 @@ docs for examples. -
+
\ No newline at end of file diff --git a/ui/lib/open-api-explorer/addon/templates/index.hbs b/ui/lib/open-api-explorer/addon/templates/index.hbs index 2a0fa3f0f416..0948e9227b7f 100644 --- a/ui/lib/open-api-explorer/addon/templates/index.hbs +++ b/ui/lib/open-api-explorer/addon/templates/index.hbs @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/ui/mirage/factories/open-api-explorer.js b/ui/mirage/factories/open-api-explorer.js new file mode 100644 index 000000000000..7f47b465a011 --- /dev/null +++ b/ui/mirage/factories/open-api-explorer.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { Factory } from 'ember-cli-mirage'; +/* eslint-disable ember/avoid-leaking-state-in-ember-objects */ +export default Factory.extend({ + openapi: '3.0.2', + info: { + title: 'HashiCorp Vault API', + description: 'HTTP API that gives you full access to Vault. All API routes are prefixed with `/v1/`.', + version: '1.0.0', + license: { + name: 'Mozilla Public License 2.0', + url: 'https://www.mozilla.org/en-US/MPL/2.0', + }, + }, + paths: { + '/auth/token/create': { + description: 'The token create path is used to create new tokens.', + post: { + summary: 'The token create path is used to create new tokens.', + tags: ['auth'], + responses: { + 200: { + description: 'OK', + }, + }, + }, + }, + '/secret/data/{path}': { + description: 'Location of a secret.', + post: { + summary: 'Location of a secret.', + tags: ['secret'], + responses: { + 200: { + description: 'OK', + }, + }, + }, + }, + }, +}); diff --git a/ui/package.json b/ui/package.json index 673f692d72df..b32ee3287eee 100644 --- a/ui/package.json +++ b/ui/package.json @@ -176,7 +176,7 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-qunit": "^7.2.0", "filesize": "^4.2.1", - "flat": "^4.1.0", + "flat": "^6.0.1", "jsondiffpatch": "^0.4.1", "jsonlint": "^1.6.3", "loader.js": "^4.7.0", @@ -192,10 +192,10 @@ "qunit-dom": "^2.0.0", "route-recognizer": "^0.3.4", "sass-svg-uri": "^1.0.0", - "shell-quote": "^1.6.1", + "shell-quote": "^1.8.1", "string.prototype.endswith": "^0.2.0", "string.prototype.startswith": "^0.2.0", - "swagger-ui-dist": "^3.36.2", + "swagger-ui-dist": "^5.9.0", "text-encoder-lite": "2.0.0", "typescript": "^4.8.4", "walk-sync": "^2.0.2", diff --git a/ui/tests/acceptance/api-explorer/index-test.js b/ui/tests/acceptance/api-explorer/index-test.js deleted file mode 100644 index daebfe8947f0..000000000000 --- a/ui/tests/acceptance/api-explorer/index-test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { find, fillIn, visit, waitUntil } from '@ember/test-helpers'; -import { module, test } from 'qunit'; -import { setupApplicationTest } from 'ember-qunit'; - -import authPage from 'vault/tests/pages/auth'; - -module('Acceptance | API Explorer', function (hooks) { - setupApplicationTest(hooks); - - hooks.beforeEach(function () { - return authPage.login(); - }); - - test('it filters paths after swagger-ui is loaded', async function (assert) { - await visit('/vault/api-explorer'); - await waitUntil(() => { - return find('[data-test-filter-input]').disabled === false; - }); - await fillIn('[data-test-filter-input]', 'sys/health'); - assert.dom('.opblock').exists({ count: 1 }, 'renders a single opblock for sys/health'); - }); -}); diff --git a/ui/yarn.lock b/ui/yarn.lock index b55cf6e1716a..0cf1d020c68a 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -11515,12 +11515,10 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flat@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" - integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== - dependencies: - is-buffer "~2.0.3" +flat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-6.0.1.tgz#09070cf918293b401577f20843edeadf4d3e8755" + integrity sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw== flatted@^3.1.0: version "3.1.1" @@ -12769,7 +12767,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^2.0.0, is-buffer@~2.0.3: +is-buffer@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -17067,6 +17065,11 @@ shell-quote@^1.6.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -17774,10 +17777,10 @@ svgo@1.3.0: unquote "~1.1.1" util.promisify "~1.0.0" -swagger-ui-dist@^3.36.2: - version "3.48.0" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.48.0.tgz#3d23194d86ec1e76b926a345578c290ef3fc6e98" - integrity sha512-UgpKIQW5RAb4nYRG8B615blmQzct0DNuvtX4904Fe2aMWAVfWeKHKl4kwzFXuBJgr2WYWTwM1PnhZ+qqkLrpPg== +swagger-ui-dist@^5.9.0: + version "5.9.1" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz#d0bcd614e3752da02df141846348f84468ae815e" + integrity sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw== symbol-tree@^3.2.4: version "3.2.4" From 6eeb82047de2b4729f5a6b9f5fdedc10208af83a Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Thu, 26 Oct 2023 08:51:52 -0600 Subject: [PATCH 2/2] missed two files --- .../open-api-explorer/addon/styles/addon.css | 152 ------------------ .../open-api-explorer/swagger-ui-test.js | 33 ++++ 2 files changed, 33 insertions(+), 152 deletions(-) delete mode 100644 ui/lib/open-api-explorer/addon/styles/addon.css create mode 100644 ui/tests/integration/components/open-api-explorer/swagger-ui-test.js diff --git a/ui/lib/open-api-explorer/addon/styles/addon.css b/ui/lib/open-api-explorer/addon/styles/addon.css deleted file mode 100644 index 25d1d8689828..000000000000 --- a/ui/lib/open-api-explorer/addon/styles/addon.css +++ /dev/null @@ -1,152 +0,0 @@ -.swagger-ember .swagger-ui .wrapper { - padding: 0; -} - -.swagger-ember .swagger-ui .info { - margin: 25px 0; -} - -/*hide the swagger-ui headers*/ -.swagger-ember .swagger-ui .filter-container, -.swagger-ember .swagger-ui .information-container.wrapper { - display: none; -} - -/*some general de-rounding and removing backgrounds and drop shadows*/ -.swagger-ember .swagger-ui .btn { - border-width: 1px; - box-shadow: none; - border-radius: 0px; -} - -.swagger-ember .swagger-ui .opblock { - background: none; - border-width: 1px; - border-radius: 2px; - box-shadow: none; -} - - -/*customize method, path, description*/ -.swagger-ember .swagger-ui .opblock .opblock-summary, -.swagger-ember .swagger-ui .opblock .opblock-summary-description { - display: block; - margin: 0; - padding: 0; -} - -.swagger-ember .swagger-ui .opblock .opblock-summary { - padding: 1rem; -} - -.swagger-ember .swagger-ui .opblock .opblock-summary-description { - font-size: 14px; -} - -.swagger-ember .swagger-ui .opblock .opblock-summary-method, -.swagger-ember .swagger-ui .opblock .opblock-summary-path{ - display: inline-block; - margin: 0; - padding: 0; -} - -.swagger-ember .swagger-ui .opblock .opblock-summary-method { - border-radius: 1px; - min-width: auto; - text-align: left; - font-size: 10px; - box-shadow: 0 0 0 1px currentColor; - position: relative; - top: -2px; - padding: 0 2px; - margin-right: 8px; -} - -/*make tags look like list items */ -.swagger-ember .swagger-ui .opblock-tag{ - font-size: 16px; -} - -.swagger-ember .swagger-ui .opblock-tag-section .opblock-tag { - color: #0a0a0a; - font-weight: 600 !important; - font-size: 1rem !important; - transition: box-shadow 150ms, margin 150ms, padding 150ms; - will-change: box-shadow, margin, padding; - background-color: white; - border-radius: 0; - padding: 1.25rem; - margin: 0; -} - -.swagger-ember .swagger-ui .opblock-tag:hover, -.swagger-ember .swagger-ui .opblock-tag:focus, -.swagger-ember .swagger-ui .opblock-tag:active { - margin-left: -0.75rem !important; - margin-right: -0.75rem !important; - padding-left: 0.75rem; - padding-right: 0.75rem; - position: relative; - box-shadow: 0 2px 0 -1px #BAC1CC, 0 -2px 0 -1px #BAC1CC, 0 0 0 1px #BAC1CC, 0 8px 4px -4px rgba(10, 10, 10, 0.1), 0 6px 8px -2px rgba(10, 10, 10, 0.05); -} - -/*shrink the size of the arrows*/ -.swagger-ember .swagger-ui .expand-methods svg, -.swagger-ember .swagger-ui .expand-operation svg { - height: 12px; - width: 12px; -} - - -/*operation box - GET (blue) */ -.swagger-ember .swagger-ui .opblock.opblock-get { - background: #f5f8ff; - border: 1px solid #bfd4ff; -} - -/*operation label*/ -.swagger-ember .swagger-ui .opblock.opblock-get .opblock-summary-method { - color: #1563ff; - background: none; -} - /*and expanded tab highlight */ -.swagger-ember .swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span::after { - background: #1563ff; -} - - -/*operation box - POST (green) */ -.swagger-ember .swagger-ui .opblock.opblock-post { - background: #fafdfa; - border: 1px solid #c6e9c9; -} -.swagger-ember .swagger-ui .opblock.opblock-post .opblock-summary-method { - color: #2eb039; - background: none; -} -.swagger-ember .swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span::after { - background: #2eb039; -} - -/*operation box - POST (red) */ -.swagger-ember .swagger-ui .opblock.opblock-delete { - background: #fdfafb; - border: 1px solid #f9ecee; -} -.swagger-ember .swagger-ui .opblock.opblock-delete .opblock-summary-method { - color: #c73445; - background: none; -} -.swagger-ember .swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span::after { - background: #c73445; -} - -/*remove "LOADING" from initial loading spinner*/ -.swagger-ember .swagger-ui .loading-container .loading::after { - content: ""; -} - -/*add text about requests to a live vault server*/ -.swagger-ember .swagger-ui .btn.execute::after { - content: " - send a request with your token to Vault." -} diff --git a/ui/tests/integration/components/open-api-explorer/swagger-ui-test.js b/ui/tests/integration/components/open-api-explorer/swagger-ui-test.js new file mode 100644 index 000000000000..0aabd0766bf3 --- /dev/null +++ b/ui/tests/integration/components/open-api-explorer/swagger-ui-test.js @@ -0,0 +1,33 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'vault/tests/helpers'; +import { waitUntil, find } from '@ember/test-helpers'; +import { setupEngine } from 'ember-engines/test-support'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | open-api-explorer | swagger-ui', function (hooks) { + setupRenderingTest(hooks); + setupEngine(hooks, 'open-api-explorer'); + setupMirage(hooks); + hooks.beforeEach(function () { + this.store = this.owner.lookup('service:store'); + }); + + test('it renders', async function (assert) { + assert.expect(2); + const openApiResponse = this.server.create('open-api-explorer'); + this.server.get('sys/internal/specs/openapi', () => { + return openApiResponse; + }); + + await render(hbs``, { + owner: this.engine, + }); + + await waitUntil(() => find('[data-test-swagger-ui]')); + assert.dom('[data-test-swagger-ui]').exists('renders component'); + await waitUntil(() => find('.operation-filter-input')); + assert.dom('.opblock-post').exists({ count: 2 }, 'renders two blocks'); + }); +});