From eaadfb668e66114e8af2b5e1d1536fd54988843d Mon Sep 17 00:00:00 2001 From: Noelle Daley Date: Mon, 29 Apr 2024 10:47:29 -0700 Subject: [PATCH] [UI] fix filter on api explorer (#26657) * fix: filter on api explorer * cleanup: improve efficiency of swagger-ui filtering * tests: add swagger-ui search tests --- .../addon/components/swagger-ui.js | 30 ++++++++----- .../open-api-explorer/swagger-ui-test.js | 45 ++++++++++++++----- 2 files changed, 52 insertions(+), 23 deletions(-) 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 4910ac6bf1..dae7a86ca0 100644 --- a/ui/lib/open-api-explorer/addon/components/swagger-ui.js +++ b/ui/lib/open-api-explorer/addon/components/swagger-ui.js @@ -26,18 +26,24 @@ export default class SwaggerUiComponent extends Component { 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.operations.filter((operationObj) => { - return operationObj.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.operations.size) - ); + const filteredOperations = taggedOps.reduce((acc, tagObj) => { + const operations = tagObj.get('operations'); + + // filter out operations where the path doesn't match search phrase + const operationsWithMatchingPath = operations.filter((operationObj) => { + const path = operationObj.get('path'); + return path.includes(phrase); + }); + + // if there are any operations left after filtering, add the tagObj to the accumulator + if (operationsWithMatchingPath.size > 0) { + acc.push(tagObj.set('operations', operationsWithMatchingPath)); + } + + return acc; + }, []); + + return filteredOperations; }, }, }; 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 index a78cdc3a6e..2c53c0ffa1 100644 --- a/ui/tests/integration/components/open-api-explorer/swagger-ui-test.js +++ b/ui/tests/integration/components/open-api-explorer/swagger-ui-test.js @@ -8,31 +8,54 @@ 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 { render, fillIn } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; +const SELECTORS = { + container: '[data-test-swagger-ui]', + searchInput: '.operation-filter-input', + apiPathBlock: '.opblock-post', +}; + 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, - }); + this.totalApiPaths = Object.keys(openApiResponse.paths).length; - 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'); + this.renderComponent = async () => { + await render(hbs``, { + owner: this.engine, + }); + }; + }); + + test('it renders', async function (assert) { + await this.renderComponent(); + + await waitUntil(() => find(SELECTORS.container)); + + assert.dom(SELECTORS.container).exists('renders component'); + assert.dom(SELECTORS.apiPathBlock).exists({ count: this.totalApiPaths }, 'renders all api paths'); + }); + + test('it can search', async function (assert) { + await this.renderComponent(); + + await waitUntil(() => find(SELECTORS.searchInput)); + await fillIn(SELECTORS.searchInput, 'token'); + + // for some reason search results are not rendered immediately in tests, + // so asserting that the search input has the value we expect is the best we can do here + // if the search fn breaks, this test will fail + assert.dom(SELECTORS.searchInput).hasValue('token', 'search input has value'); }); });