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