github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/tests/acceptance/variables-test.js (about) 1 import { 2 currentRouteName, 3 currentURL, 4 click, 5 find, 6 findAll, 7 typeIn, 8 visit, 9 } from '@ember/test-helpers'; 10 import { setupMirage } from 'ember-cli-mirage/test-support'; 11 import { 12 selectChoose, 13 clickTrigger, 14 } from 'ember-power-select/test-support/helpers'; 15 import { setupApplicationTest } from 'ember-qunit'; 16 import { module, test } from 'qunit'; 17 import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit'; 18 import { allScenarios } from '../../mirage/scenarios/default'; 19 import cleanWhitespace from '../utils/clean-whitespace'; 20 import percySnapshot from '@percy/ember'; 21 import faker from 'nomad-ui/mirage/faker'; 22 23 import Variables from 'nomad-ui/tests/pages/variables'; 24 import Layout from 'nomad-ui/tests/pages/layout'; 25 26 const VARIABLE_TOKEN_ID = '53cur3-v4r14bl35'; 27 const LIMITED_VARIABLE_TOKEN_ID = 'f3w3r-53cur3-v4r14bl35'; 28 29 module('Acceptance | variables', function (hooks) { 30 setupApplicationTest(hooks); 31 setupMirage(hooks); 32 hooks.beforeEach(async function () { 33 faker.seed(1); 34 server.createList('variable', 3); 35 }); 36 37 test('it redirects to jobs and hides the gutter link when the token lacks permissions', async function (assert) { 38 await Variables.visit(); 39 assert.equal(currentURL(), '/jobs'); 40 assert.ok(Layout.gutter.variables.isHidden); 41 }); 42 43 test('it allows access for management level tokens', async function (assert) { 44 allScenarios.variableTestCluster(server); 45 window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId; 46 await Variables.visit(); 47 assert.equal(currentURL(), '/variables'); 48 assert.ok(Layout.gutter.variables.isVisible, 'Menu section is visible'); 49 }); 50 51 test('it allows access for list-variables allowed ACL rules', async function (assert) { 52 assert.expect(2); 53 allScenarios.variableTestCluster(server); 54 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 55 window.localStorage.nomadTokenSecret = variablesToken.secretId; 56 57 await Variables.visit(); 58 assert.equal(currentURL(), '/variables'); 59 assert.ok(Layout.gutter.variables.isVisible); 60 await percySnapshot(assert); 61 }); 62 63 test('it correctly traverses to and deletes a variable', async function (assert) { 64 assert.expect(13); 65 allScenarios.variableTestCluster(server); 66 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 67 window.localStorage.nomadTokenSecret = variablesToken.secretId; 68 server.db.variables.update({ namespace: 'default' }); 69 const policy = server.db.policies.find('Variable Maker'); 70 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 71 (path) => path.PathSpec === '*' 72 ).Capabilities = ['list', 'read', 'destroy']; 73 74 await Variables.visit(); 75 assert.equal(currentURL(), '/variables'); 76 assert.ok(Layout.gutter.variables.isVisible); 77 78 let abcLink = [...findAll('[data-test-folder-row]')].filter((a) => 79 a.textContent.includes('a/b/c') 80 )[0]; 81 82 await click(abcLink); 83 84 assert.equal( 85 currentURL(), 86 '/variables/path/a/b/c', 87 'correctly traverses to a deeply nested path' 88 ); 89 assert.equal( 90 findAll('[data-test-folder-row]').length, 91 2, 92 'correctly shows 2 sub-folders' 93 ); 94 assert.equal( 95 findAll('[data-test-file-row]').length, 96 2, 97 'correctly shows 2 files' 98 ); 99 let fooLink = [...findAll('[data-test-file-row]')].filter((a) => 100 a.textContent.includes('foo0') 101 )[0]; 102 103 assert.ok(fooLink, 'foo0 file is present'); 104 105 await percySnapshot(assert); 106 107 await click(fooLink); 108 assert.ok( 109 currentURL().includes('/variables/var/a/b/c/foo0'), 110 'correctly traverses to a deeply nested variable file' 111 ); 112 const deleteButton = find('[data-test-delete-button] button'); 113 assert.dom(deleteButton).exists('delete button is present'); 114 115 await percySnapshot('deeply nested variable'); 116 117 await click(deleteButton); 118 assert 119 .dom('[data-test-confirmation-message]') 120 .exists('confirmation message is present'); 121 122 await click(find('[data-test-confirm-button]')); 123 assert.equal( 124 currentURL(), 125 '/variables/path/a/b/c', 126 'correctly returns to the parent path page after deletion' 127 ); 128 129 assert.equal( 130 findAll('[data-test-folder-row]').length, 131 2, 132 'still correctly shows 2 sub-folders' 133 ); 134 assert.equal( 135 findAll('[data-test-file-row]').length, 136 1, 137 'now correctly shows 1 file' 138 ); 139 140 fooLink = [...findAll('[data-test-file-row]')].filter((a) => 141 a.textContent.includes('foo0') 142 )[0]; 143 144 assert.notOk(fooLink, 'foo0 file is no longer present'); 145 }); 146 147 test('variables prefixed with nomad/jobs/ correctly link to entities', async function (assert) { 148 assert.expect(29); 149 allScenarios.variableTestCluster(server); 150 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 151 152 const variableLinkedJob = server.db.jobs[0]; 153 const variableLinkedGroup = server.db.taskGroups.findBy({ 154 jobId: variableLinkedJob.id, 155 }); 156 const variableLinkedTask = server.db.tasks.findBy({ 157 taskGroupId: variableLinkedGroup.id, 158 }); 159 const variableLinkedTaskAlloc = server.db.allocations 160 .filterBy('taskGroup', variableLinkedGroup.name) 161 ?.find((alloc) => alloc.taskStateIds.length); 162 163 window.localStorage.nomadTokenSecret = variablesToken.secretId; 164 165 // Non-job variable 166 await Variables.visit(); 167 assert.equal(currentURL(), '/variables'); 168 assert.ok(Layout.gutter.variables.isVisible); 169 170 let nonJobLink = [...findAll('[data-test-file-row]')].filter((a) => 171 a.textContent.includes('just some arbitrary file') 172 )[0]; 173 174 assert.ok(nonJobLink, 'non-job file is present'); 175 176 await click(nonJobLink); 177 assert.ok( 178 currentURL().includes('/variables/var/just some arbitrary file'), 179 'correctly traverses to a non-job file' 180 ); 181 let relatedEntitiesBox = find('.related-entities'); 182 assert 183 .dom(relatedEntitiesBox) 184 .doesNotExist('Related Entities box is not present'); 185 186 // Job variable 187 await Variables.visit(); 188 let jobsDirectoryLink = [...findAll('[data-test-folder-row]')].filter((a) => 189 a.textContent.includes('jobs') 190 )[0]; 191 192 await click(jobsDirectoryLink); 193 194 assert.equal( 195 currentURL(), 196 '/variables/path/nomad/jobs', 197 'correctly traverses to the jobs directory' 198 ); 199 let jobFileLink = find('[data-test-file-row]'); 200 201 assert.ok(jobFileLink, 'A job file is present'); 202 203 await click(jobFileLink); 204 assert.ok( 205 currentURL().startsWith('/variables/var/nomad/jobs/'), 206 'correctly traverses to a job file' 207 ); 208 relatedEntitiesBox = find('.related-entities'); 209 assert.dom(relatedEntitiesBox).exists('Related Entities box is present'); 210 assert.ok( 211 cleanWhitespace(relatedEntitiesBox.textContent).includes( 212 'This variable is accessible by job' 213 ), 214 'Related Entities box is job-oriented' 215 ); 216 217 await percySnapshot('related entities box for job variable'); 218 219 let relatedJobLink = find('.related-entities a'); 220 await click(relatedJobLink); 221 assert 222 .dom('[data-test-job-stat="variables"]') 223 .exists('Link from Job to Variable exists'); 224 let jobVariableLink = find('[data-test-job-stat="variables"] a'); 225 await click(jobVariableLink); 226 assert.ok( 227 currentURL().startsWith( 228 `/variables/var/nomad/jobs/${variableLinkedJob.id}` 229 ), 230 'correctly traverses from job to variable' 231 ); 232 233 // Group Variable 234 await Variables.visit(); 235 jobsDirectoryLink = [...findAll('[data-test-folder-row]')].filter((a) => 236 a.textContent.includes('jobs') 237 )[0]; 238 await click(jobsDirectoryLink); 239 let groupDirectoryLink = [...findAll('[data-test-folder-row]')][0]; 240 await click(groupDirectoryLink); 241 let groupFileLink = find('[data-test-file-row]'); 242 assert.ok(groupFileLink, 'A group file is present'); 243 await click(groupFileLink); 244 relatedEntitiesBox = find('.related-entities'); 245 assert.dom(relatedEntitiesBox).exists('Related Entities box is present'); 246 assert.ok( 247 cleanWhitespace(relatedEntitiesBox.textContent).includes( 248 'This variable is accessible by group' 249 ), 250 'Related Entities box is group-oriented' 251 ); 252 253 await percySnapshot('related entities box for group variable'); 254 255 let relatedGroupLink = find('.related-entities a'); 256 await click(relatedGroupLink); 257 assert 258 .dom('[data-test-task-group-stat="variables"]') 259 .exists('Link from Group to Variable exists'); 260 let groupVariableLink = find('[data-test-task-group-stat="variables"] a'); 261 await click(groupVariableLink); 262 assert.ok( 263 currentURL().startsWith( 264 `/variables/var/nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}` 265 ), 266 'correctly traverses from group to variable' 267 ); 268 269 // Task Variable 270 await Variables.visit(); 271 jobsDirectoryLink = [...findAll('[data-test-folder-row]')].filter((a) => 272 a.textContent.includes('jobs') 273 )[0]; 274 await click(jobsDirectoryLink); 275 groupDirectoryLink = [...findAll('[data-test-folder-row]')][0]; 276 await click(groupDirectoryLink); 277 let taskDirectoryLink = [...findAll('[data-test-folder-row]')][0]; 278 await click(taskDirectoryLink); 279 let taskFileLink = find('[data-test-file-row]'); 280 assert.ok(taskFileLink, 'A task file is present'); 281 await click(taskFileLink); 282 relatedEntitiesBox = find('.related-entities'); 283 assert.dom(relatedEntitiesBox).exists('Related Entities box is present'); 284 assert.ok( 285 cleanWhitespace(relatedEntitiesBox.textContent).includes( 286 'This variable is accessible by task' 287 ), 288 'Related Entities box is task-oriented' 289 ); 290 291 await percySnapshot('related entities box for task variable'); 292 293 let relatedTaskLink = find('.related-entities a'); 294 await click(relatedTaskLink); 295 // Gotta go the long way and click into the alloc/then task from here; but we know this one by virtue of stable test env. 296 await visit( 297 `/allocations/${variableLinkedTaskAlloc.id}/${variableLinkedTask.name}` 298 ); 299 assert 300 .dom('[data-test-task-stat="variables"]') 301 .exists('Link from Task to Variable exists'); 302 let taskVariableLink = find('[data-test-task-stat="variables"] a'); 303 await click(taskVariableLink); 304 assert.ok( 305 currentURL().startsWith( 306 `/variables/var/nomad/jobs/${variableLinkedJob.id}/${variableLinkedGroup.name}/${variableLinkedTask.name}` 307 ), 308 'correctly traverses from task to variable' 309 ); 310 311 // A non-variable-having job 312 await visit(`/jobs/${server.db.jobs[1].id}`); 313 assert 314 .dom('[data-test-task-stat="variables"]') 315 .doesNotExist('Link from Variable-less Job to Variable does not exist'); 316 317 // Related Entities during the Variable creation process 318 await Variables.visitNew(); 319 assert 320 .dom('.related-entities.notification') 321 .doesNotExist('Related Entities notification is not present by default'); 322 await typeIn('[data-test-path-input]', 'foo/bar'); 323 assert 324 .dom('.related-entities.notification') 325 .doesNotExist( 326 'Related Entities notification is not present when path is generic' 327 ); 328 document.querySelector('[data-test-path-input]').value = ''; // clear path input 329 await typeIn('[data-test-path-input]', 'nomad/jobs/abc'); 330 assert 331 .dom('.related-entities.notification') 332 .exists( 333 'Related Entities notification is present when path is job-oriented' 334 ); 335 assert 336 .dom('.related-entities.notification') 337 .containsText( 338 'This variable will be accessible by job', 339 'Related Entities notification is job-oriented' 340 ); 341 await typeIn('[data-test-path-input]', '/def'); 342 assert 343 .dom('.related-entities.notification') 344 .containsText( 345 'This variable will be accessible by group', 346 'Related Entities notification is group-oriented' 347 ); 348 await typeIn('[data-test-path-input]', '/ghi'); 349 assert 350 .dom('.related-entities.notification') 351 .containsText( 352 'This variable will be accessible by task', 353 'Related Entities notification is task-oriented' 354 ); 355 }); 356 357 test('it does not allow you to save if you lack Items', async function (assert) { 358 assert.expect(5); 359 allScenarios.variableTestCluster(server); 360 window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId; 361 await Variables.visitNew(); 362 assert.equal(currentURL(), '/variables/new'); 363 await typeIn('.path-input', 'foo/bar'); 364 await click('button[type="submit"]'); 365 assert.dom('.flash-message.alert-error').exists(); 366 await click('.flash-message.alert-error .close-button'); 367 assert.dom('.flash-message.alert-error').doesNotExist(); 368 369 await typeIn('.key-value label:nth-child(1) input', 'myKey'); 370 await typeIn('.key-value label:nth-child(2) input', 'superSecret'); 371 372 await percySnapshot(assert); 373 374 await click('button[type="submit"]'); 375 376 assert.dom('.flash-message.alert-success').exists(); 377 assert.ok( 378 currentURL().includes('/variables/var/foo'), 379 'drops you back off to the parent page' 380 ); 381 }); 382 383 test('it passes an accessibility audit', async function (assert) { 384 assert.expect(1); 385 allScenarios.variableTestCluster(server); 386 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 387 window.localStorage.nomadTokenSecret = variablesToken.secretId; 388 await Variables.visit(); 389 await a11yAudit(assert); 390 }); 391 392 module('create flow', function () { 393 test('allows a user with correct permissions to create a variable', async function (assert) { 394 // Arrange Test Set-up 395 allScenarios.variableTestCluster(server); 396 server.createList('variable', 3); 397 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 398 window.localStorage.nomadTokenSecret = variablesToken.secretId; 399 await Variables.visit(); 400 // End Test Set-up 401 402 assert 403 .dom('[data-test-create-var]') 404 .exists('It should display an enabled button to create a variable'); 405 await click('[data-test-create-var]'); 406 407 assert.equal(currentRouteName(), 'variables.new'); 408 409 await typeIn('[data-test-path-input]', 'foo/bar'); 410 await clickTrigger('[data-test-variable-namespace-filter]'); 411 412 assert.dom('.dropdown-options').exists('Namespace can be edited.'); 413 assert 414 .dom('[data-test-variable-namespace-filter]') 415 .containsText( 416 'default', 417 'The first alphabetically sorted namespace should be selected as the default option.' 418 ); 419 420 await selectChoose( 421 '[data-test-variable-namespace-filter]', 422 'namespace-1' 423 ); 424 await typeIn('[data-test-var-key]', 'kiki'); 425 await typeIn('[data-test-var-value]', 'do you love me'); 426 await click('[data-test-submit-var]'); 427 428 assert.equal( 429 currentRouteName(), 430 'variables.variable.index', 431 'Navigates user back to variables list page after creating variable.' 432 ); 433 assert 434 .dom('.flash-message.alert.alert-success') 435 .exists('Shows a success toast notification on creation.'); 436 assert 437 .dom('[data-test-var=kiki]') 438 .exists('The new variable key should appear in the list.'); 439 440 // Reset Token 441 window.localStorage.nomadTokenSecret = null; 442 }); 443 444 test('prevents users from creating a variable without proper permissions', async function (assert) { 445 // Arrange Test Set-up 446 allScenarios.variableTestCluster(server); 447 server.createList('variable', 3); 448 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 449 window.localStorage.nomadTokenSecret = variablesToken.secretId; 450 const policy = server.db.policies.find('Variable Maker'); 451 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 452 (path) => path.PathSpec === '*' 453 ).Capabilities = ['list']; 454 await Variables.visit(); 455 // End Test Set-up 456 457 assert 458 .dom('[data-test-disabled-create-var]') 459 .exists( 460 'It should display an disabled button to create a variable on the main listings page' 461 ); 462 463 // Reset Token 464 window.localStorage.nomadTokenSecret = null; 465 }); 466 467 test('allows creating a variable that starts with nomad/jobs/', async function (assert) { 468 // Arrange Test Set-up 469 allScenarios.variableTestCluster(server); 470 server.createList('variable', 3); 471 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 472 window.localStorage.nomadTokenSecret = variablesToken.secretId; 473 await Variables.visitNew(); 474 // End Test Set-up 475 476 await typeIn('[data-test-path-input]', 'nomad/jobs/foo/bar'); 477 await typeIn('[data-test-var-key]', 'my-test-key'); 478 await typeIn('[data-test-var-value]', 'my_test_value'); 479 await click('[data-test-submit-var]'); 480 481 assert.equal( 482 currentRouteName(), 483 'variables.variable.index', 484 'Navigates user back to variables list page after creating variable.' 485 ); 486 assert 487 .dom('.flash-message.alert.alert-success') 488 .exists('Shows a success toast notification on creation.'); 489 490 // Reset Token 491 window.localStorage.nomadTokenSecret = null; 492 }); 493 494 test('disallows creating a variable that starts with nomad/<something-other-than-jobs>/', async function (assert) { 495 // Arrange Test Set-up 496 allScenarios.variableTestCluster(server); 497 server.createList('variable', 3); 498 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 499 window.localStorage.nomadTokenSecret = variablesToken.secretId; 500 await Variables.visitNew(); 501 // End Test Set-up 502 503 await typeIn('[data-test-path-input]', 'nomad/foo/'); 504 await typeIn('[data-test-var-key]', 'my-test-key'); 505 await typeIn('[data-test-var-value]', 'my_test_value'); 506 assert 507 .dom('[data-test-submit-var]') 508 .isDisabled( 509 'Cannot submit a variable that begins with nomad/<not-jobs>/' 510 ); 511 512 // Reset Token 513 window.localStorage.nomadTokenSecret = null; 514 }); 515 }); 516 517 module('edit flow', function () { 518 test('allows a user with correct permissions to edit a variable', async function (assert) { 519 assert.expect(8); 520 // Arrange Test Set-up 521 allScenarios.variableTestCluster(server); 522 server.createList('variable', 3); 523 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 524 window.localStorage.nomadTokenSecret = variablesToken.secretId; 525 const policy = server.db.policies.find('Variable Maker'); 526 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 527 (path) => path.PathSpec === '*' 528 ).Capabilities = ['list', 'read', 'write']; 529 server.db.variables.update({ namespace: 'default' }); 530 await Variables.visit(); 531 await click('[data-test-file-row]'); 532 // End Test Set-up 533 534 assert.equal(currentRouteName(), 'variables.variable.index'); 535 assert 536 .dom('[data-test-edit-button]') 537 .exists('The edit button is enabled in the view.'); 538 await click('[data-test-edit-button]'); 539 assert.equal( 540 currentRouteName(), 541 'variables.variable.edit', 542 'Clicking the button navigates you to editing view.' 543 ); 544 545 await percySnapshot(assert); 546 547 assert.dom('[data-test-path-input]').isDisabled('Path cannot be edited'); 548 await clickTrigger('[data-test-variable-namespace-filter]'); 549 assert 550 .dom('.dropdown-options') 551 .doesNotExist('Namespace cannot be edited.'); 552 553 document.querySelector('[data-test-var-key]').value = ''; // clear current input 554 await typeIn('[data-test-var-key]', 'kiki'); 555 await typeIn('[data-test-var-value]', 'do you love me'); 556 await click('[data-test-submit-var]'); 557 assert.equal( 558 currentRouteName(), 559 'variables.variable.index', 560 'Navigates user back to variables list page after creating variable.' 561 ); 562 assert 563 .dom('.flash-message.alert.alert-success') 564 .exists('Shows a success toast notification on edit.'); 565 assert 566 .dom('[data-test-var=kiki]') 567 .exists('The edited variable key should appear in the list.'); 568 569 // Reset Token 570 window.localStorage.nomadTokenSecret = null; 571 }); 572 573 test('prevents users from editing a variable without proper permissions', async function (assert) { 574 // Arrange Test Set-up 575 allScenarios.variableTestCluster(server); 576 server.createList('variable', 3); 577 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 578 window.localStorage.nomadTokenSecret = variablesToken.secretId; 579 const policy = server.db.policies.find('Variable Maker'); 580 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 581 (path) => path.PathSpec === '*' 582 ).Capabilities = ['list', 'read']; 583 await Variables.visit(); 584 await click('[data-test-file-row]'); 585 // End Test Set-up 586 587 assert.equal(currentRouteName(), 'variables.variable.index'); 588 assert 589 .dom('[data-test-edit-button]') 590 .doesNotExist('The edit button is hidden in the view.'); 591 592 // Reset Token 593 window.localStorage.nomadTokenSecret = null; 594 }); 595 test('handles conflicts on save', async function (assert) { 596 // Arrange Test Set-up 597 allScenarios.variableTestCluster(server); 598 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 599 window.localStorage.nomadTokenSecret = variablesToken.secretId; 600 // End Test Set-up 601 602 await Variables.visitConflicting(); 603 await click('button[type="submit"]'); 604 605 assert 606 .dom('.notification.conflict') 607 .exists('Notification alerting user of conflict is present'); 608 609 document.querySelector('[data-test-var-key]').value = ''; // clear current input 610 await typeIn('[data-test-var-key]', 'buddy'); 611 await typeIn('[data-test-var-value]', 'pal'); 612 await click('[data-test-submit-var]'); 613 614 await click('button[data-test-overwrite-button]'); 615 assert.equal( 616 currentURL(), 617 '/variables/var/Auto-conflicting Variable@default', 618 'Selecting overwrite forces a save and redirects' 619 ); 620 621 assert 622 .dom('.flash-message.alert.alert-success') 623 .exists('Shows a success toast notification on edit.'); 624 625 assert 626 .dom('[data-test-var=buddy]') 627 .exists('The edited variable key should appear in the list.'); 628 629 // Reset Token 630 window.localStorage.nomadTokenSecret = null; 631 }); 632 633 test('warns you if you try to leave with an unsaved form', async function (assert) { 634 // Arrange Test Set-up 635 allScenarios.variableTestCluster(server); 636 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 637 window.localStorage.nomadTokenSecret = variablesToken.secretId; 638 639 const originalWindowConfirm = window.confirm; 640 let confirmFired = false; 641 let leave = true; 642 window.confirm = function () { 643 confirmFired = true; 644 return leave; 645 }; 646 // End Test Set-up 647 648 await Variables.visitConflicting(); 649 document.querySelector('[data-test-var-key]').value = ''; // clear current input 650 await typeIn('[data-test-var-key]', 'buddy'); 651 await typeIn('[data-test-var-value]', 'pal'); 652 await click('[data-test-gutter-link="jobs"]'); 653 assert.ok(confirmFired, 'Confirm fired when leaving with unsaved form'); 654 assert.equal( 655 currentURL(), 656 '/jobs?namespace=*', 657 'Opted to leave, ended up on desired page' 658 ); 659 660 // Reset checks 661 confirmFired = false; 662 leave = false; 663 664 await Variables.visitConflicting(); 665 document.querySelector('[data-test-var-key]').value = ''; // clear current input 666 await typeIn('[data-test-var-key]', 'buddy'); 667 await typeIn('[data-test-var-value]', 'pal'); 668 await click('[data-test-gutter-link="jobs"]'); 669 assert.ok(confirmFired, 'Confirm fired when leaving with unsaved form'); 670 assert.equal( 671 currentURL(), 672 '/variables/var/Auto-conflicting%20Variable@default/edit', 673 'Opted to stay, did not leave page' 674 ); 675 676 // Reset checks 677 confirmFired = false; 678 679 await Variables.visitConflicting(); 680 document.querySelector('[data-test-var-key]').value = ''; // clear current input 681 await typeIn('[data-test-var-key]', 'buddy'); 682 await typeIn('[data-test-var-value]', 'pal'); 683 await click('[data-test-json-toggle]'); 684 assert.notOk( 685 confirmFired, 686 'Confirm did not fire when only transitioning queryParams' 687 ); 688 assert.equal( 689 currentURL(), 690 '/variables/var/Auto-conflicting%20Variable@default/edit?view=json', 691 'Stayed on page, queryParams changed' 692 ); 693 694 // Reset Token 695 window.localStorage.nomadTokenSecret = null; 696 // Restore the original window.confirm implementation 697 window.confirm = originalWindowConfirm; 698 }); 699 }); 700 701 module('delete flow', function () { 702 test('allows a user with correct permissions to delete a variable', async function (assert) { 703 // Arrange Test Set-up 704 allScenarios.variableTestCluster(server); 705 server.createList('variable', 3); 706 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 707 window.localStorage.nomadTokenSecret = variablesToken.secretId; 708 const policy = server.db.policies.find('Variable Maker'); 709 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 710 (path) => path.PathSpec === '*' 711 ).Capabilities = ['list', 'read', 'destroy']; 712 server.db.variables.update({ namespace: 'default' }); 713 await Variables.visit(); 714 await click('[data-test-file-row]'); 715 // End Test Set-up 716 assert.equal(currentRouteName(), 'variables.variable.index'); 717 assert 718 .dom('[data-test-delete-button]') 719 .exists('The delete button is enabled in the view.'); 720 await click('[data-test-idle-button]'); 721 722 assert 723 .dom('[data-test-confirmation-message]') 724 .exists('Deleting a variable requires two-step confirmation.'); 725 726 await click('[data-test-confirm-button]'); 727 728 assert.equal( 729 currentRouteName(), 730 'variables.index', 731 'Navigates user back to variables list page after destroying a variable.' 732 ); 733 734 // Reset Token 735 window.localStorage.nomadTokenSecret = null; 736 }); 737 738 test('prevents users from delete a variable without proper permissions', async function (assert) { 739 // Arrange Test Set-up 740 allScenarios.variableTestCluster(server); 741 server.createList('variable', 3); 742 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 743 window.localStorage.nomadTokenSecret = variablesToken.secretId; 744 const policy = server.db.policies.find('Variable Maker'); 745 policy.rulesJSON.Namespaces[0].Variables.Paths.find( 746 (path) => path.PathSpec === '*' 747 ).Capabilities = ['list', 'read']; 748 await Variables.visit(); 749 await click('[data-test-file-row]'); 750 // End Test Set-up 751 752 assert.equal(currentRouteName(), 'variables.variable.index'); 753 assert 754 .dom('[data-test-delete-button]') 755 .doesNotExist('The delete button is hidden in the view.'); 756 757 // Reset Token 758 window.localStorage.nomadTokenSecret = null; 759 }); 760 }); 761 762 module('read flow', function () { 763 test('allows a user with correct permissions to read a variable', async function (assert) { 764 allScenarios.variableTestCluster(server); 765 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 766 window.localStorage.nomadTokenSecret = variablesToken.secretId; 767 await Variables.visit(); 768 769 assert 770 .dom('[data-test-file-row]:not(.inaccessible)') 771 .exists( 772 { count: 4 }, 773 'Shows 4 variable files, none of which are inaccessible' 774 ); 775 776 await click('[data-test-file-row]'); 777 assert.equal(currentRouteName(), 'variables.variable.index'); 778 779 // Reset Token 780 window.localStorage.nomadTokenSecret = null; 781 }); 782 783 test('prevents users from reading a variable without proper permissions', async function (assert) { 784 allScenarios.variableTestCluster(server); 785 const variablesToken = server.db.tokens.find(LIMITED_VARIABLE_TOKEN_ID); 786 window.localStorage.nomadTokenSecret = variablesToken.secretId; 787 await Variables.visit(); 788 789 assert 790 .dom('[data-test-file-row].inaccessible') 791 .exists( 792 { count: 4 }, 793 'Shows 4 variable files, all of which are inaccessible' 794 ); 795 796 // Reset Token 797 window.localStorage.nomadTokenSecret = null; 798 }); 799 }); 800 801 module('namespace filtering', function () { 802 test('allows a user to filter variables by namespace', async function (assert) { 803 assert.expect(3); 804 805 // Arrange 806 allScenarios.variableTestCluster(server); 807 server.createList('variable', 3); 808 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 809 window.localStorage.nomadTokenSecret = variablesToken.secretId; 810 await Variables.visit(); 811 812 assert 813 .dom('[data-test-variable-namespace-filter]') 814 .exists('Shows a dropdown of namespaces'); 815 816 // Assert Side Side Effect 817 server.get('/vars', function (_server, fakeRequest) { 818 assert.deepEqual( 819 fakeRequest.queryParams, 820 { 821 namespace: 'default', 822 }, 823 'It makes another server request using the options selected by the user' 824 ); 825 return []; 826 }); 827 828 // Act 829 await clickTrigger('[data-test-variable-namespace-filter]'); 830 await selectChoose('[data-test-variable-namespace-filter]', 'default'); 831 832 assert 833 .dom('[data-test-no-matching-variables-list-headline]') 834 .exists('Renders an empty list.'); 835 }); 836 837 test('does not show namespace filtering if the user only has access to one namespace', async function (assert) { 838 allScenarios.variableTestCluster(server); 839 server.createList('variable', 3); 840 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 841 window.localStorage.nomadTokenSecret = variablesToken.secretId; 842 const twoTokens = server.db.namespaces.slice(0, 2); 843 server.db.namespaces.remove(twoTokens); 844 await Variables.visit(); 845 846 assert.equal( 847 server.db.namespaces.length, 848 1, 849 'There should only be one namespace.' 850 ); 851 assert 852 .dom('[data-test-variable-namespace-filter]') 853 .doesNotExist('Does not show a dropdown of namespaces'); 854 }); 855 856 module('path route', function () { 857 test('allows a user to filter variables by namespace', async function (assert) { 858 assert.expect(4); 859 860 // Arrange 861 allScenarios.variableTestCluster(server); 862 server.createList('variable', 3); 863 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 864 window.localStorage.nomadTokenSecret = variablesToken.secretId; 865 await Variables.visit(); 866 await click('[data-test-folder-row]'); 867 868 assert.equal( 869 currentRouteName(), 870 'variables.path', 871 'It navigates a user to the path subroute' 872 ); 873 874 assert 875 .dom('[data-test-variable-namespace-filter]') 876 .exists('Shows a dropdown of namespaces'); 877 878 // Assert Side Side Effect 879 server.get('/vars', function (_server, fakeRequest) { 880 assert.deepEqual( 881 fakeRequest.queryParams, 882 { 883 namespace: 'default', 884 }, 885 'It makes another server request using the options selected by the user' 886 ); 887 return []; 888 }); 889 890 // Act 891 await clickTrigger('[data-test-variable-namespace-filter]'); 892 await selectChoose('[data-test-variable-namespace-filter]', 'default'); 893 894 assert 895 .dom('[data-test-no-matching-variables-list-headline]') 896 .exists('Renders an empty list.'); 897 }); 898 899 test('does not show namespace filtering if the user only has access to one namespace', async function (assert) { 900 allScenarios.variableTestCluster(server); 901 server.createList('variable', 3); 902 const variablesToken = server.db.tokens.find(VARIABLE_TOKEN_ID); 903 window.localStorage.nomadTokenSecret = variablesToken.secretId; 904 const twoTokens = server.db.namespaces.slice(0, 2); 905 server.db.namespaces.remove(twoTokens); 906 await Variables.visit(); 907 908 assert.equal( 909 server.db.namespaces.length, 910 1, 911 'There should only be one namespace.' 912 ); 913 914 await click('[data-test-folder-row]'); 915 916 assert.equal( 917 currentRouteName(), 918 'variables.path', 919 'It navigates a user to the path subroute' 920 ); 921 922 assert 923 .dom('[data-test-variable-namespace-filter]') 924 .doesNotExist('Does not show a dropdown of namespaces'); 925 }); 926 }); 927 }); 928 });