common.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /* Add js reporter for sauce */
  2. jasmine.getEnv().addReporter(new jasmine.JSReporter2());
  3. jasmine.getEnv().defaultTimeoutInterval = 3000;
  4. // From https://github.com/axemclion/grunt-saucelabs/issues/109#issuecomment-166767282
  5. // (function () {
  6. // var oldJSReport = window.jasmine.getJSReport;
  7. // window.jasmine.getJSReport = function () {
  8. // var results = oldJSReport();
  9. // if (results) {
  10. // return {
  11. // durationSec: results.durationSec,
  12. // suites: removePassingTests(results.suites),
  13. // passed: results.passed
  14. // };
  15. // } else {
  16. // return null;
  17. // }
  18. // };
  19. // function removePassingTests (suites) {
  20. // return suites.filter(specFailed)
  21. // .map(mapSuite);
  22. // }
  23. // function mapSuite (suite) {
  24. // var result = {};
  25. // for (var s in suite) {
  26. // result[s] = suite[s];
  27. // }
  28. // result.specs = suite.specs.filter(specFailed);
  29. // result.suites = removePassingTests(suite.suites);
  30. // return result;
  31. // }
  32. // function specFailed (item) {
  33. // return !item.passed;
  34. // }
  35. // })();
  36. /* record log messages for testing */
  37. var logMessages = [];
  38. window.less = window.less || {};
  39. var logLevel_debug = 4,
  40. logLevel_info = 3,
  41. logLevel_warn = 2,
  42. logLevel_error = 1;
  43. // The amount of logging in the javascript console.
  44. // 3 - Debug, information and errors
  45. // 2 - Information and errors
  46. // 1 - Errors
  47. // 0 - None
  48. // Defaults to 2
  49. less.loggers = [
  50. {
  51. debug: function(msg) {
  52. if (less.options.logLevel >= logLevel_debug) {
  53. logMessages.push(msg);
  54. }
  55. },
  56. info: function(msg) {
  57. if (less.options.logLevel >= logLevel_info) {
  58. logMessages.push(msg);
  59. }
  60. },
  61. warn: function(msg) {
  62. if (less.options.logLevel >= logLevel_warn) {
  63. logMessages.push(msg);
  64. }
  65. },
  66. error: function(msg) {
  67. if (less.options.logLevel >= logLevel_error) {
  68. logMessages.push(msg);
  69. }
  70. }
  71. }
  72. ];
  73. testLessEqualsInDocument = function () {
  74. testLessInDocument(testSheet);
  75. };
  76. testLessErrorsInDocument = function (isConsole) {
  77. testLessInDocument(isConsole ? testErrorSheetConsole : testErrorSheet);
  78. };
  79. testLessInDocument = function (testFunc) {
  80. var links = document.getElementsByTagName('link'),
  81. typePattern = /^text\/(x-)?less$/;
  82. for (var i = 0; i < links.length; i++) {
  83. if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
  84. (links[i].type.match(typePattern)))) {
  85. testFunc(links[i]);
  86. }
  87. }
  88. };
  89. ieFormat = function(text) {
  90. var styleNode = document.createElement('style');
  91. styleNode.setAttribute('type', 'text/css');
  92. var headNode = document.getElementsByTagName('head')[0];
  93. headNode.appendChild(styleNode);
  94. try {
  95. if (styleNode.styleSheet) {
  96. styleNode.styleSheet.cssText = text;
  97. } else {
  98. styleNode.innerText = text;
  99. }
  100. } catch (e) {
  101. throw new Error('Couldn\'t reassign styleSheet.cssText.');
  102. }
  103. var transformedText = styleNode.styleSheet ? styleNode.styleSheet.cssText : styleNode.innerText;
  104. headNode.removeChild(styleNode);
  105. return transformedText;
  106. };
  107. testSheet = function (sheet) {
  108. it(sheet.id + ' should match the expected output', function (done) {
  109. var lessOutputId = sheet.id.replace('original-', ''),
  110. expectedOutputId = 'expected-' + lessOutputId,
  111. lessOutputObj,
  112. lessOutput,
  113. expectedOutputHref = document.getElementById(expectedOutputId).href,
  114. expectedOutput = loadFile(expectedOutputHref);
  115. // Browser spec generates less on the fly, so we need to loose control
  116. less.pageLoadFinished
  117. .then(function () {
  118. lessOutputObj = document.getElementById(lessOutputId);
  119. lessOutput = lessOutputObj.styleSheet ? lessOutputObj.styleSheet.cssText :
  120. (lessOutputObj.innerText || lessOutputObj.innerHTML);
  121. expectedOutput
  122. .then(function (text) {
  123. if (window.navigator.userAgent.indexOf('MSIE') >= 0 ||
  124. window.navigator.userAgent.indexOf('Trident/') >= 0) {
  125. text = ieFormat(text);
  126. }
  127. expect(lessOutput).toEqual(text);
  128. done();
  129. });
  130. })
  131. .catch(function(err) {
  132. console.log(err);
  133. done();
  134. });
  135. });
  136. };
  137. // TODO: do it cleaner - the same way as in css
  138. function extractId(href) {
  139. return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain
  140. .replace(/^\//, '') // Remove root /
  141. .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension
  142. .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
  143. .replace(/\./g, ':'); // Replace dots with colons(for valid id)
  144. }
  145. waitFor = function (waitFunc) {
  146. return new Promise(function (resolve) {
  147. var timeoutId = setInterval(function () {
  148. if (waitFunc()) {
  149. clearInterval(timeoutId);
  150. resolve();
  151. }
  152. }, 5);
  153. });
  154. };
  155. testErrorSheet = function (sheet) {
  156. it(sheet.id + ' should match an error', function (done) {
  157. var lessHref = sheet.href,
  158. id = 'less-error-message:' + extractId(lessHref),
  159. errorHref = lessHref.replace(/.less$/, '.txt'),
  160. errorFile = loadFile(errorHref),
  161. actualErrorElement,
  162. actualErrorMsg;
  163. // Less.js sets 10ms timer in order to add error message on top of page.
  164. waitFor(function () {
  165. actualErrorElement = document.getElementById(id);
  166. return actualErrorElement !== null;
  167. }).then(function () {
  168. var innerText = (actualErrorElement.innerHTML
  169. .replace(/<h3>|<\/?p>|<a href="[^"]*">|<\/a>|<ul>|<\/?pre( class="?[^">]*"?)?>|<\/li>|<\/?label>/ig, '')
  170. .replace(/<\/h3>/ig, ' ')
  171. .replace(/<li>|<\/ul>|<br>/ig, '\n'))
  172. .replace(/&amp;/ig, '&')
  173. // for IE8
  174. .replace(/\r\n/g, '\n')
  175. .replace(/\. \nin/, '. in');
  176. actualErrorMsg = innerText
  177. .replace(/\n\d+/g, function (lineNo) {
  178. return lineNo + ' ';
  179. })
  180. .replace(/\n\s*in /g, ' in ')
  181. .replace(/\n{2,}/g, '\n')
  182. .replace(/\nStack Trace\n[\s\S]*/i, '')
  183. .replace(/\n$/, '')
  184. .trim();
  185. errorFile
  186. .then(function (errorTxt) {
  187. errorTxt = errorTxt
  188. .replace(/\{path\}/g, '')
  189. .replace(/\{pathrel\}/g, '')
  190. .replace(/\{pathhref\}/g, 'http://localhost:8081/test/less/errors/')
  191. .replace(/\{404status\}/g, ' (404)')
  192. .replace(/\{node\}[\s\S]*\{\/node\}/g, '')
  193. .replace(/\n$/, '')
  194. .trim();
  195. expect(actualErrorMsg).toEqual(errorTxt);
  196. if (errorTxt == actualErrorMsg) {
  197. actualErrorElement.style.display = 'none';
  198. }
  199. done();
  200. });
  201. });
  202. });
  203. };
  204. testErrorSheetConsole = function (sheet) {
  205. it(sheet.id + ' should match an error', function (done) {
  206. var lessHref = sheet.href,
  207. id = sheet.id.replace(/^original-less:/, 'less-error-message:'),
  208. errorHref = lessHref.replace(/.less$/, '.txt'),
  209. errorFile = loadFile(errorHref),
  210. actualErrorElement = document.getElementById(id),
  211. actualErrorMsg = logMessages[logMessages.length - 1]
  212. .replace(/\nStack Trace\n[\s\S]*/, '');
  213. describe('the error', function () {
  214. expect(actualErrorElement).toBe(null);
  215. });
  216. errorFile
  217. .then(function (errorTxt) {
  218. errorTxt
  219. .replace(/\{path\}/g, '')
  220. .replace(/\{pathrel\}/g, '')
  221. .replace(/\{pathhref\}/g, 'http://localhost:8081/browser/less/')
  222. .replace(/\{404status\}/g, ' (404)')
  223. .replace(/\{node\}.*\{\/node\}/g, '')
  224. .trim();
  225. expect(actualErrorMsg).toEqual(errorTxt);
  226. done();
  227. });
  228. });
  229. };
  230. loadFile = function (href) {
  231. return new Promise(function (resolve, reject) {
  232. var request = new XMLHttpRequest();
  233. request.open('GET', href, true);
  234. request.onreadystatechange = function () {
  235. if (request.readyState == 4) {
  236. resolve(request.responseText.replace(/\r/g, ''));
  237. }
  238. };
  239. request.send(null);
  240. });
  241. };
  242. jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;