github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/integration/components/task-log-test.js (about) 1 import { run } from '@ember/runloop'; 2 import { module, test } from 'qunit'; 3 import { setupRenderingTest } from 'ember-qunit'; 4 import { find, click, render, settled } from '@ember/test-helpers'; 5 import hbs from 'htmlbars-inline-precompile'; 6 import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; 7 import Pretender from 'pretender'; 8 import { logEncode } from '../../../mirage/data/logs'; 9 10 const HOST = '1.1.1.1:1111'; 11 const allowedConnectionTime = 100; 12 const commonProps = { 13 interval: 200, 14 allocation: { 15 id: 'alloc-1', 16 node: { 17 httpAddr: HOST, 18 }, 19 }, 20 taskState: 'task-name', 21 clientTimeout: allowedConnectionTime, 22 serverTimeout: allowedConnectionTime, 23 }; 24 25 const logHead = [logEncode(['HEAD'], 0)]; 26 const logTail = [logEncode(['TAIL'], 0)]; 27 const streamFrames = ['one\n', 'two\n', 'three\n', 'four\n', 'five\n']; 28 let streamPointer = 0; 29 let logMode = null; 30 31 module('Integration | Component | task log', function(hooks) { 32 setupRenderingTest(hooks); 33 34 hooks.beforeEach(function() { 35 const handler = ({ queryParams }) => { 36 let frames; 37 let data; 38 39 if (logMode === 'head') { 40 frames = logHead; 41 } else if (logMode === 'tail') { 42 frames = logTail; 43 } else { 44 frames = streamFrames; 45 } 46 47 if (frames === streamFrames) { 48 data = queryParams.plain ? frames[streamPointer] : logEncode(frames, streamPointer); 49 streamPointer++; 50 } else { 51 data = queryParams.plain ? frames.join('') : logEncode(frames, frames.length - 1); 52 } 53 54 return [200, {}, data]; 55 }; 56 57 this.server = new Pretender(function() { 58 this.get(`http://${HOST}/v1/client/fs/logs/:allocation_id`, handler); 59 this.get('/v1/client/fs/logs/:allocation_id', handler); 60 this.get('/v1/regions', () => [200, {}, '[]']); 61 }); 62 }); 63 64 hooks.afterEach(function() { 65 window.localStorage.clear(); 66 this.server.shutdown(); 67 streamPointer = 0; 68 logMode = null; 69 }); 70 71 test('Basic appearance', async function(assert) { 72 run.later(run, run.cancelTimers, commonProps.interval); 73 74 this.setProperties(commonProps); 75 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 76 77 assert.ok(find('[data-test-log-action="stdout"]'), 'Stdout button'); 78 assert.ok(find('[data-test-log-action="stderr"]'), 'Stderr button'); 79 assert.ok(find('[data-test-log-action="head"]'), 'Head button'); 80 assert.ok(find('[data-test-log-action="tail"]'), 'Tail button'); 81 assert.ok(find('[data-test-log-action="toggle-stream"]'), 'Stream toggle button'); 82 83 assert.ok(find('[data-test-log-box].is-full-bleed.is-dark'), 'Body is full-bleed and dark'); 84 85 assert.ok( 86 find('pre.cli-window'), 87 'Cli is preformatted and using the cli-window component class' 88 ); 89 90 await componentA11yAudit(this.element, assert); 91 }); 92 93 test('Streaming starts on creation', async function(assert) { 94 run.later(run, run.cancelTimers, commonProps.interval); 95 96 this.setProperties(commonProps); 97 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 98 99 const logUrlRegex = new RegExp(`${HOST}/v1/client/fs/logs/${commonProps.allocation.id}`); 100 assert.ok( 101 this.server.handledRequests.filter(req => logUrlRegex.test(req.url)).length, 102 'Log requests were made' 103 ); 104 105 await settled(); 106 assert.equal( 107 find('[data-test-log-cli]').textContent, 108 streamFrames[0], 109 'First chunk of streaming log is shown' 110 ); 111 112 await componentA11yAudit(this.element, assert); 113 }); 114 115 test('Clicking Head loads the log head', async function(assert) { 116 logMode = 'head'; 117 run.later(run, run.cancelTimers, commonProps.interval); 118 119 this.setProperties(commonProps); 120 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 121 122 click('[data-test-log-action="head"]'); 123 124 await settled(); 125 assert.ok( 126 this.server.handledRequests.find( 127 ({ queryParams: qp }) => qp.origin === 'start' && qp.offset === '0' 128 ), 129 'Log head request was made' 130 ); 131 assert.equal(find('[data-test-log-cli]').textContent, logHead[0], 'Head of the log is shown'); 132 }); 133 134 test('Clicking Tail loads the log tail', async function(assert) { 135 logMode = 'tail'; 136 run.later(run, run.cancelTimers, commonProps.interval); 137 138 this.setProperties(commonProps); 139 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 140 141 click('[data-test-log-action="tail"]'); 142 143 await settled(); 144 assert.ok( 145 this.server.handledRequests.find(({ queryParams: qp }) => qp.origin === 'end'), 146 'Log tail request was made' 147 ); 148 assert.equal(find('[data-test-log-cli]').textContent, logTail[0], 'Tail of the log is shown'); 149 }); 150 151 test('Clicking toggleStream starts and stops the log stream', async function(assert) { 152 run.later(run, run.cancelTimers, commonProps.interval); 153 154 const { interval } = commonProps; 155 this.setProperties(commonProps); 156 await render( 157 hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} @interval={{interval}} />` 158 ); 159 160 run.later(() => { 161 click('[data-test-log-action="toggle-stream"]'); 162 }, interval); 163 164 await settled(); 165 assert.equal(find('[data-test-log-cli]').textContent, streamFrames[0], 'First frame loaded'); 166 167 run.later(() => { 168 assert.equal( 169 find('[data-test-log-cli]').textContent, 170 streamFrames[0], 171 'Still only first frame' 172 ); 173 click('[data-test-log-action="toggle-stream"]'); 174 run.later(run, run.cancelTimers, interval * 2); 175 }, interval * 2); 176 177 await settled(); 178 assert.equal( 179 find('[data-test-log-cli]').textContent, 180 streamFrames[0] + streamFrames[0] + streamFrames[1], 181 'Now includes second frame' 182 ); 183 }); 184 185 test('Clicking stderr switches the log to standard error', async function(assert) { 186 run.later(run, run.cancelTimers, commonProps.interval); 187 188 this.setProperties(commonProps); 189 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 190 191 click('[data-test-log-action="stderr"]'); 192 run.later(run, run.cancelTimers, commonProps.interval); 193 194 await settled(); 195 assert.ok( 196 this.server.handledRequests.filter(req => req.queryParams.type === 'stderr').length, 197 'stderr log requests were made' 198 ); 199 }); 200 201 test('Clicking stderr/stdout mode buttons does nothing when the mode remains the same', async function(assert) { 202 const { interval } = commonProps; 203 204 run.later(() => { 205 click('[data-test-log-action="stdout"]'); 206 run.later(run, run.cancelTimers, interval * 6); 207 }, interval * 2); 208 209 this.setProperties(commonProps); 210 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 211 212 await settled(); 213 assert.equal( 214 find('[data-test-log-cli]').textContent, 215 streamFrames[0] + streamFrames[0] + streamFrames[1], 216 'Now includes second frame' 217 ); 218 }); 219 220 test('When the client is inaccessible, task-log falls back to requesting logs through the server', async function(assert) { 221 run.later(run, run.cancelTimers, allowedConnectionTime * 2); 222 223 // override client response to timeout 224 this.server.get( 225 `http://${HOST}/v1/client/fs/logs/:allocation_id`, 226 () => [400, {}, ''], 227 allowedConnectionTime * 2 228 ); 229 230 this.setProperties(commonProps); 231 await render(hbs`<TaskLog 232 @allocation={{allocation}} 233 @task={{taskState}} 234 @clientTimeout={{clientTimeout}} 235 @serverTimeout={{serverTimeout}} />`); 236 237 const clientUrlRegex = new RegExp(`${HOST}/v1/client/fs/logs/${commonProps.allocation.id}`); 238 assert.ok( 239 this.server.handledRequests.filter(req => clientUrlRegex.test(req.url)).length, 240 'Log request was initially made directly to the client' 241 ); 242 243 await settled(); 244 const serverUrl = `/v1/client/fs/logs/${commonProps.allocation.id}`; 245 assert.ok( 246 this.server.handledRequests.filter(req => req.url.startsWith(serverUrl)).length, 247 'Log request was later made to the server' 248 ); 249 250 assert.ok( 251 this.server.handledRequests.filter(req => clientUrlRegex.test(req.url))[0].aborted, 252 'Client log request was aborted' 253 ); 254 }); 255 256 test('When both the client and the server are inaccessible, an error message is shown', async function(assert) { 257 run.later(run, run.cancelTimers, allowedConnectionTime * 5); 258 259 // override client and server responses to timeout 260 this.server.get( 261 `http://${HOST}/v1/client/fs/logs/:allocation_id`, 262 () => [400, {}, ''], 263 allowedConnectionTime * 2 264 ); 265 this.server.get( 266 '/v1/client/fs/logs/:allocation_id', 267 () => [400, {}, ''], 268 allowedConnectionTime * 2 269 ); 270 271 this.setProperties(commonProps); 272 await render(hbs`<TaskLog 273 @allocation={{allocation}} 274 @task={{taskState}} 275 @clientTimeout={{clientTimeout}} 276 @serverTimeout={{serverTimeout}} />`); 277 278 await settled(); 279 const clientUrlRegex = new RegExp(`${HOST}/v1/client/fs/logs/${commonProps.allocation.id}`); 280 assert.ok( 281 this.server.handledRequests.filter(req => clientUrlRegex.test(req.url)).length, 282 'Log request was initially made directly to the client' 283 ); 284 const serverUrl = `/v1/client/fs/logs/${commonProps.allocation.id}`; 285 assert.ok( 286 this.server.handledRequests.filter(req => req.url.startsWith(serverUrl)).length, 287 'Log request was later made to the server' 288 ); 289 assert.ok(find('[data-test-connection-error]'), 'An error message is shown'); 290 291 await click('[data-test-connection-error-dismiss]'); 292 assert.notOk(find('[data-test-connection-error]'), 'The error message is dismissable'); 293 294 await componentA11yAudit(this.element, assert); 295 }); 296 297 test('When the client is inaccessible, the server is accessible, and stderr is pressed before the client timeout occurs, the no connection error is not shown', async function(assert) { 298 // override client response to timeout 299 this.server.get( 300 `http://${HOST}/v1/client/fs/logs/:allocation_id`, 301 () => [400, {}, ''], 302 allowedConnectionTime * 2 303 ); 304 305 // Click stderr before the client request responds 306 run.later(() => { 307 click('[data-test-log-action="stderr"]'); 308 run.later(run, run.cancelTimers, commonProps.interval * 5); 309 }, allowedConnectionTime / 2); 310 311 this.setProperties(commonProps); 312 await render(hbs`<TaskLog 313 @allocation={{allocation}} 314 @task={{taskState}} 315 @clientTimeout={{clientTimeout}} 316 @serverTimeout={{serverTimeout}} />`); 317 318 await settled(); 319 320 const clientUrlRegex = new RegExp(`${HOST}/v1/client/fs/logs/${commonProps.allocation.id}`); 321 const clientRequests = this.server.handledRequests.filter(req => clientUrlRegex.test(req.url)); 322 assert.ok( 323 clientRequests.find(req => req.queryParams.type === 'stdout'), 324 'Client request for stdout' 325 ); 326 assert.ok( 327 clientRequests.find(req => req.queryParams.type === 'stderr'), 328 'Client request for stderr' 329 ); 330 331 const serverUrl = `/v1/client/fs/logs/${commonProps.allocation.id}`; 332 assert.ok( 333 this.server.handledRequests 334 .filter(req => req.url.startsWith(serverUrl)) 335 .find(req => req.queryParams.type === 'stderr'), 336 'Server request for stderr' 337 ); 338 339 assert.notOk(find('[data-test-connection-error]'), 'An error message is not shown'); 340 }); 341 342 test('The log streaming mode is persisted in localStorage', async function(assert) { 343 window.localStorage.nomadLogMode = JSON.stringify('stderr'); 344 345 run.later(run, run.cancelTimers, commonProps.interval); 346 347 this.setProperties(commonProps); 348 await render(hbs`<TaskLog @allocation={{allocation}} @task={{taskState}} />`); 349 350 await settled(); 351 assert.ok(this.server.handledRequests.filter(req => req.queryParams.type === 'stderr').length); 352 assert.notOk( 353 this.server.handledRequests.filter(req => req.queryParams.type === 'stdout').length 354 ); 355 356 click('[data-test-log-action="stdout"]'); 357 run.later(run, run.cancelTimers, commonProps.interval); 358 359 await settled(); 360 assert.ok(this.server.handledRequests.filter(req => req.queryParams.type === 'stdout').length); 361 assert.equal(window.localStorage.nomadLogMode, JSON.stringify('stdout')); 362 }); 363 });