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