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