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  });