Gruntfile.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. 'use strict';
  2. module.exports = function (grunt) {
  3. grunt.option('stack', true)
  4. // Report the elapsed execution time of tasks.
  5. require('time-grunt')(grunt);
  6. var COMPRESS_FOR_TESTS = false;
  7. var git = require('git-rev');
  8. // Sauce Labs browser
  9. var browsers = [
  10. // Desktop browsers
  11. {
  12. browserName: 'chrome',
  13. version: 'latest',
  14. platform: 'Windows 7'
  15. },
  16. {
  17. browserName: 'firefox',
  18. version: 'latest',
  19. platform: 'Linux'
  20. },
  21. {
  22. browserName: 'safari',
  23. version: '9',
  24. platform: 'OS X 10.11'
  25. },
  26. {
  27. browserName: 'internet explorer',
  28. version: '8',
  29. platform: 'Windows XP'
  30. },
  31. {
  32. browserName: 'internet explorer',
  33. version: '11',
  34. platform: 'Windows 8.1'
  35. },
  36. {
  37. browserName: 'edge',
  38. version: '13',
  39. platform: 'Windows 10'
  40. },
  41. // Mobile browsers
  42. {
  43. browserName: 'ipad',
  44. deviceName: 'iPad Air Simulator',
  45. deviceOrientation: 'portrait',
  46. version: '8.4',
  47. platform: 'OS X 10.9'
  48. },
  49. {
  50. browserName: 'iphone',
  51. deviceName: 'iPhone 5 Simulator',
  52. deviceOrientation: 'portrait',
  53. version: '9.3',
  54. platform: 'OS X 10.11'
  55. },
  56. {
  57. browserName: 'android',
  58. deviceName: 'Google Nexus 7 HD Emulator',
  59. deviceOrientation: 'portrait',
  60. version: '4.4',
  61. platform: 'Linux'
  62. }
  63. ];
  64. var sauceJobs = {};
  65. var browserTests = [
  66. 'filemanager-plugin',
  67. 'visitor-plugin',
  68. 'global-vars',
  69. 'modify-vars',
  70. 'production',
  71. 'rootpath-relative',
  72. 'rootpath-rewrite-urls',
  73. 'rootpath',
  74. 'relative-urls',
  75. 'rewrite-urls',
  76. 'browser',
  77. 'no-js-errors',
  78. 'legacy'
  79. ];
  80. function makeJob(testName) {
  81. sauceJobs[testName] = {
  82. options: {
  83. urls: testName === 'all' ?
  84. browserTests.map(function(name) {
  85. return 'http://localhost:8081/tmp/browser/test-runner-' + name + '.html';
  86. }) :
  87. ['http://localhost:8081/tmp/browser/test-runner-' + testName + '.html'],
  88. testname: testName === 'all' ? 'Unit Tests for Less.js' : testName,
  89. browsers: browsers,
  90. public: 'public',
  91. recordVideo: false,
  92. videoUploadOnPass: false,
  93. recordScreenshots: process.env.TRAVIS_BRANCH !== 'master',
  94. build: process.env.TRAVIS_BRANCH === 'master' ? process.env.TRAVIS_JOB_ID : undefined,
  95. tags: [process.env.TRAVIS_BUILD_NUMBER, process.env.TRAVIS_PULL_REQUEST, process.env.TRAVIS_BRANCH],
  96. statusCheckAttempts: -1,
  97. sauceConfig: {
  98. 'idle-timeout': 100
  99. },
  100. throttled: 5,
  101. onTestComplete: function(result, callback) {
  102. // Called after a unit test is done, per page, per browser
  103. // 'result' param is the object returned by the test framework's reporter
  104. // 'callback' is a Node.js style callback function. You must invoke it after you
  105. // finish your work.
  106. // Pass a non-null value as the callback's first parameter if you want to throw an
  107. // exception. If your function is synchronous you can also throw exceptions
  108. // directly.
  109. // Passing true or false as the callback's second parameter passes or fails the
  110. // test. Passing undefined does not alter the test result. Please note that this
  111. // only affects the grunt task's result. You have to explicitly update the Sauce
  112. // Labs job's status via its REST API, if you want so.
  113. // This should be the encrypted value in Travis
  114. var user = process.env.SAUCE_USERNAME;
  115. var pass = process.env.SAUCE_ACCESS_KEY;
  116. git.short(function(hash) {
  117. require('phin')({
  118. method: 'PUT',
  119. url: ['https://saucelabs.com/rest/v1', user, 'jobs', result.job_id].join('/'),
  120. auth: { user: user, pass: pass },
  121. data: {
  122. passed: result.passed,
  123. build: 'build-' + hash
  124. }
  125. }, function (error, response) {
  126. if (error) {
  127. console.log(error);
  128. callback(error);
  129. } else if (response.statusCode !== 200) {
  130. console.log(response);
  131. callback(new Error('Unexpected response status'));
  132. } else {
  133. callback(null, result.passed);
  134. }
  135. });
  136. });
  137. }
  138. }
  139. };
  140. }
  141. // Make the SauceLabs jobs
  142. (['all'].concat(browserTests)).map(makeJob);
  143. // Project configuration.
  144. grunt.initConfig({
  145. pkg: grunt.file.readJSON('package.json'),
  146. meta: {
  147. copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>',
  148. banner: '/*!\n' +
  149. ' * Less - <%= pkg.description %> v<%= pkg.version %>\n' +
  150. ' * http://lesscss.org\n' +
  151. ' *\n' +
  152. ' * <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>>\n' +
  153. ' * Licensed under the <%= pkg.license %> License.\n' +
  154. ' *\n' +
  155. ' */\n\n' +
  156. ' /**' +
  157. ' * @license <%= pkg.license %>\n' +
  158. ' */\n\n'
  159. },
  160. shell: {
  161. options: {
  162. stdout: true,
  163. failOnError: true,
  164. execOptions: {
  165. maxBuffer: Infinity
  166. }
  167. },
  168. test: {
  169. command: 'node test/index.js'
  170. },
  171. benchmark: {
  172. command: 'node benchmark/index.js'
  173. },
  174. opts: { // test running with all current options (using `opts` since `options` means something already)
  175. command: [ // @TODO: make this more thorough
  176. // CURRENT OPTIONS
  177. // --math
  178. 'node bin/lessc --math=always test/less/lazy-eval.less tmp/lazy-eval.css',
  179. 'node bin/lessc --math=parens-division test/less/lazy-eval.less tmp/lazy-eval.css',
  180. 'node bin/lessc --math=parens test/less/lazy-eval.less tmp/lazy-eval.css',
  181. 'node bin/lessc --math=strict test/less/lazy-eval.less tmp/lazy-eval.css',
  182. 'node bin/lessc --math=strict-legacy test/less/lazy-eval.less tmp/lazy-eval.css',
  183. // DEPRECATED OPTIONS
  184. // --strict-math
  185. 'node bin/lessc --strict-math=on test/less/lazy-eval.less tmp/lazy-eval.css',
  186. ].join(' && ')
  187. },
  188. plugin: {
  189. command: [
  190. 'node bin/lessc --clean-css="--s1 --advanced" test/less/lazy-eval.less tmp/lazy-eval.css',
  191. 'cd lib',
  192. 'node ../bin/lessc --clean-css="--s1 --advanced" ../test/less/lazy-eval.less ../tmp/lazy-eval.css',
  193. 'cd ..',
  194. // Test multiple plugins
  195. 'node bin/lessc --plugin=clean-css="--s1 --advanced" --plugin=autoprefix="ie 11,Edge >= 13,Chrome >= 47,Firefox >= 45,iOS >= 9.2,Safari >= 9" test/less/lazy-eval.less tmp/lazy-eval.css'
  196. ].join(' && ')
  197. },
  198. 'sourcemap-test': { // quoted value doesn't seem to get picked up by time-grunt, or isn't output, at least; maybe just "sourcemap" is fine?
  199. command: [
  200. 'node bin/lessc --source-map=test/sourcemaps/maps/import-map.map test/less/import.less test/sourcemaps/import.css',
  201. 'node bin/lessc --source-map test/less/sourcemaps/basic.less test/sourcemaps/basic.css'
  202. ].join(' && ')
  203. },
  204. },
  205. browserify: {
  206. browser: {
  207. src: ['./lib/less-browser/bootstrap.js'],
  208. options: {
  209. exclude: ['promise'],
  210. browserifyOptions: {
  211. standalone: 'less',
  212. noParse: ['clone']
  213. }
  214. },
  215. dest: 'tmp/less.js'
  216. }
  217. },
  218. concat: {
  219. options: {
  220. stripBanners: 'all',
  221. banner: '<%= meta.banner %>'
  222. },
  223. browsertest: {
  224. src: COMPRESS_FOR_TESTS ? '<%= uglify.test.dest %>' : '<%= browserify.browser.dest %>',
  225. dest: 'test/browser/less.js'
  226. },
  227. dist: {
  228. src: '<%= browserify.browser.dest %>',
  229. dest: 'dist/less.js'
  230. }
  231. },
  232. uglify: {
  233. options: {
  234. banner: '<%= meta.banner %>',
  235. mangle: true,
  236. compress: {
  237. pure_getters: true
  238. }
  239. },
  240. dist: {
  241. src: ['<%= concat.dist.dest %>'],
  242. dest: 'dist/less.min.js'
  243. },
  244. test: {
  245. src: '<%= browserify.browser.dest %>',
  246. dest: 'tmp/less.min.js'
  247. }
  248. },
  249. eslint: {
  250. target: ['Gruntfile.js',
  251. 'test/**/*.js',
  252. 'lib/less*/**/*.js',
  253. 'bin/lessc',
  254. '!test/browser/jasmine-jsreporter.js',
  255. '!test/less/errors/plugin/plugin-error.js'
  256. ],
  257. options: {
  258. configFile: '.eslintrc.json'
  259. }
  260. },
  261. connect: {
  262. server: {
  263. options: {
  264. port: 8081
  265. }
  266. }
  267. },
  268. jasmine: {
  269. options: {
  270. keepRunner: true,
  271. host: 'http://localhost:8081/',
  272. vendor: [
  273. './node_modules/phantomjs-polyfill-object-assign/object-assign-polyfill.js',
  274. 'test/browser/vendor/promise.js',
  275. 'test/browser/jasmine-jsreporter.js',
  276. 'test/browser/common.js',
  277. 'test/browser/less.js'
  278. ],
  279. template: 'test/browser/test-runner-template.tmpl'
  280. },
  281. main: {
  282. // src is used to build list of less files to compile
  283. src: [
  284. 'test/less/*.less',
  285. '!test/less/plugin-preeval.less', // uses ES6 syntax
  286. // Don't test NPM import, obviously
  287. '!test/less/plugin-module.less',
  288. '!test/less/import-module.less',
  289. '!test/less/javascript.less',
  290. '!test/less/urls.less',
  291. '!test/less/empty.less'
  292. ],
  293. options: {
  294. helpers: 'test/browser/runner-main-options.js',
  295. specs: 'test/browser/runner-main-spec.js',
  296. outfile: 'tmp/browser/test-runner-main.html'
  297. }
  298. },
  299. legacy: {
  300. src: ['test/less/legacy/*.less'],
  301. options: {
  302. helpers: 'test/browser/runner-legacy-options.js',
  303. specs: 'test/browser/runner-legacy-spec.js',
  304. outfile: 'tmp/browser/test-runner-legacy.html'
  305. }
  306. },
  307. strictUnits: {
  308. src: ['test/less/strict-units/*.less'],
  309. options: {
  310. helpers: 'test/browser/runner-strict-units-options.js',
  311. specs: 'test/browser/runner-strict-units-spec.js',
  312. outfile: 'tmp/browser/test-runner-strict-units.html'
  313. }
  314. },
  315. errors: {
  316. src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less', 'test/browser/less/errors/*.less'],
  317. options: {
  318. timeout: 20000,
  319. helpers: 'test/browser/runner-errors-options.js',
  320. specs: 'test/browser/runner-errors-spec.js',
  321. outfile: 'tmp/browser/test-runner-errors.html'
  322. }
  323. },
  324. noJsErrors: {
  325. src: ['test/less/no-js-errors/*.less'],
  326. options: {
  327. helpers: 'test/browser/runner-no-js-errors-options.js',
  328. specs: 'test/browser/runner-no-js-errors-spec.js',
  329. outfile: 'tmp/browser/test-runner-no-js-errors.html'
  330. }
  331. },
  332. browser: {
  333. src: ['test/browser/less/*.less', 'test/browser/less/plugin/*.less'],
  334. options: {
  335. helpers: 'test/browser/runner-browser-options.js',
  336. specs: 'test/browser/runner-browser-spec.js',
  337. outfile: 'tmp/browser/test-runner-browser.html'
  338. }
  339. },
  340. relativeUrls: {
  341. src: ['test/browser/less/relative-urls/*.less'],
  342. options: {
  343. helpers: 'test/browser/runner-relative-urls-options.js',
  344. specs: 'test/browser/runner-relative-urls-spec.js',
  345. outfile: 'tmp/browser/test-runner-relative-urls.html'
  346. }
  347. },
  348. rewriteUrls: {
  349. src: ['test/browser/less/rewrite-urls/*.less'],
  350. options: {
  351. helpers: 'test/browser/runner-rewrite-urls-options.js',
  352. specs: 'test/browser/runner-rewrite-urls-spec.js',
  353. outfile: 'tmp/browser/test-runner-rewrite-urls.html'
  354. }
  355. },
  356. rootpath: {
  357. src: ['test/browser/less/rootpath/*.less'],
  358. options: {
  359. helpers: 'test/browser/runner-rootpath-options.js',
  360. specs: 'test/browser/runner-rootpath-spec.js',
  361. outfile: 'tmp/browser/test-runner-rootpath.html'
  362. }
  363. },
  364. rootpathRelative: {
  365. src: ['test/browser/less/rootpath-relative/*.less'],
  366. options: {
  367. helpers: 'test/browser/runner-rootpath-relative-options.js',
  368. specs: 'test/browser/runner-rootpath-relative-spec.js',
  369. outfile: 'tmp/browser/test-runner-rootpath-relative.html'
  370. }
  371. },
  372. rootpathRewriteUrls: {
  373. src: ['test/browser/less/rootpath-rewrite-urls/*.less'],
  374. options: {
  375. helpers: 'test/browser/runner-rootpath-rewrite-urls-options.js',
  376. specs: 'test/browser/runner-rootpath-rewrite-urls-spec.js',
  377. outfile: 'tmp/browser/test-runner-rootpath-rewrite-urls.html'
  378. }
  379. },
  380. production: {
  381. src: ['test/browser/less/production/*.less'],
  382. options: {
  383. helpers: 'test/browser/runner-production-options.js',
  384. specs: 'test/browser/runner-production-spec.js',
  385. outfile: 'tmp/browser/test-runner-production.html'
  386. }
  387. },
  388. modifyVars: {
  389. src: ['test/browser/less/modify-vars/*.less'],
  390. options: {
  391. helpers: 'test/browser/runner-modify-vars-options.js',
  392. specs: 'test/browser/runner-modify-vars-spec.js',
  393. outfile: 'tmp/browser/test-runner-modify-vars.html'
  394. }
  395. },
  396. globalVars: {
  397. src: ['test/browser/less/global-vars/*.less'],
  398. options: {
  399. helpers: 'test/browser/runner-global-vars-options.js',
  400. specs: 'test/browser/runner-global-vars-spec.js',
  401. outfile: 'tmp/browser/test-runner-global-vars.html'
  402. }
  403. },
  404. postProcessorPlugin: {
  405. src: ['test/less/postProcessorPlugin/*.less'],
  406. options: {
  407. helpers: ['test/plugins/postprocess/index.js','test/browser/runner-postProcessorPlugin-options.js'],
  408. specs: 'test/browser/runner-postProcessorPlugin.js',
  409. outfile: 'tmp/browser/test-runner-post-processor-plugin.html'
  410. }
  411. },
  412. preProcessorPlugin: {
  413. src: ['test/less/preProcessorPlugin/*.less'],
  414. options: {
  415. helpers: ['test/plugins/preprocess/index.js','test/browser/runner-preProcessorPlugin-options.js'],
  416. specs: 'test/browser/runner-preProcessorPlugin.js',
  417. outfile: 'tmp/browser/test-runner-pre-processor-plugin.html'
  418. }
  419. },
  420. visitorPlugin: {
  421. src: ['test/less/visitorPlugin/*.less'],
  422. options: {
  423. helpers: ['test/plugins/visitor/index.js','test/browser/runner-VisitorPlugin-options.js'],
  424. specs: 'test/browser/runner-VisitorPlugin.js',
  425. outfile: 'tmp/browser/test-runner-visitor-plugin.html'
  426. }
  427. },
  428. filemanagerPlugin: {
  429. src: ['test/less/filemanagerPlugin/*.less'],
  430. options: {
  431. helpers: ['test/plugins/filemanager/index.js','test/browser/runner-filemanagerPlugin-options.js'],
  432. specs: 'test/browser/runner-filemanagerPlugin.js',
  433. outfile: 'tmp/browser/test-runner-filemanager-plugin.html'
  434. }
  435. }
  436. },
  437. 'saucelabs-jasmine': sauceJobs,
  438. // Clean the version of less built for the tests
  439. clean: {
  440. test: ['test/browser/less.js', 'tmp', 'test/less-bom'],
  441. 'sourcemap-test': ['test/sourcemaps/*.css', 'test/sourcemaps/*.map'],
  442. sauce_log: ['sc_*.log']
  443. }
  444. });
  445. // Load these plugins to provide the necessary tasks
  446. grunt.loadNpmTasks('grunt-saucelabs');
  447. require('jit-grunt')(grunt);
  448. // by default, run tests
  449. grunt.registerTask('default', [
  450. 'test'
  451. ]);
  452. // Release
  453. grunt.registerTask('dist', [
  454. 'browserify:browser',
  455. 'concat:dist',
  456. 'uglify:dist'
  457. ]);
  458. // Create the browser version of less.js
  459. grunt.registerTask('browsertest-lessjs', [
  460. 'browserify:browser',
  461. 'uglify:test',
  462. 'concat:browsertest'
  463. ]);
  464. // Run all browser tests
  465. grunt.registerTask('browsertest', [
  466. 'browsertest-lessjs',
  467. 'connect',
  468. 'jasmine'
  469. ]);
  470. // setup a web server to run the browser tests in a browser rather than phantom
  471. grunt.registerTask('browsertest-server', [
  472. 'browsertest-lessjs',
  473. 'jasmine::build',
  474. 'connect::keepalive'
  475. ]);
  476. var previous_force_state = grunt.option('force');
  477. grunt.registerTask('force',function(set) {
  478. if (set === 'on') {
  479. grunt.option('force',true);
  480. }
  481. else if (set === 'off') {
  482. grunt.option('force',false);
  483. }
  484. else if (set === 'restore') {
  485. grunt.option('force',previous_force_state);
  486. }
  487. });
  488. grunt.registerTask('sauce', [
  489. 'browsertest-lessjs',
  490. 'jasmine::build',
  491. 'connect',
  492. 'sauce-after-setup'
  493. ]);
  494. grunt.registerTask('sauce-after-setup', [
  495. 'saucelabs-jasmine:all',
  496. 'clean:sauce_log'
  497. ]);
  498. var testTasks = [
  499. 'clean',
  500. 'eslint',
  501. 'shell:test',
  502. 'shell:opts',
  503. 'shell:plugin',
  504. 'browsertest'
  505. ];
  506. if (isNaN(Number(process.env.TRAVIS_PULL_REQUEST, 10)) &&
  507. Number(process.env.TRAVIS_NODE_VERSION) === 4 &&
  508. (process.env.TRAVIS_BRANCH === 'master' || process.env.TRAVIS_BRANCH === '3.x')) {
  509. testTasks.push('force:on');
  510. testTasks.push('sauce-after-setup');
  511. testTasks.push('force:off');
  512. }
  513. // Run all tests
  514. grunt.registerTask('test', testTasks);
  515. // Run shell option tests (includes deprecated options)
  516. grunt.registerTask('shell-options', ['shell:opts']);
  517. // Run shell plugin test
  518. grunt.registerTask('shell-plugin', ['shell:plugin']);
  519. // Run all tests
  520. grunt.registerTask('quicktest', testTasks.slice(0, -1));
  521. // generate a good test environment for testing sourcemaps
  522. grunt.registerTask('sourcemap-test', [
  523. 'clean:sourcemap-test',
  524. 'shell:sourcemap-test',
  525. 'connect::keepalive'
  526. ]);
  527. // Run benchmark
  528. grunt.registerTask('benchmark', [
  529. 'shell:benchmark'
  530. ]);
  531. };