support.js (3900B)
1 // ---------------------------------------- 2 // Test Utilities 3 // ---------------------------------------- 4 5 setup({explicit_timeout: true}); 6 7 const tests = []; 8 window.addEventListener('DOMContentLoaded', e => { 9 const header = document.createElement('h1'); 10 header.innerText = document.title; 11 document.body.appendChild(header); 12 const elem = document.createElement('div'); 13 elem.style.cssText = 'height: 50px; border: 1px dotted red;'; 14 elem.innerHTML = 'Drop or paste the <b>support/upload</b> directory here.</div>'; 15 document.body.appendChild(elem); 16 elem.addEventListener('dragover', e => { 17 e.preventDefault(); 18 }); 19 const onDropOrPaste = dataTransfer => { 20 for (let i = 0; i < dataTransfer.items.length; ++i) { 21 const item = dataTransfer.items[i]; 22 if (item.kind !== 'file') 23 continue; 24 const entry = item.webkitGetAsEntry(); 25 elem.parentElement.removeChild(elem); 26 tests.forEach(f => f(entry, item)); 27 break; 28 } 29 }; 30 elem.addEventListener('drop', e => { 31 e.preventDefault(); 32 onDropOrPaste(e.dataTransfer); 33 }); 34 elem.addEventListener('paste', e => { 35 e.preventDefault(); 36 onDropOrPaste(e.clipboardData); 37 }); 38 }); 39 40 41 // Registers a test to be run when an entry is dropped. Calls |func| 42 // with (test, entry, item); |func| must call `test.done()` when complete. 43 function entry_test(func, description) { 44 const test = async_test(description); 45 tests.push(test.step_func((entry, item) => func(test, entry, item))); 46 } 47 48 // Registers a test to be run when an entry is dropped. Digs the named 49 // |file| out of the dropped entry and calls |func| with 50 // (test, file_entry); |func| must call `test.done()` when complete. 51 function file_entry_test(name, func, description) { 52 return entry_test((t, entry, item) => { 53 getChildEntry(entry, name, 54 t.step_func((entry) => func(t, entry)), 55 t.unreached_func('Did not find expected file: ' + name)); 56 }, description); 57 } 58 59 60 // ---------------------------------------- 61 // Paths 62 // ---------------------------------------- 63 64 const INVALID_PATHS = [ 65 '\x00', 'a-\x00-b', 66 '\\', 'a-\\-b' 67 ]; 68 const EMPTY_PATHS = ['', null, undefined]; 69 const NOT_FOUND_PATHS = [ 70 'nope', 71 '/upload/nope', 72 './nope', 73 'subdir/../nope', 74 '\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f', 75 '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f', 76 ]; 77 78 const DIR_PATHS = [ 79 'subdir', 80 '/upload/subdir', 81 './subdir', 82 'subdir/.', 83 'subdir/../subdir', 84 'subdir/./../subdir', 85 'subdir/../subdir/.', 86 '//upload/subdir', 87 '/upload//subdir', 88 './/subdir', 89 'subdir//.', 90 ]; 91 const FILE_PATHS = [ 92 'file.txt', 93 '/upload/file.txt', 94 'subdir/../file.txt', 95 '//upload/file.txt', 96 '/upload//file.txt', 97 'subdir/./../file.txt', 98 ]; 99 100 // ---------------------------------------- 101 // Helpers 102 // ---------------------------------------- 103 104 // Wrapper for FileSystemDirectoryReader that yields all entries via a 105 // Promise. 106 107 function getEntriesAsPromise(dirEntry) { 108 return new Promise((resolve, reject) => { 109 const result = []; 110 const reader = dirEntry.createReader(); 111 const doBatch = () => { 112 reader.readEntries(entries => { 113 if (entries.length > 0) { 114 entries.forEach(e => result.push(e)); 115 doBatch(); 116 } else { 117 resolve(result); 118 } 119 }, reject); 120 }; 121 doBatch(); 122 }); 123 } 124 125 126 // Wrapper for FileSystemDirectoryReader that yields a single entry by 127 // name via a callback. Can be used instead of getFile() or 128 // getDirectory() since not all implementations support those. 129 130 function getChildEntry(dirEntry, name, callback, errback) { 131 getEntriesAsPromise(dirEntry) 132 .then(entries => { 133 const entry = entries.filter(entry => entry.name === name)[0]; 134 if (!entry) 135 throw new Error('No such file: ' + name); 136 return entry; 137 }).then(callback, errback); 138 }