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 }