runtests.js (7829B)
1 Components.utils.import("resource://gre/modules/Services.jsm"); 2 Services.scriptloader.loadSubScript("resource://zotero/polyfill.js"); 3 Components.utils.import("resource://gre/modules/osfile.jsm"); 4 var EventUtils = Components.utils.import("resource://zotero-unit/EventUtils.jsm"); 5 6 var ZoteroUnit = Components.classes["@mozilla.org/commandlinehandler/general-startup;1?type=zotero-unit"]. 7 getService(Components.interfaces.nsISupports). 8 wrappedJSObject; 9 10 var dump = ZoteroUnit.dump; 11 12 // Mocha HTML reporter doesn't show deepEqual diffs, so we change this. 13 chai.config.truncateThreshold = 0 14 15 function quit(failed) { 16 // Quit with exit status 17 if(!failed) { 18 OS.File.writeAtomic(OS.Path.join(OS.Constants.Path.profileDir, "success"), new Uint8Array(0)); 19 } 20 if(!ZoteroUnit.noquit) { 21 setTimeout(function () { 22 Components.classes['@mozilla.org/toolkit/app-startup;1'] 23 .getService(Components.interfaces.nsIAppStartup) 24 .quit(Components.interfaces.nsIAppStartup.eForceQuit); 25 }, 250); 26 } 27 } 28 29 if (ZoteroUnit.makeTestData) { 30 let dataPath = getTestDataDirectory().path; 31 32 Zotero.Prefs.set("export.citePaperJournalArticleURL", true); 33 34 let dataFiles = [ 35 { 36 name: 'allTypesAndFields', 37 func: generateAllTypesAndFieldsData 38 }, 39 { 40 name: 'itemJSON', 41 func: generateItemJSONData, 42 args: [null] 43 }, 44 // { 45 // name: 'citeProcJSExport', 46 // func: generateCiteProcJSExportData 47 // }, 48 { 49 name: 'translatorExportLegacy', 50 func: generateTranslatorExportData, 51 args: [true] 52 }, 53 { 54 name: 'translatorExport', 55 func: generateTranslatorExportData, 56 args: [false] 57 } 58 ]; 59 Zotero.Promise.coroutine(function* () { 60 yield Zotero.initializationPromise; 61 for (let i=0; i<dataFiles.length; i++) { 62 let first = !i; 63 let params = dataFiles[i]; 64 65 // Make sure to not run next loop if previous fails 66 if (!first) dump('\n'); 67 dump('Generating data for ' + params.name + '...'); 68 69 let filePath = OS.Path.join(dataPath, params.name + '.js'); 70 let exists = yield OS.File.exists(filePath); 71 let currentData; 72 if (exists) { 73 currentData = loadSampleData(params.name); 74 } 75 76 let args = params.args || []; 77 args.push(currentData); 78 let newData = params.func.apply(null, args); 79 if (newData instanceof Zotero.Promise) { 80 newData = yield newData; 81 } 82 let str = stableStringify(newData); 83 84 yield OS.File.writeAtomic(OS.Path.join(dataPath, params.name + '.js'), str); 85 dump("done."); 86 } 87 })() 88 .catch(function(e) { dump('\n'); dump(Zotero.Utilities.varDump(e)) }) 89 .finally(function() { quit(false) }); 90 } 91 92 function Reporter(runner) { 93 var indents = 0, passed = 0, failed = 0, aborted = false; 94 95 function indent() { 96 return Array(indents).join(' '); 97 } 98 99 runner.on('start', function(){}); 100 101 runner.on('suite', function(suite){ 102 ++indents; 103 dump("\r"+indent()+suite.title+"\n"); 104 }); 105 106 runner.on('suite end', function(suite){ 107 --indents; 108 if (1 == indents) dump("\n"); 109 }); 110 111 runner.on('pending', function(test){ 112 dump("\r"+indent()+"pending -"+test.title+"\n"); 113 }); 114 115 runner.on('pass', function(test){ 116 passed++; 117 var msg = "\r"+indent()+Mocha.reporters.Base.symbols.ok+" "+test.title; 118 if ('fast' != test.speed) { 119 msg += " ("+Math.round(test.duration)+" ms)"; 120 } 121 dump(msg+"\n"); 122 }); 123 124 runner.on('fail', function(test, err){ 125 // Remove internal code references 126 err.stack = err.stack.replace(/.+(?:zotero-unit\/|\/Task\.jsm|\/bluebird\.js).+\n?/g, ""); 127 128 // Strip "From previous event:" block if it's all internals 129 if (err.stack.indexOf('From previous event:') != -1) { 130 err.stack = err.stack 131 // Drop first line, because it contains the error message 132 .replace(/^.+\n/, '') 133 // Drop "From previous event:" labels for empty blocks 134 .replace(/.*From previous event:.*(?:\n(?=\s*From previous event:)|\s*$)/g, ''); 135 } 136 137 // Make sure there's a blank line after all stack traces 138 err.stack = err.stack.replace(/\s*$/, '\n\n'); 139 140 failed++; 141 let indentStr = indent(); 142 dump("\r" + indentStr 143 // Dark red X for errors 144 + "\x1B[31;40m" + Mocha.reporters.Base.symbols.err + " [FAIL]\x1B[0m" 145 // Trigger bell if interactive 146 + (Zotero.automatedTest ? "" : "\x07") 147 + " " + test.title + "\n" 148 + indentStr + " " + err.toString() + " at\n" 149 + err.stack.replace(/^/gm, indentStr + " ")); 150 151 if (ZoteroUnit.bail) { 152 aborted = true; 153 runner.abort(); 154 } 155 }); 156 157 runner.on('end', function() { 158 dump(passed + "/" + (passed + failed) + " tests passed" 159 + (aborted ? " -- aborting" : "") + "\n"); 160 quit(failed != 0); 161 }); 162 } 163 164 // Setup Mocha 165 mocha.setup({ 166 ui: "bdd", 167 reporter: Reporter, 168 timeout: ZoteroUnit.timeout || 10000, 169 grep: ZoteroUnit.grep 170 }); 171 172 coMocha(Mocha); 173 174 before(function () { 175 // Store all prefs set in runtests.sh 176 Components.utils.import("resource://zotero/config.js"); 177 var prefBranch = Services.prefs.getBranch(ZOTERO_CONFIG.PREF_BRANCH); 178 ZoteroUnit.customPrefs = {}; 179 prefBranch.getChildList("", {}) 180 .filter(key => prefBranch.prefHasUserValue(key)) 181 .forEach(key => ZoteroUnit.customPrefs[key] = Zotero.Prefs.get(key)); 182 }); 183 184 /** 185 * Clear all prefs, and reset those set in runtests.sh to original values 186 */ 187 function resetPrefs() { 188 Components.utils.import("resource://zotero/config.js"); 189 var prefBranch = Services.prefs.getBranch(ZOTERO_CONFIG.PREF_BRANCH); 190 prefBranch.getChildList("", {}).forEach(key => { 191 var origVal = ZoteroUnit.customPrefs[key]; 192 if (origVal !== undefined) { 193 if (origVal != Zotero.Prefs.get(key)) { 194 Zotero.Prefs.set(key, ZoteroUnit.customPrefs[key]); 195 } 196 } 197 else if (prefBranch.prefHasUserValue(key)) { 198 Zotero.Prefs.clear(key) 199 } 200 }); 201 } 202 203 afterEach(function () { 204 resetPrefs(); 205 }); 206 207 208 var assert = chai.assert, 209 expect = chai.expect; 210 211 // Set up tests to run 212 var run = ZoteroUnit.runTests; 213 if(run && ZoteroUnit.tests) { 214 function getTestFilename(test) { 215 // Allow foo, fooTest, fooTest.js, and tests/fooTest.js 216 test = test.replace(/\.js$/, ""); 217 test = test.replace(/Test$/, ""); 218 test = test.replace(/^tests[/\\]/, ""); 219 return test + "Test.js"; 220 } 221 222 var testDirectory = getTestDataDirectory().parent, 223 testFiles = []; 224 if(ZoteroUnit.tests == "all") { 225 var enumerator = testDirectory.directoryEntries; 226 let startFile = ZoteroUnit.startAt ? getTestFilename(ZoteroUnit.startAt) : false; 227 let started = !startFile; 228 let stopFile = ZoteroUnit.stopAt ? getTestFilename(ZoteroUnit.stopAt) : false; 229 while(enumerator.hasMoreElements()) { 230 var file = enumerator.getNext().QueryInterface(Components.interfaces.nsIFile); 231 if (file.leafName.endsWith(".js")) { 232 testFiles.push(file.leafName); 233 } 234 } 235 testFiles.sort(); 236 237 // Find the start and stop files 238 let startPos = 0; 239 let stopPos = testFiles.length - 1; 240 for (let i = 0; i < testFiles.length; i++) { 241 if (testFiles[i] == startFile) { 242 startPos = i; 243 } 244 if (testFiles[i] == stopFile) { 245 stopPos = i; 246 break; 247 } 248 } 249 if (startFile && startPos == 0 && startFile != testFiles[0]) { 250 dump(`Invalid start file ${startFile}\n`); 251 } 252 testFiles = testFiles.slice(startPos, stopPos + 1); 253 } else { 254 var specifiedTests = ZoteroUnit.tests.split(","); 255 for (let test of specifiedTests) { 256 let fname = getTestFilename(test); 257 let file = testDirectory.clone(); 258 file.append(fname); 259 if (!file.exists()) { 260 dump("Invalid test file "+test+"\n"); 261 run = false; 262 quit(true); 263 } 264 testFiles.push(fname); 265 } 266 } 267 268 for(var fname of testFiles) { 269 var el = document.createElement("script"); 270 el.type = "application/javascript;version=1.8"; 271 el.src = "resource://zotero-unit-tests/"+fname; 272 el.async = false; 273 document.body.appendChild(el); 274 } 275 } 276 277 if(run) { 278 window.onload = function() { 279 Zotero.spawn(function* () { 280 yield Zotero.Schema.schemaUpdatePromise; 281 282 initPDFToolsPath(); 283 284 return mocha.run(); 285 }) 286 }; 287 }