github.com/hernad/nomad@v1.6.112/ui/tests/integration/components/fs/file-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import { module, test } from 'qunit'; 7 import { setupRenderingTest } from 'ember-qunit'; 8 import { find, click, render, settled } from '@ember/test-helpers'; 9 import hbs from 'htmlbars-inline-precompile'; 10 import Pretender from 'pretender'; 11 import { logEncode } from '../../../../mirage/data/logs'; 12 import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; 13 14 const { assign } = Object; 15 const HOST = '1.1.1.1:1111'; 16 17 module('Integration | Component | fs/file', function (hooks) { 18 setupRenderingTest(hooks); 19 20 hooks.beforeEach(function () { 21 this.server = new Pretender(function () { 22 this.get('/v1/agent/members', () => [ 23 200, 24 {}, 25 JSON.stringify({ ServerRegion: 'default', Members: [] }), 26 ]); 27 this.get('/v1/regions', () => [ 28 200, 29 {}, 30 JSON.stringify(['default', 'region-2']), 31 ]); 32 this.get('/v1/client/fs/stream/:alloc_id', () => [ 33 200, 34 {}, 35 logEncode(['Hello World'], 0), 36 ]); 37 this.get('/v1/client/fs/cat/:alloc_id', () => [200, {}, 'Hello World']); 38 this.get('/v1/client/fs/readat/:alloc_id', () => [ 39 200, 40 {}, 41 'Hello World', 42 ]); 43 }); 44 this.system = this.owner.lookup('service:system'); 45 }); 46 47 hooks.afterEach(function () { 48 this.server.shutdown(); 49 window.localStorage.clear(); 50 }); 51 52 const commonTemplate = hbs` 53 <Fs::File @allocation={{this.allocation}} @taskState={{this.taskState}} @file={{this.file}} @stat={{this.stat}} /> 54 `; 55 56 const fileStat = (type, size = 0) => ({ 57 stat: { 58 Size: size, 59 ContentType: type, 60 }, 61 }); 62 const makeProps = (props = {}) => 63 assign( 64 {}, 65 { 66 allocation: { 67 id: 'alloc-1', 68 node: { 69 httpAddr: HOST, 70 }, 71 }, 72 taskState: { 73 name: 'task-name', 74 }, 75 file: 'path/to/file', 76 stat: { 77 Size: 12345, 78 ContentType: 'text/plain', 79 }, 80 }, 81 props 82 ); 83 84 test('When a file is text-based, the file mode is streaming', async function (assert) { 85 assert.expect(3); 86 87 const props = makeProps(fileStat('text/plain', 500)); 88 this.setProperties(props); 89 90 await render(commonTemplate); 91 92 assert.ok( 93 find('[data-test-file-box] [data-test-log-cli]'), 94 'The streaming file component was rendered' 95 ); 96 assert.notOk( 97 find('[data-test-file-box] [data-test-image-file]'), 98 'The image file component was not rendered' 99 ); 100 101 await componentA11yAudit(this.element, assert); 102 }); 103 104 test('When a file is an image, the file mode is image', async function (assert) { 105 assert.expect(3); 106 107 const props = makeProps(fileStat('image/png', 1234)); 108 this.setProperties(props); 109 110 await render(commonTemplate); 111 112 assert.ok( 113 find('[data-test-file-box] [data-test-image-file]'), 114 'The image file component was rendered' 115 ); 116 assert.notOk( 117 find('[data-test-file-box] [data-test-log-cli]'), 118 'The streaming file component was not rendered' 119 ); 120 121 await componentA11yAudit(this.element, assert); 122 }); 123 124 test('When the file is neither text-based or an image, the unsupported file type empty state is shown', async function (assert) { 125 assert.expect(4); 126 127 const props = makeProps(fileStat('wat/ohno', 1234)); 128 this.setProperties(props); 129 130 await render(commonTemplate); 131 132 assert.notOk( 133 find('[data-test-file-box] [data-test-image-file]'), 134 'The image file component was not rendered' 135 ); 136 assert.notOk( 137 find('[data-test-file-box] [data-test-log-cli]'), 138 'The streaming file component was not rendered' 139 ); 140 assert.ok( 141 find('[data-test-unsupported-type]'), 142 'Unsupported file type message is shown' 143 ); 144 await componentA11yAudit(this.element, assert); 145 }); 146 147 test('The unsupported file type empty state includes a link to the raw file', async function (assert) { 148 const props = makeProps(fileStat('wat/ohno', 1234)); 149 this.setProperties(props); 150 151 await render(commonTemplate); 152 153 assert.ok( 154 find('[data-test-unsupported-type] [data-test-log-action="raw"]'), 155 'Unsupported file type message includes a link to the raw file' 156 ); 157 158 assert.notOk( 159 find('[data-test-header] [data-test-log-action="raw"]'), 160 'Raw link is no longer in the header' 161 ); 162 }); 163 164 test('The view raw button goes to the correct API url', async function (assert) { 165 const props = makeProps(fileStat('image/png', 1234)); 166 this.setProperties(props); 167 168 await render(commonTemplate); 169 click('[data-test-log-action="raw"]'); 170 await settled(); 171 assert.ok( 172 this.server.handledRequests.find( 173 ({ url: url }) => 174 url === 175 `/v1/client/fs/cat/${props.allocation.id}?path=${encodeURIComponent( 176 `${props.taskState.name}/${props.file}` 177 )}` 178 ), 179 'Request to file is made' 180 ); 181 }); 182 183 test('The view raw button respects the active region', async function (assert) { 184 const region = 'region-2'; 185 window.localStorage.nomadActiveRegion = region; 186 187 const props = makeProps(fileStat('image/png', 1234)); 188 this.setProperties(props); 189 190 await this.system.get('regions'); 191 await render(commonTemplate); 192 193 click('[data-test-log-action="raw"]'); 194 await settled(); 195 assert.ok( 196 this.server.handledRequests.find( 197 ({ url: url }) => 198 url === 199 `/v1/client/fs/cat/${props.allocation.id}?path=${encodeURIComponent( 200 `${props.taskState.name}/${props.file}` 201 )}®ion=${region}` 202 ), 203 'Request to file is made with region' 204 ); 205 }); 206 207 test('The head and tail buttons are not shown when the file is small', async function (assert) { 208 const props = makeProps(fileStat('application/json', 5000)); 209 this.setProperties(props); 210 211 await render(commonTemplate); 212 213 assert.notOk(find('[data-test-log-action="head"]'), 'No head button'); 214 assert.notOk(find('[data-test-log-action="tail"]'), 'No tail button'); 215 216 this.set('stat.Size', 100000); 217 218 await settled(); 219 220 assert.ok(find('[data-test-log-action="head"]'), 'Head button is shown'); 221 assert.ok(find('[data-test-log-action="tail"]'), 'Tail button is shown'); 222 }); 223 224 test('The head and tail buttons are not shown for an image file', async function (assert) { 225 const props = makeProps(fileStat('image/svg', 5000)); 226 this.setProperties(props); 227 228 await render(commonTemplate); 229 230 assert.notOk(find('[data-test-log-action="head"]'), 'No head button'); 231 assert.notOk(find('[data-test-log-action="tail"]'), 'No tail button'); 232 233 this.set('stat.Size', 100000); 234 235 await settled(); 236 237 assert.notOk(find('[data-test-log-action="head"]'), 'Still no head button'); 238 assert.notOk(find('[data-test-log-action="tail"]'), 'Still no tail button'); 239 }); 240 241 test('Yielded content goes in the top-left header area', async function (assert) { 242 assert.expect(2); 243 244 const props = makeProps(fileStat('image/svg', 5000)); 245 this.setProperties(props); 246 247 await render(hbs` 248 <Fs::File @allocation={{this.allocation}} @taskState={{this.taskState}} @file={{this.file}} @stat={{this.stat}}> 249 <div data-test-yield-spy>Yielded content</div> 250 </Fs::File> 251 `); 252 253 assert.ok( 254 find('[data-test-header] [data-test-yield-spy]'), 255 'Yielded content shows up in the header' 256 ); 257 258 await componentA11yAudit(this.element, assert); 259 }); 260 261 test('The body is full-bleed and dark when the file is streaming', async function (assert) { 262 const props = makeProps(fileStat('application/json', 5000)); 263 this.setProperties(props); 264 265 await render(commonTemplate); 266 267 const classes = Array.from(find('[data-test-file-box]').classList); 268 assert.ok(classes.includes('is-dark'), 'Body is dark'); 269 assert.ok(classes.includes('is-full-bleed'), 'Body is full-bleed'); 270 }); 271 272 test('The body has padding and a light background when the file is not streaming', async function (assert) { 273 const props = makeProps(fileStat('image/jpeg', 5000)); 274 this.setProperties(props); 275 276 await render(commonTemplate); 277 278 let classes = Array.from(find('[data-test-file-box]').classList); 279 assert.notOk(classes.includes('is-dark'), 'Body is not dark'); 280 assert.notOk(classes.includes('is-full-bleed'), 'Body is not full-bleed'); 281 282 this.set('stat.ContentType', 'something/unknown'); 283 284 await settled(); 285 286 classes = Array.from(find('[data-test-file-box]').classList); 287 assert.notOk(classes.includes('is-dark'), 'Body is still not dark'); 288 assert.notOk( 289 classes.includes('is-full-bleed'), 290 'Body is still not full-bleed' 291 ); 292 }); 293 });