github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/acceptance/behaviors/fs.js (about)

     1  import { test } from 'qunit';
     2  import { currentURL, visit } from '@ember/test-helpers';
     3  
     4  import { filesForPath } from 'nomad-ui/mirage/config';
     5  import { formatBytes } from 'nomad-ui/helpers/format-bytes';
     6  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
     7  
     8  import Response from 'ember-cli-mirage/response';
     9  import moment from 'moment';
    10  
    11  import FS from 'nomad-ui/tests/pages/allocations/fs';
    12  
    13  const fileSort = (prop, files) => {
    14    let dir = [];
    15    let file = [];
    16    files.forEach(f => {
    17      if (f.isDir) {
    18        dir.push(f);
    19      } else {
    20        file.push(f);
    21      }
    22    });
    23  
    24    return dir.sortBy(prop).concat(file.sortBy(prop));
    25  };
    26  
    27  export default function browseFilesystem({
    28    pageObjectVisitPathFunctionName,
    29    pageObjectVisitFunctionName,
    30    visitSegments,
    31    getExpectedPathBase,
    32    getTitleComponent,
    33    getBreadcrumbComponent,
    34    getFilesystemRoot,
    35  }) {
    36    test('it passes an accessibility audit', async function(assert) {
    37      await FS[pageObjectVisitFunctionName](
    38        visitSegments({ allocation: this.allocation, task: this.task })
    39      );
    40      await a11yAudit(assert);
    41    });
    42  
    43    test('visiting filesystem root', async function(assert) {
    44      await FS[pageObjectVisitFunctionName](
    45        visitSegments({ allocation: this.allocation, task: this.task })
    46      );
    47  
    48      const pathBaseWithTrailingSlash = getExpectedPathBase({
    49        allocation: this.allocation,
    50        task: this.task,
    51      });
    52      const pathBaseWithoutTrailingSlash = pathBaseWithTrailingSlash.slice(0, -1);
    53  
    54      assert.equal(currentURL(), pathBaseWithoutTrailingSlash, 'No redirect');
    55    });
    56  
    57    test('visiting filesystem paths', async function(assert) {
    58      const paths = ['some-file.log', 'a/deep/path/to/a/file.log', '/', 'Unicode™®'];
    59  
    60      const testPath = async filePath => {
    61        let pathWithLeadingSlash = filePath;
    62  
    63        if (!pathWithLeadingSlash.startsWith('/')) {
    64          pathWithLeadingSlash = `/${filePath}`;
    65        }
    66  
    67        await FS[pageObjectVisitPathFunctionName]({
    68          ...visitSegments({ allocation: this.allocation, task: this.task }),
    69          path: filePath,
    70        });
    71        assert.equal(
    72          currentURL(),
    73          `${getExpectedPathBase({
    74            allocation: this.allocation,
    75            task: this.task,
    76          })}${encodeURIComponent(filePath)}`,
    77          'No redirect'
    78        );
    79        assert.equal(
    80          document.title,
    81          `${pathWithLeadingSlash} - ${getTitleComponent({
    82            allocation: this.allocation,
    83            task: this.task,
    84          })} - Nomad`
    85        );
    86        assert.equal(
    87          FS.breadcrumbsText,
    88          `${getBreadcrumbComponent({
    89            allocation: this.allocation,
    90            task: this.task,
    91          })} ${filePath.replace(/\//g, ' ')}`.trim()
    92        );
    93      };
    94  
    95      await paths.reduce(async (prev, filePath) => {
    96        await prev;
    97        return testPath(filePath);
    98      }, Promise.resolve());
    99    });
   100  
   101    test('navigating allocation filesystem', async function(assert) {
   102      const objects = { allocation: this.allocation, task: this.task };
   103      await FS[pageObjectVisitPathFunctionName]({ ...visitSegments(objects), path: '/' });
   104  
   105      const sortedFiles = fileSort(
   106        'name',
   107        filesForPath(this.server.schema.allocFiles, getFilesystemRoot(objects)).models
   108      );
   109  
   110      assert.ok(FS.fileViewer.isHidden);
   111  
   112      assert.equal(FS.directoryEntries.length, 4);
   113  
   114      assert.equal(FS.breadcrumbsText, getBreadcrumbComponent(objects));
   115  
   116      assert.equal(FS.breadcrumbs.length, 1);
   117      assert.ok(FS.breadcrumbs[0].isActive);
   118      assert.equal(FS.breadcrumbs[0].text, getBreadcrumbComponent(objects));
   119  
   120      FS.directoryEntries[0].as(directory => {
   121        const fileRecord = sortedFiles[0];
   122        assert.equal(directory.name, fileRecord.name, 'directories should come first');
   123        assert.ok(directory.isDirectory);
   124        assert.equal(directory.size, '', 'directory sizes are hidden');
   125        assert.equal(directory.lastModified, moment(fileRecord.modTime).fromNow());
   126        assert.notOk(directory.path.includes('//'), 'paths shouldn’t have redundant separators');
   127      });
   128  
   129      FS.directoryEntries[2].as(file => {
   130        const fileRecord = sortedFiles[2];
   131        assert.equal(file.name, fileRecord.name);
   132        assert.ok(file.isFile);
   133        assert.equal(file.size, formatBytes([fileRecord.size]));
   134        assert.equal(file.lastModified, moment(fileRecord.modTime).fromNow());
   135      });
   136  
   137      await FS.directoryEntries[0].visit();
   138  
   139      assert.equal(FS.directoryEntries.length, 1);
   140  
   141      assert.equal(FS.breadcrumbs.length, 2);
   142      assert.equal(FS.breadcrumbsText, `${getBreadcrumbComponent(objects)} ${this.directory.name}`);
   143  
   144      assert.notOk(FS.breadcrumbs[0].isActive);
   145  
   146      assert.equal(FS.breadcrumbs[1].text, this.directory.name);
   147      assert.ok(FS.breadcrumbs[1].isActive);
   148  
   149      await FS.directoryEntries[0].visit();
   150  
   151      assert.equal(FS.directoryEntries.length, 1);
   152      assert.notOk(
   153        FS.directoryEntries[0].path.includes('//'),
   154        'paths shouldn’t have redundant separators'
   155      );
   156  
   157      assert.equal(FS.breadcrumbs.length, 3);
   158      assert.equal(
   159        FS.breadcrumbsText,
   160        `${getBreadcrumbComponent(objects)} ${this.directory.name} ${this.nestedDirectory.name}`
   161      );
   162      assert.equal(FS.breadcrumbs[2].text, this.nestedDirectory.name);
   163  
   164      assert.notOk(
   165        FS.breadcrumbs[0].path.includes('//'),
   166        'paths shouldn’t have redundant separators'
   167      );
   168      assert.notOk(
   169        FS.breadcrumbs[1].path.includes('//'),
   170        'paths shouldn’t have redundant separators'
   171      );
   172  
   173      await FS.breadcrumbs[1].visit();
   174      assert.equal(FS.breadcrumbsText, `${getBreadcrumbComponent(objects)} ${this.directory.name}`);
   175      assert.equal(FS.breadcrumbs.length, 2);
   176    });
   177  
   178    test('sorting allocation filesystem directory', async function(assert) {
   179      this.server.get('/client/fs/ls/:allocation_id', () => {
   180        return [
   181          {
   182            Name: 'aaa-big-old-file',
   183            IsDir: false,
   184            Size: 19190000,
   185            ModTime: moment()
   186              .subtract(1, 'year')
   187              .format(),
   188          },
   189          {
   190            Name: 'mmm-small-mid-file',
   191            IsDir: false,
   192            Size: 1919,
   193            ModTime: moment()
   194              .subtract(6, 'month')
   195              .format(),
   196          },
   197          {
   198            Name: 'zzz-med-new-file',
   199            IsDir: false,
   200            Size: 191900,
   201            ModTime: moment().format(),
   202          },
   203          {
   204            Name: 'aaa-big-old-directory',
   205            IsDir: true,
   206            Size: 19190000,
   207            ModTime: moment()
   208              .subtract(1, 'year')
   209              .format(),
   210          },
   211          {
   212            Name: 'mmm-small-mid-directory',
   213            IsDir: true,
   214            Size: 1919,
   215            ModTime: moment()
   216              .subtract(6, 'month')
   217              .format(),
   218          },
   219          {
   220            Name: 'zzz-med-new-directory',
   221            IsDir: true,
   222            Size: 191900,
   223            ModTime: moment().format(),
   224          },
   225        ];
   226      });
   227  
   228      await FS[pageObjectVisitPathFunctionName]({
   229        ...visitSegments({ allocation: this.allocation, task: this.task }),
   230        path: '/',
   231      });
   232  
   233      assert.deepEqual(FS.directoryEntryNames(), [
   234        'aaa-big-old-directory',
   235        'mmm-small-mid-directory',
   236        'zzz-med-new-directory',
   237        'aaa-big-old-file',
   238        'mmm-small-mid-file',
   239        'zzz-med-new-file',
   240      ]);
   241  
   242      await FS.sortBy('Name');
   243  
   244      assert.deepEqual(FS.directoryEntryNames(), [
   245        'zzz-med-new-file',
   246        'mmm-small-mid-file',
   247        'aaa-big-old-file',
   248        'zzz-med-new-directory',
   249        'mmm-small-mid-directory',
   250        'aaa-big-old-directory',
   251      ]);
   252  
   253      await FS.sortBy('ModTime');
   254  
   255      assert.deepEqual(FS.directoryEntryNames(), [
   256        'zzz-med-new-file',
   257        'mmm-small-mid-file',
   258        'aaa-big-old-file',
   259        'zzz-med-new-directory',
   260        'mmm-small-mid-directory',
   261        'aaa-big-old-directory',
   262      ]);
   263  
   264      await FS.sortBy('ModTime');
   265  
   266      assert.deepEqual(FS.directoryEntryNames(), [
   267        'aaa-big-old-directory',
   268        'mmm-small-mid-directory',
   269        'zzz-med-new-directory',
   270        'aaa-big-old-file',
   271        'mmm-small-mid-file',
   272        'zzz-med-new-file',
   273      ]);
   274  
   275      await FS.sortBy('Size');
   276  
   277      assert.deepEqual(
   278        FS.directoryEntryNames(),
   279        [
   280          'aaa-big-old-file',
   281          'zzz-med-new-file',
   282          'mmm-small-mid-file',
   283          'zzz-med-new-directory',
   284          'mmm-small-mid-directory',
   285          'aaa-big-old-directory',
   286        ],
   287        'expected files to be sorted by descending size and directories to be sorted by descending name'
   288      );
   289  
   290      await FS.sortBy('Size');
   291  
   292      assert.deepEqual(
   293        FS.directoryEntryNames(),
   294        [
   295          'aaa-big-old-directory',
   296          'mmm-small-mid-directory',
   297          'zzz-med-new-directory',
   298          'mmm-small-mid-file',
   299          'zzz-med-new-file',
   300          'aaa-big-old-file',
   301        ],
   302        'expected directories to be sorted by name and files to be sorted by ascending size'
   303      );
   304    });
   305  
   306    test('viewing a file', async function(assert) {
   307      const objects = { allocation: this.allocation, task: this.task };
   308      const node = server.db.nodes.find(this.allocation.nodeId);
   309  
   310      server.get(`http://${node.httpAddr}/v1/client/fs/readat/:allocation_id`, function() {
   311        return new Response(500);
   312      });
   313  
   314      await FS[pageObjectVisitPathFunctionName]({ ...visitSegments(objects), path: '/' });
   315  
   316      const sortedFiles = fileSort(
   317        'name',
   318        filesForPath(this.server.schema.allocFiles, getFilesystemRoot(objects)).models
   319      );
   320      const fileRecord = sortedFiles.find(f => !f.isDir);
   321      const fileIndex = sortedFiles.indexOf(fileRecord);
   322  
   323      await FS.directoryEntries[fileIndex].visit();
   324  
   325      assert.equal(FS.breadcrumbsText, `${getBreadcrumbComponent(objects)} ${fileRecord.name}`);
   326  
   327      assert.ok(FS.fileViewer.isPresent);
   328  
   329      const requests = this.server.pretender.handledRequests;
   330      const secondAttempt = requests.pop();
   331      const firstAttempt = requests.pop();
   332  
   333      assert.equal(
   334        firstAttempt.url.split('?')[0],
   335        `//${node.httpAddr}/v1/client/fs/readat/${this.allocation.id}`,
   336        'Client is hit first'
   337      );
   338      assert.equal(firstAttempt.status, 500, 'Client request fails');
   339      assert.equal(
   340        secondAttempt.url.split('?')[0],
   341        `/v1/client/fs/readat/${this.allocation.id}`,
   342        'Server is hit second'
   343      );
   344    });
   345  
   346    test('viewing an empty directory', async function(assert) {
   347      await FS[pageObjectVisitPathFunctionName]({
   348        ...visitSegments({ allocation: this.allocation, task: this.task }),
   349        path: 'empty-directory',
   350      });
   351  
   352      assert.ok(FS.isEmptyDirectory);
   353    });
   354  
   355    test('viewing paths that produce stat API errors', async function(assert) {
   356      this.server.get('/client/fs/stat/:allocation_id', () => {
   357        return new Response(500, {}, 'no such file or directory');
   358      });
   359  
   360      await FS[pageObjectVisitPathFunctionName]({
   361        ...visitSegments({ allocation: this.allocation, task: this.task }),
   362        path: '/what-is-this',
   363      });
   364      assert.equal(FS.error.title, 'Not Found', '500 is interpreted as 404');
   365  
   366      await visit('/');
   367  
   368      this.server.get('/client/fs/stat/:allocation_id', () => {
   369        return new Response(999);
   370      });
   371  
   372      await FS[pageObjectVisitPathFunctionName]({
   373        ...visitSegments({ allocation: this.allocation, task: this.task }),
   374        path: '/what-is-this',
   375      });
   376      assert.equal(FS.error.title, 'Error', 'other statuses are passed through');
   377    });
   378  
   379    test('viewing paths that produce ls API errors', async function(assert) {
   380      this.server.get('/client/fs/ls/:allocation_id', () => {
   381        return new Response(500, {}, 'no such file or directory');
   382      });
   383  
   384      await FS[pageObjectVisitPathFunctionName]({
   385        ...visitSegments({ allocation: this.allocation, task: this.task }),
   386        path: this.directory.name,
   387      });
   388      assert.equal(FS.error.title, 'Not Found', '500 is interpreted as 404');
   389  
   390      await visit('/');
   391  
   392      this.server.get('/client/fs/ls/:allocation_id', () => {
   393        return new Response(999);
   394      });
   395  
   396      await FS[pageObjectVisitPathFunctionName]({
   397        ...visitSegments({ allocation: this.allocation, task: this.task }),
   398        path: this.directory.name,
   399      });
   400      assert.equal(FS.error.title, 'Error', 'other statuses are passed through');
   401    });
   402  }