/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ /* eslint-disable ember-a11y-testing/a11y-audit-called */ /* eslint-disable qunit/require-expect */ import { module, test } from 'qunit'; import { currentURL, triggerEvent, visit } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; import Layout from 'nomad-ui/tests/pages/layout'; import JobsList from 'nomad-ui/tests/pages/jobs/list'; import { selectSearch } from 'ember-power-select/test-support'; import Response from 'ember-cli-mirage/response'; module('Acceptance | search', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); test('search exposes and navigates to results from the fuzzy search endpoint', async function (assert) { server.create('node', { name: 'xyz' }); const otherNode = server.create('node', { name: 'ghi' }); server.create('namespace'); server.create('namespace', { id: 'dev' }); server.create('job', { id: 'vwxyz', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1, }); server.create('job', { id: 'xyz', name: 'xyz job', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1, }); server.create('job', { id: 'xyzw', name: 'xyzw job', namespaceId: 'dev', groupsCount: 1, groupTaskCount: 1, }); server.create('job', { id: 'abc', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1, }); const firstAllocation = server.schema.allocations.all().models[0]; const firstTaskGroup = server.schema.taskGroups.all().models[0]; const namespacedTaskGroup = server.schema.taskGroups.all().models[2]; server.create('csi-plugin', { id: 'xyz-plugin', createVolumes: false }); await visit('/'); await selectSearch(Layout.navbar.search.scope, 'xy'); Layout.navbar.search.as((search) => { assert.equal(search.groups.length, 5); search.groups[0].as((jobs) => { assert.equal(jobs.name, 'Jobs (3)'); assert.equal(jobs.options.length, 3); assert.equal(jobs.options[0].text, 'default > vwxyz'); assert.equal(jobs.options[1].text, 'default > xyz job'); assert.equal(jobs.options[2].text, 'dev > xyzw job'); }); search.groups[1].as((clients) => { assert.equal(clients.name, 'Clients (1)'); assert.equal(clients.options.length, 1); assert.equal(clients.options[0].text, 'xyz'); }); search.groups[2].as((allocs) => { assert.equal(allocs.name, 'Allocations (0)'); assert.equal(allocs.options.length, 0); }); search.groups[3].as((groups) => { assert.equal(groups.name, 'Task Groups (0)'); assert.equal(groups.options.length, 0); }); search.groups[4].as((plugins) => { assert.equal(plugins.name, 'CSI Plugins (1)'); assert.equal(plugins.options.length, 1); assert.equal(plugins.options[0].text, 'xyz-plugin'); }); }); await Layout.navbar.search.groups[0].options[1].click(); assert.equal(currentURL(), '/jobs/xyz@default'); await selectSearch(Layout.navbar.search.scope, 'xy'); await Layout.navbar.search.groups[0].options[2].click(); assert.equal(currentURL(), '/jobs/xyzw@dev'); await selectSearch(Layout.navbar.search.scope, otherNode.name); await Layout.navbar.search.groups[1].options[0].click(); assert.equal(currentURL(), `/clients/${otherNode.id}`); await selectSearch(Layout.navbar.search.scope, firstAllocation.name); assert.equal( Layout.navbar.search.groups[2].options[0].text, `${firstAllocation.namespace} > ${firstAllocation.name}` ); await Layout.navbar.search.groups[2].options[0].click(); assert.equal(currentURL(), `/allocations/${firstAllocation.id}`); await selectSearch(Layout.navbar.search.scope, firstTaskGroup.name); assert.equal( Layout.navbar.search.groups[3].options[0].text, `default > vwxyz > ${firstTaskGroup.name}` ); await Layout.navbar.search.groups[3].options[0].click(); assert.equal(currentURL(), `/jobs/vwxyz@default/${firstTaskGroup.name}`); await selectSearch(Layout.navbar.search.scope, namespacedTaskGroup.name); assert.equal( Layout.navbar.search.groups[3].options[0].text, `dev > xyzw > ${namespacedTaskGroup.name}` ); await Layout.navbar.search.groups[3].options[0].click(); assert.equal(currentURL(), `/jobs/xyzw@dev/${namespacedTaskGroup.name}`); await selectSearch(Layout.navbar.search.scope, 'xy'); await Layout.navbar.search.groups[4].options[0].click(); assert.equal(currentURL(), '/csi/plugins/xyz-plugin'); const fuzzySearchQueries = server.pretender.handledRequests.filterBy( 'url', '/v1/search/fuzzy' ); const featureDetectionQueries = fuzzySearchQueries.filter((request) => request.requestBody.includes('feature-detection-query') ); assert.equal( featureDetectionQueries.length, 1, 'expect the feature detection query to only run once' ); const realFuzzySearchQuery = fuzzySearchQueries[1]; assert.deepEqual(JSON.parse(realFuzzySearchQuery.requestBody), { Context: 'all', Namespace: '*', Text: 'xy', }); }); test('search does not perform a request when only one character has been entered', async function (assert) { await visit('/'); await selectSearch(Layout.navbar.search.scope, 'q'); assert.ok(Layout.navbar.search.noOptionsShown); assert.equal( server.pretender.handledRequests.filterBy('url', '/v1/search/fuzzy') .length, 1, 'expect the feature detection query' ); }); test('when fuzzy search is disabled on the server, the search control is hidden', async function (assert) { server.post('/search/fuzzy', function () { return new Response(500, {}, ''); }); await visit('/'); assert.ok(Layout.navbar.search.isHidden); }); test('results are truncated at 10 per group', async function (assert) { server.create('node', { name: 'xyz' }); for (let i = 0; i < 11; i++) { server.create('job', { id: `job-${i}`, namespaceId: 'default' }); } await visit('/'); await selectSearch(Layout.navbar.search.scope, 'job'); Layout.navbar.search.as((search) => { search.groups[0].as((jobs) => { assert.equal(jobs.name, 'Jobs (showing 10 of 11)'); assert.equal(jobs.options.length, 10); }); }); }); test('server-side truncation is indicated in the group label', async function (assert) { server.create('node', { name: 'xyz' }); for (let i = 0; i < 21; i++) { server.create('job', { id: `job-${i}`, namespaceId: 'default' }); } await visit('/'); await selectSearch(Layout.navbar.search.scope, 'job'); Layout.navbar.search.as((search) => { search.groups[0].as((jobs) => { assert.equal(jobs.name, 'Jobs (showing 10 of 20+)'); }); }); }); test('clicking the search field starts search immediately', async function (assert) { await visit('/'); assert.notOk(Layout.navbar.search.field.isPresent); await Layout.navbar.search.click(); assert.ok(Layout.navbar.search.field.isPresent); }); test('pressing slash starts a search', async function (assert) { await visit('/'); assert.notOk(Layout.navbar.search.field.isPresent); await triggerEvent('.page-layout', 'keydown', { key: '/' }); assert.ok(Layout.navbar.search.field.isPresent); }); test('pressing slash when an input element is focused does not start a search', async function (assert) { server.create('node'); server.create('job'); await visit('/'); assert.notOk(Layout.navbar.search.field.isPresent); await JobsList.search.click(); await JobsList.search.keydown({ key: '/' }); assert.notOk(Layout.navbar.search.field.isPresent); }); });