github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/search-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 /* eslint-disable ember-a11y-testing/a11y-audit-called */ 7 /* eslint-disable qunit/require-expect */ 8 import { module, test } from 'qunit'; 9 import { currentURL, triggerEvent, visit } from '@ember/test-helpers'; 10 import { setupApplicationTest } from 'ember-qunit'; 11 import { setupMirage } from 'ember-cli-mirage/test-support'; 12 import Layout from 'nomad-ui/tests/pages/layout'; 13 import JobsList from 'nomad-ui/tests/pages/jobs/list'; 14 import { selectSearch } from 'ember-power-select/test-support'; 15 import Response from 'ember-cli-mirage/response'; 16 17 module('Acceptance | search', function (hooks) { 18 setupApplicationTest(hooks); 19 setupMirage(hooks); 20 21 test('search exposes and navigates to results from the fuzzy search endpoint', async function (assert) { 22 server.create('node-pool'); 23 server.create('node', { name: 'xyz' }); 24 const otherNode = server.create('node', { name: 'ghi' }); 25 26 server.create('namespace'); 27 server.create('namespace', { id: 'dev' }); 28 29 server.create('job', { 30 id: 'vwxyz', 31 namespaceId: 'default', 32 groupsCount: 1, 33 groupTaskCount: 1, 34 }); 35 server.create('job', { 36 id: 'xyz', 37 name: 'xyz job', 38 namespaceId: 'default', 39 groupsCount: 1, 40 groupTaskCount: 1, 41 }); 42 server.create('job', { 43 id: 'xyzw', 44 name: 'xyzw job', 45 namespaceId: 'dev', 46 groupsCount: 1, 47 groupTaskCount: 1, 48 }); 49 server.create('job', { 50 id: 'abc', 51 namespaceId: 'default', 52 groupsCount: 1, 53 groupTaskCount: 1, 54 }); 55 56 const firstAllocation = server.schema.allocations.all().models[0]; 57 const firstTaskGroup = server.schema.taskGroups.all().models[0]; 58 const namespacedTaskGroup = server.schema.taskGroups.all().models[2]; 59 60 server.create('csi-plugin', { id: 'xyz-plugin', createVolumes: false }); 61 62 await visit('/'); 63 64 await selectSearch(Layout.navbar.search.scope, 'xy'); 65 66 Layout.navbar.search.as((search) => { 67 assert.equal(search.groups.length, 5); 68 69 search.groups[0].as((jobs) => { 70 assert.equal(jobs.name, 'Jobs (3)'); 71 assert.equal(jobs.options.length, 3); 72 assert.equal(jobs.options[0].text, 'default > vwxyz'); 73 assert.equal(jobs.options[1].text, 'default > xyz job'); 74 assert.equal(jobs.options[2].text, 'dev > xyzw job'); 75 }); 76 77 search.groups[1].as((clients) => { 78 assert.equal(clients.name, 'Clients (1)'); 79 assert.equal(clients.options.length, 1); 80 assert.equal(clients.options[0].text, 'xyz'); 81 }); 82 83 search.groups[2].as((allocs) => { 84 assert.equal(allocs.name, 'Allocations (0)'); 85 assert.equal(allocs.options.length, 0); 86 }); 87 88 search.groups[3].as((groups) => { 89 assert.equal(groups.name, 'Task Groups (0)'); 90 assert.equal(groups.options.length, 0); 91 }); 92 93 search.groups[4].as((plugins) => { 94 assert.equal(plugins.name, 'CSI Plugins (1)'); 95 assert.equal(plugins.options.length, 1); 96 assert.equal(plugins.options[0].text, 'xyz-plugin'); 97 }); 98 }); 99 100 await Layout.navbar.search.groups[0].options[1].click(); 101 assert.equal(currentURL(), '/jobs/xyz@default'); 102 103 await selectSearch(Layout.navbar.search.scope, 'xy'); 104 await Layout.navbar.search.groups[0].options[2].click(); 105 assert.equal(currentURL(), '/jobs/xyzw@dev'); 106 107 await selectSearch(Layout.navbar.search.scope, otherNode.name); 108 await Layout.navbar.search.groups[1].options[0].click(); 109 assert.equal(currentURL(), `/clients/${otherNode.id}`); 110 111 await selectSearch(Layout.navbar.search.scope, firstAllocation.name); 112 assert.equal( 113 Layout.navbar.search.groups[2].options[0].text, 114 `${firstAllocation.namespace} > ${firstAllocation.name}` 115 ); 116 await Layout.navbar.search.groups[2].options[0].click(); 117 assert.equal(currentURL(), `/allocations/${firstAllocation.id}`); 118 119 await selectSearch(Layout.navbar.search.scope, firstTaskGroup.name); 120 assert.equal( 121 Layout.navbar.search.groups[3].options[0].text, 122 `default > vwxyz > ${firstTaskGroup.name}` 123 ); 124 await Layout.navbar.search.groups[3].options[0].click(); 125 assert.equal(currentURL(), `/jobs/vwxyz@default/${firstTaskGroup.name}`); 126 127 await selectSearch(Layout.navbar.search.scope, namespacedTaskGroup.name); 128 assert.equal( 129 Layout.navbar.search.groups[3].options[0].text, 130 `dev > xyzw > ${namespacedTaskGroup.name}` 131 ); 132 await Layout.navbar.search.groups[3].options[0].click(); 133 assert.equal(currentURL(), `/jobs/xyzw@dev/${namespacedTaskGroup.name}`); 134 135 await selectSearch(Layout.navbar.search.scope, 'xy'); 136 await Layout.navbar.search.groups[4].options[0].click(); 137 assert.equal(currentURL(), '/csi/plugins/xyz-plugin'); 138 139 const fuzzySearchQueries = server.pretender.handledRequests.filterBy( 140 'url', 141 '/v1/search/fuzzy' 142 ); 143 144 const featureDetectionQueries = fuzzySearchQueries.filter((request) => 145 request.requestBody.includes('feature-detection-query') 146 ); 147 148 assert.equal( 149 featureDetectionQueries.length, 150 1, 151 'expect the feature detection query to only run once' 152 ); 153 154 const realFuzzySearchQuery = fuzzySearchQueries[1]; 155 156 assert.deepEqual(JSON.parse(realFuzzySearchQuery.requestBody), { 157 Context: 'all', 158 Namespace: '*', 159 Text: 'xy', 160 }); 161 }); 162 163 test('search does not perform a request when only one character has been entered', async function (assert) { 164 await visit('/'); 165 166 await selectSearch(Layout.navbar.search.scope, 'q'); 167 168 assert.ok(Layout.navbar.search.noOptionsShown); 169 assert.equal( 170 server.pretender.handledRequests.filterBy('url', '/v1/search/fuzzy') 171 .length, 172 1, 173 'expect the feature detection query' 174 ); 175 }); 176 177 test('when fuzzy search is disabled on the server, the search control is hidden', async function (assert) { 178 server.post('/search/fuzzy', function () { 179 return new Response(500, {}, ''); 180 }); 181 182 await visit('/'); 183 184 assert.ok(Layout.navbar.search.isHidden); 185 }); 186 187 test('results are truncated at 10 per group', async function (assert) { 188 server.create('node-pool'); 189 server.create('node', { name: 'xyz' }); 190 191 for (let i = 0; i < 11; i++) { 192 server.create('job', { id: `job-${i}`, namespaceId: 'default' }); 193 } 194 195 await visit('/'); 196 197 await selectSearch(Layout.navbar.search.scope, 'job'); 198 199 Layout.navbar.search.as((search) => { 200 search.groups[0].as((jobs) => { 201 assert.equal(jobs.name, 'Jobs (showing 10 of 11)'); 202 assert.equal(jobs.options.length, 10); 203 }); 204 }); 205 }); 206 207 test('server-side truncation is indicated in the group label', async function (assert) { 208 server.create('node-pool'); 209 server.create('node', { name: 'xyz' }); 210 211 for (let i = 0; i < 21; i++) { 212 server.create('job', { id: `job-${i}`, namespaceId: 'default' }); 213 } 214 215 await visit('/'); 216 217 await selectSearch(Layout.navbar.search.scope, 'job'); 218 219 Layout.navbar.search.as((search) => { 220 search.groups[0].as((jobs) => { 221 assert.equal(jobs.name, 'Jobs (showing 10 of 20+)'); 222 }); 223 }); 224 }); 225 226 test('clicking the search field starts search immediately', async function (assert) { 227 await visit('/'); 228 229 assert.notOk(Layout.navbar.search.field.isPresent); 230 231 await Layout.navbar.search.click(); 232 233 assert.ok(Layout.navbar.search.field.isPresent); 234 }); 235 236 test('pressing slash starts a search', async function (assert) { 237 await visit('/'); 238 239 assert.notOk(Layout.navbar.search.field.isPresent); 240 241 await triggerEvent('.page-layout', 'keydown', { key: '/' }); 242 243 assert.ok(Layout.navbar.search.field.isPresent); 244 }); 245 246 test('pressing slash when an input element is focused does not start a search', async function (assert) { 247 server.create('node-pool'); 248 server.create('node'); 249 server.create('job'); 250 251 await visit('/'); 252 253 assert.notOk(Layout.navbar.search.field.isPresent); 254 255 await JobsList.search.click(); 256 await JobsList.search.keydown({ key: '/' }); 257 258 assert.notOk(Layout.navbar.search.field.isPresent); 259 }); 260 });