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