github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/keyboard-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 /* eslint-disable qunit/require-expect */ 7 import { module, test } from 'qunit'; 8 import { setupApplicationTest } from 'ember-qunit'; 9 import { 10 click, 11 currentURL, 12 visit, 13 triggerEvent, 14 triggerKeyEvent, 15 findAll, 16 } from '@ember/test-helpers'; 17 import { setupMirage } from 'ember-cli-mirage/test-support'; 18 import Layout from 'nomad-ui/tests/pages/layout'; 19 import percySnapshot from '@percy/ember'; 20 import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit'; 21 import faker from 'nomad-ui/mirage/faker'; 22 23 module('Acceptance | keyboard', function (hooks) { 24 setupApplicationTest(hooks); 25 setupMirage(hooks); 26 27 module('modal', function () { 28 test('Opening and closing shortcuts modal with key commands', async function (assert) { 29 faker.seed(1); 30 assert.expect(4); 31 await visit('/'); 32 assert.notOk(Layout.keyboard.modalShown); 33 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 34 assert.ok(Layout.keyboard.modalShown); 35 await percySnapshot(assert); 36 await a11yAudit(assert); 37 await triggerEvent('.page-layout', 'keydown', { key: 'Escape' }); 38 assert.notOk(Layout.keyboard.modalShown); 39 }); 40 41 test('closing shortcuts modal by clicking dismiss', async function (assert) { 42 await visit('/'); 43 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 44 assert.ok(Layout.keyboard.modalShown); 45 assert.dom('button.dismiss').isFocused(); 46 await click('button.dismiss'); 47 assert.notOk(Layout.keyboard.modalShown); 48 }); 49 50 test('closing shortcuts modal by clicking outside', async function (assert) { 51 await visit('/'); 52 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 53 assert.ok(Layout.keyboard.modalShown); 54 await click('.page-layout'); 55 assert.notOk(Layout.keyboard.modalShown); 56 }); 57 }); 58 59 module('Enable/Disable', function (enableDisableHooks) { 60 enableDisableHooks.beforeEach(function () { 61 window.localStorage.clear(); 62 }); 63 64 test('Shortcuts work by default and stops working when disabled', async function (assert) { 65 await visit('/'); 66 67 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 68 await triggerEvent('.page-layout', 'keydown', { key: 'c' }); 69 assert.equal( 70 currentURL(), 71 `/clients`, 72 'end up on the clients page after typing g c' 73 ); 74 assert.notOk(Layout.keyboard.modalShown); 75 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 76 assert.ok(Layout.keyboard.modalShown); 77 assert.dom('[data-test-enable-shortcuts-toggle]').hasClass('is-active'); 78 await click('[data-test-enable-shortcuts-toggle]'); 79 assert 80 .dom('[data-test-enable-shortcuts-toggle]') 81 .doesNotHaveClass('is-active'); 82 await triggerEvent('.page-layout', 'keydown', { key: 'Escape' }); 83 assert.notOk(Layout.keyboard.modalShown); 84 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 85 await triggerEvent('.page-layout', 'keydown', { key: 'j' }); 86 assert.equal( 87 currentURL(), 88 `/clients`, 89 'typing g j did not bring you back to the jobs page, since shortcuts are disabled' 90 ); 91 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 92 await click('[data-test-enable-shortcuts-toggle]'); 93 assert.dom('[data-test-enable-shortcuts-toggle]').hasClass('is-active'); 94 await triggerEvent('.page-layout', 'keydown', { key: 'Escape' }); 95 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 96 await triggerEvent('.page-layout', 'keydown', { key: 'j' }); 97 assert.equal( 98 currentURL(), 99 `/jobs`, 100 'typing g j brings me to the jobs page after re-enabling shortcuts' 101 ); 102 }); 103 }); 104 105 module('Local storage bind/rebind', function (rebindHooks) { 106 rebindHooks.beforeEach(function () { 107 window.localStorage.clear(); 108 }); 109 110 test('You can rebind shortcuts', async function (assert) { 111 await visit('/'); 112 113 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 114 await triggerEvent('.page-layout', 'keydown', { key: 'c' }); 115 assert.equal( 116 currentURL(), 117 `/clients`, 118 'end up on the clients page after typing g c' 119 ); 120 121 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 122 await triggerEvent('.page-layout', 'keydown', { key: 'j' }); 123 assert.equal( 124 currentURL(), 125 `/jobs`, 126 'end up on the clients page after typing g j' 127 ); 128 129 assert.notOk(Layout.keyboard.modalShown); 130 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 131 assert.ok(Layout.keyboard.modalShown); 132 133 await click( 134 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 135 ); 136 137 triggerEvent('.page-layout', 'keydown', { key: 'r' }); 138 triggerEvent('.page-layout', 'keydown', { key: 'o' }); 139 triggerEvent('.page-layout', 'keydown', { key: 'f' }); 140 triggerEvent('.page-layout', 'keydown', { key: 'l' }); 141 await triggerEvent('.page-layout', 'keydown', { key: 'Enter' }); 142 assert 143 .dom( 144 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 145 ) 146 .hasText('r o f l'); 147 148 assert.equal( 149 currentURL(), 150 `/jobs`, 151 'typing g c does not do anything, since I re-bound the shortcut' 152 ); 153 154 triggerEvent('.page-layout', 'keydown', { key: 'r' }); 155 triggerEvent('.page-layout', 'keydown', { key: 'o' }); 156 triggerEvent('.page-layout', 'keydown', { key: 'f' }); 157 await triggerEvent('.page-layout', 'keydown', { key: 'l' }); 158 159 assert.equal( 160 currentURL(), 161 `/clients`, 162 'typing the newly bound shortcut brings me to clients' 163 ); 164 165 await click( 166 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 167 ); 168 169 triggerEvent('.page-layout', 'keydown', { key: 'n' }); 170 triggerEvent('.page-layout', 'keydown', { key: 'o' }); 171 triggerEvent('.page-layout', 'keydown', { key: 'p' }); 172 triggerEvent('.page-layout', 'keydown', { key: 'e' }); 173 await triggerEvent('.page-layout', 'keydown', { key: 'Escape' }); 174 assert 175 .dom( 176 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 177 ) 178 .hasText( 179 'r o f l', 180 'text unchanged when I hit escape during recording' 181 ); 182 183 await click( 184 '[data-test-command-label="Go to Clients"] button.reset-to-default' 185 ); 186 assert 187 .dom( 188 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 189 ) 190 .hasText('g c', 'Resetting to default rebinds the shortcut'); 191 }); 192 193 test('Rebound shortcuts persist from localStorage', async function (assert) { 194 window.localStorage.setItem( 195 'keyboard.command.Go to Clients', 196 JSON.stringify(['b', 'o', 'o', 'p']) 197 ); 198 await visit('/'); 199 200 triggerEvent('.page-layout', 'keydown', { key: 'g' }); 201 await triggerEvent('.page-layout', 'keydown', { key: 'c' }); 202 assert.equal( 203 currentURL(), 204 `/jobs`, 205 'After a refresh with a localStorage-found binding, a default key binding doesnt do anything' 206 ); 207 208 triggerEvent('.page-layout', 'keydown', { key: 'b' }); 209 triggerEvent('.page-layout', 'keydown', { key: 'o' }); 210 triggerEvent('.page-layout', 'keydown', { key: 'o' }); 211 await triggerEvent('.page-layout', 'keydown', { key: 'p' }); 212 assert.equal( 213 currentURL(), 214 `/clients`, 215 'end up on the clients page after typing the localstorage-bound shortcut' 216 ); 217 218 assert.notOk(Layout.keyboard.modalShown); 219 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 220 assert.ok(Layout.keyboard.modalShown); 221 assert 222 .dom( 223 '[data-test-command-label="Go to Clients"] button[data-test-rebinder]' 224 ) 225 .hasText('b o o p'); 226 }); 227 }); 228 229 module('Hints', function () { 230 test('Hints show up on shift', async function (assert) { 231 await visit('/'); 232 233 await triggerEvent('.page-layout', 'keydown', { key: 'Shift' }); 234 assert.equal( 235 document.querySelectorAll('[data-test-keyboard-hint]').length, 236 7, 237 'Shows 7 hints by default' 238 ); 239 await triggerEvent('.page-layout', 'keyup', { key: 'Shift' }); 240 241 assert.equal( 242 document.querySelectorAll('[data-test-keyboard-hint]').length, 243 0, 244 'Hints disappear when you release Shift' 245 ); 246 }); 247 }); 248 249 module('Dynamic Nav', function (dynamicHooks) { 250 dynamicHooks.beforeEach(async function () { 251 server.create('node-pool'); 252 server.create('node'); 253 }); 254 test('Dynamic Table Nav', async function (assert) { 255 assert.expect(4); 256 server.createList('job', 3, { createRecommendations: true }); 257 await visit('/jobs'); 258 259 await triggerEvent('.page-layout', 'keydown', { key: 'Shift' }); 260 assert.equal( 261 document.querySelectorAll('[data-shortcut="Shift+01"]').length, 262 1, 263 'First job gets a shortcut hint' 264 ); 265 assert.equal( 266 document.querySelectorAll('[data-shortcut="Shift+02"]').length, 267 1, 268 'Second job gets a shortcut hint' 269 ); 270 assert.equal( 271 document.querySelectorAll('[data-shortcut="Shift+03"]').length, 272 1, 273 'Third job gets a shortcut hint' 274 ); 275 276 triggerEvent('.page-layout', 'keydown', { key: 'Shift' }); 277 triggerEvent('.page-layout', 'keydown', { key: '0' }); 278 await triggerEvent('.page-layout', 'keydown', { key: '1' }); 279 280 const clickedJob = server.db.jobs.sortBy('modifyIndex').reverse()[0].id; 281 assert.equal( 282 currentURL(), 283 `/jobs/${clickedJob}@default`, 284 'Shift+01 takes you to the first job' 285 ); 286 }); 287 test('Multi-Table Nav', async function (assert) { 288 server.createList('job', 3, { createRecommendations: true }); 289 await visit( 290 `/jobs/${server.db.jobs.sortBy('modifyIndex').reverse()[0].id}@default` 291 ); 292 const numberOfGroups = findAll('.task-group-row').length; 293 const numberOfAllocs = findAll('.allocation-row').length; 294 295 await triggerEvent('.page-layout', 'keydown', { key: 'Shift' }); 296 [...Array(numberOfGroups + numberOfAllocs)].forEach((_, iter) => { 297 assert.equal( 298 document.querySelectorAll(`[data-shortcut="Shift+0${iter + 1}"]`) 299 .length, 300 1, 301 `Dynamic item #${iter + 1} gets a shortcut hint` 302 ); 303 }); 304 await triggerEvent('.page-layout', 'keyup', { key: 'Shift' }); 305 }); 306 307 test('Dynamic nav arrows and looping', async function (assert) { 308 server.createList('job', 3, { createAllocations: true, type: 'system' }); 309 const jobID = server.db.jobs.sortBy('modifyIndex').reverse()[0].id; 310 await visit(`/jobs/${jobID}@default`); 311 312 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 313 shiftKey: true, 314 }); 315 assert.ok( 316 currentURL().startsWith(`/jobs/${jobID}@default/definition`), 317 'Shift+ArrowRight takes you to the next tab (Definition)' 318 ); 319 320 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 321 shiftKey: true, 322 }); 323 assert.equal( 324 currentURL(), 325 `/jobs/${jobID}@default/versions`, 326 'Shift+ArrowRight takes you to the next tab (Version)' 327 ); 328 329 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 330 shiftKey: true, 331 }); 332 assert.equal( 333 currentURL(), 334 `/jobs/${jobID}@default/allocations`, 335 'Shift+ArrowRight takes you to the next tab (Allocations)' 336 ); 337 338 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 339 shiftKey: true, 340 }); 341 assert.equal( 342 currentURL(), 343 `/jobs/${jobID}@default/evaluations`, 344 'Shift+ArrowRight takes you to the next tab (Evaluations)' 345 ); 346 347 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 348 shiftKey: true, 349 }); 350 assert.equal( 351 currentURL(), 352 `/jobs/${jobID}@default/services`, 353 'Shift+ArrowRight takes you to the next tab (Services)' 354 ); 355 356 await triggerKeyEvent('.page-layout', 'keydown', 'ArrowRight', { 357 shiftKey: true, 358 }); 359 assert.equal( 360 currentURL(), 361 `/jobs/${jobID}@default`, 362 'Shift+ArrowRight takes you to the first tab in the loop' 363 ); 364 }); 365 366 test('Region switching', async function (assert) { 367 ['Detroit', 'Halifax', 'Phoenix', 'Toronto', 'Windsor'].forEach((id) => { 368 server.create('region', { id }); 369 }); 370 371 await visit('/jobs'); 372 373 // Regions are in the keynav modal 374 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 375 await triggerEvent('.page-layout', 'keydown', { key: '?' }); 376 assert.ok(Layout.keyboard.modalShown); 377 assert 378 .dom('[data-test-command-label="Switch to Detroit region"]') 379 .exists('First created region is in the modal'); 380 381 assert 382 .dom('[data-test-command-label="Switch to Windsor region"]') 383 .exists('Last created region is in the modal'); 384 385 // Triggers a region switch to Halifax 386 triggerEvent('.page-layout', 'keydown', { key: 'r' }); 387 await triggerEvent('.page-layout', 'keydown', { key: '2' }); 388 assert.ok( 389 currentURL().includes('region=Halifax'), 390 'r 2 command takes you to the second region' 391 ); 392 393 // Triggers a region switch to Phoenix 394 triggerEvent('.page-layout', 'keydown', { key: 'r' }); 395 await triggerEvent('.page-layout', 'keydown', { key: '3' }); 396 assert.ok( 397 currentURL().includes('region=Phoenix'), 398 'r 3 command takes you to the third region' 399 ); 400 }); 401 }); 402 });