Gruntfile.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  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:
  84. testName === "all"
  85. ? browserTests.map(function(name) {
  86. return (
  87. "http://localhost:8081/tmp/browser/test-runner-" +
  88. name +
  89. ".html"
  90. );
  91. })
  92. : [
  93. "http://localhost:8081/tmp/browser/test-runner-" +
  94. testName +
  95. ".html"
  96. ],
  97. testname:
  98. testName === "all" ? "Unit Tests for Less.js" : testName,
  99. browsers: browsers,
  100. public: "public",
  101. recordVideo: false,
  102. videoUploadOnPass: false,
  103. recordScreenshots: process.env.TRAVIS_BRANCH !== "master",
  104. build:
  105. process.env.TRAVIS_BRANCH === "master"
  106. ? process.env.TRAVIS_JOB_ID
  107. : undefined,
  108. tags: [
  109. process.env.TRAVIS_BUILD_NUMBER,
  110. process.env.TRAVIS_PULL_REQUEST,
  111. process.env.TRAVIS_BRANCH
  112. ],
  113. statusCheckAttempts: -1,
  114. sauceConfig: {
  115. "idle-timeout": 100
  116. },
  117. throttled: 5,
  118. onTestComplete: function(result, callback) {
  119. // Called after a unit test is done, per page, per browser
  120. // 'result' param is the object returned by the test framework's reporter
  121. // 'callback' is a Node.js style callback function. You must invoke it after you
  122. // finish your work.
  123. // Pass a non-null value as the callback's first parameter if you want to throw an
  124. // exception. If your function is synchronous you can also throw exceptions
  125. // directly.
  126. // Passing true or false as the callback's second parameter passes or fails the
  127. // test. Passing undefined does not alter the test result. Please note that this
  128. // only affects the grunt task's result. You have to explicitly update the Sauce
  129. // Labs job's status via its REST API, if you want so.
  130. // This should be the encrypted value in Travis
  131. var user = process.env.SAUCE_USERNAME;
  132. var pass = process.env.SAUCE_ACCESS_KEY;
  133. git.short(function(hash) {
  134. require("phin")(
  135. {
  136. method: "PUT",
  137. url: [
  138. "https://saucelabs.com/rest/v1",
  139. user,
  140. "jobs",
  141. result.job_id
  142. ].join("/"),
  143. auth: { user: user, pass: pass },
  144. data: {
  145. passed: result.passed,
  146. build: "build-" + hash
  147. }
  148. },
  149. function(error, response) {
  150. if (error) {
  151. console.log(error);
  152. callback(error);
  153. } else if (response.statusCode !== 200) {
  154. console.log(response);
  155. callback(
  156. new Error("Unexpected response status")
  157. );
  158. } else {
  159. callback(null, result.passed);
  160. }
  161. }
  162. );
  163. });
  164. }
  165. }
  166. };
  167. }
  168. // Make the SauceLabs jobs
  169. ["all"].concat(browserTests).map(makeJob);
  170. var semver = require('semver');
  171. var path = require('path');
  172. // Handle async / await in Rollup build for tests
  173. // Remove this when Node 6 is no longer supported for the build/test process
  174. const nodeVersion = semver.major(process.versions.node);
  175. let scriptRuntime = 'node';
  176. if (nodeVersion < 8) {
  177. scriptRuntime = path.resolve(path.join('node_modules', '.bin', 'babel-node')) + ' --presets=@babel/env';
  178. }
  179. // Project configuration.
  180. grunt.initConfig({
  181. shell: {
  182. options: {
  183. stdout: true,
  184. failOnError: true,
  185. execOptions: {
  186. maxBuffer: Infinity
  187. }
  188. },
  189. build: {
  190. command: [
  191. scriptRuntime + " build/rollup.js --dist"
  192. ].join(" && ")
  193. },
  194. testbuild: {
  195. command: [
  196. scriptRuntime + " build/rollup.js --lessc --out=./tmp/lessc",
  197. scriptRuntime + " build/rollup.js --node --out=./tmp/less.cjs.js",
  198. scriptRuntime + " build/rollup.js --browser --out=./test/browser/less.min.js"
  199. ].join(" && ")
  200. },
  201. test: {
  202. command: "node test/index.js"
  203. },
  204. benchmark: {
  205. command: "node benchmark/index.js"
  206. },
  207. opts: {
  208. // test running with all current options (using `opts` since `options` means something already)
  209. command: [
  210. // @TODO: make this more thorough
  211. // CURRENT OPTIONS
  212. // --math
  213. "node tmp/lessc --math=always test/less/lazy-eval.less tmp/lazy-eval.css",
  214. "node tmp/lessc --math=parens-division test/less/lazy-eval.less tmp/lazy-eval.css",
  215. "node tmp/lessc --math=parens test/less/lazy-eval.less tmp/lazy-eval.css",
  216. "node tmp/lessc --math=strict test/less/lazy-eval.less tmp/lazy-eval.css",
  217. "node tmp/lessc --math=strict-legacy test/less/lazy-eval.less tmp/lazy-eval.css",
  218. // DEPRECATED OPTIONS
  219. // --strict-math
  220. "node tmp/lessc --strict-math=on test/less/lazy-eval.less tmp/lazy-eval.css"
  221. ].join(" && ")
  222. },
  223. plugin: {
  224. command: [
  225. 'node tmp/lessc --clean-css="--s1 --advanced" test/less/lazy-eval.less tmp/lazy-eval.css',
  226. "cd lib",
  227. 'node ../tmp/lessc --clean-css="--s1 --advanced" ../test/less/lazy-eval.less ../tmp/lazy-eval.css',
  228. "cd ..",
  229. // Test multiple plugins
  230. 'node tmp/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'
  231. ].join(" && ")
  232. },
  233. "sourcemap-test": {
  234. // quoted value doesn't seem to get picked up by time-grunt, or isn't output, at least; maybe just "sourcemap" is fine?
  235. command: [
  236. "node tmp/lessc --source-map=test/sourcemaps/maps/import-map.map test/less/import.less test/sourcemaps/import.css",
  237. "node tmp/lessc --source-map test/less/sourcemaps/basic.less test/sourcemaps/basic.css"
  238. ].join(" && ")
  239. }
  240. },
  241. eslint: {
  242. target: [
  243. "test/**/*.js",
  244. "lib/less*/**/*.js",
  245. "!test/less/errors/plugin/plugin-error.js"
  246. ],
  247. options: {
  248. configFile: ".eslintrc.json",
  249. fix: true
  250. }
  251. },
  252. connect: {
  253. server: {
  254. options: {
  255. port: 8081
  256. }
  257. }
  258. },
  259. jasmine: {
  260. options: {
  261. keepRunner: true,
  262. host: "http://localhost:8081/",
  263. polyfills: [
  264. "./node_modules/phantomjs-polyfill-object-assign/object-assign-polyfill.js",
  265. "test/browser/vendor/promise.js"
  266. ],
  267. vendor: [
  268. "test/browser/vendor/jasmine-jsreporter.js",
  269. "test/browser/common.js",
  270. "test/browser/less.min.js"
  271. ],
  272. template: "test/browser/test-runner-template.tmpl"
  273. },
  274. main: {
  275. // src is used to build list of less files to compile
  276. src: [
  277. "test/less/*.less",
  278. "!test/less/plugin-preeval.less", // uses ES6 syntax
  279. // Don't test NPM import, obviously
  280. "!test/less/plugin-module.less",
  281. "!test/less/import-module.less",
  282. "!test/less/javascript.less",
  283. "!test/less/urls.less",
  284. "!test/less/empty.less"
  285. ],
  286. options: {
  287. helpers: "test/browser/runner-main-options.js",
  288. specs: "test/browser/runner-main-spec.js",
  289. outfile: "tmp/browser/test-runner-main.html"
  290. }
  291. },
  292. legacy: {
  293. src: ["test/less/legacy/*.less"],
  294. options: {
  295. helpers: "test/browser/runner-legacy-options.js",
  296. specs: "test/browser/runner-legacy-spec.js",
  297. outfile: "tmp/browser/test-runner-legacy.html"
  298. }
  299. },
  300. strictUnits: {
  301. src: ["test/less/strict-units/*.less"],
  302. options: {
  303. helpers: "test/browser/runner-strict-units-options.js",
  304. specs: "test/browser/runner-strict-units-spec.js",
  305. outfile: "tmp/browser/test-runner-strict-units.html"
  306. }
  307. },
  308. errors: {
  309. src: [
  310. "test/less/errors/*.less",
  311. "!test/less/errors/javascript-error.less",
  312. "test/browser/less/errors/*.less"
  313. ],
  314. options: {
  315. timeout: 20000,
  316. helpers: "test/browser/runner-errors-options.js",
  317. specs: "test/browser/runner-errors-spec.js",
  318. outfile: "tmp/browser/test-runner-errors.html"
  319. }
  320. },
  321. noJsErrors: {
  322. src: ["test/less/no-js-errors/*.less"],
  323. options: {
  324. helpers: "test/browser/runner-no-js-errors-options.js",
  325. specs: "test/browser/runner-no-js-errors-spec.js",
  326. outfile: "tmp/browser/test-runner-no-js-errors.html"
  327. }
  328. },
  329. browser: {
  330. src: [
  331. "test/browser/less/*.less",
  332. "test/browser/less/plugin/*.less"
  333. ],
  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:
  376. "test/browser/runner-rootpath-rewrite-urls-options.js",
  377. specs: "test/browser/runner-rootpath-rewrite-urls-spec.js",
  378. outfile:
  379. "tmp/browser/test-runner-rootpath-rewrite-urls.html"
  380. }
  381. },
  382. production: {
  383. src: ["test/browser/less/production/*.less"],
  384. options: {
  385. helpers: "test/browser/runner-production-options.js",
  386. specs: "test/browser/runner-production-spec.js",
  387. outfile: "tmp/browser/test-runner-production.html"
  388. }
  389. },
  390. modifyVars: {
  391. src: ["test/browser/less/modify-vars/*.less"],
  392. options: {
  393. helpers: "test/browser/runner-modify-vars-options.js",
  394. specs: "test/browser/runner-modify-vars-spec.js",
  395. outfile: "tmp/browser/test-runner-modify-vars.html"
  396. }
  397. },
  398. globalVars: {
  399. src: ["test/browser/less/global-vars/*.less"],
  400. options: {
  401. helpers: "test/browser/runner-global-vars-options.js",
  402. specs: "test/browser/runner-global-vars-spec.js",
  403. outfile: "tmp/browser/test-runner-global-vars.html"
  404. }
  405. },
  406. postProcessorPlugin: {
  407. src: ["test/less/postProcessorPlugin/*.less"],
  408. options: {
  409. helpers: [
  410. "test/plugins/postprocess/index.js",
  411. "test/browser/runner-postProcessorPlugin-options.js"
  412. ],
  413. specs: "test/browser/runner-postProcessorPlugin.js",
  414. outfile:
  415. "tmp/browser/test-runner-post-processor-plugin.html"
  416. }
  417. },
  418. preProcessorPlugin: {
  419. src: ["test/less/preProcessorPlugin/*.less"],
  420. options: {
  421. helpers: [
  422. "test/plugins/preprocess/index.js",
  423. "test/browser/runner-preProcessorPlugin-options.js"
  424. ],
  425. specs: "test/browser/runner-preProcessorPlugin.js",
  426. outfile: "tmp/browser/test-runner-pre-processor-plugin.html"
  427. }
  428. },
  429. visitorPlugin: {
  430. src: ["test/less/visitorPlugin/*.less"],
  431. options: {
  432. helpers: [
  433. "test/plugins/visitor/index.js",
  434. "test/browser/runner-VisitorPlugin-options.js"
  435. ],
  436. specs: "test/browser/runner-VisitorPlugin.js",
  437. outfile: "tmp/browser/test-runner-visitor-plugin.html"
  438. }
  439. },
  440. filemanagerPlugin: {
  441. src: ["test/less/filemanagerPlugin/*.less"],
  442. options: {
  443. helpers: [
  444. "test/plugins/filemanager/index.js",
  445. "test/browser/runner-filemanagerPlugin-options.js"
  446. ],
  447. specs: "test/browser/runner-filemanagerPlugin.js",
  448. outfile: "tmp/browser/test-runner-filemanager-plugin.html"
  449. }
  450. }
  451. },
  452. "saucelabs-jasmine": sauceJobs,
  453. // Clean the version of less built for the tests
  454. clean: {
  455. test: ["test/browser/less.js", "tmp", "test/less-bom"],
  456. "sourcemap-test": [
  457. "test/sourcemaps/*.css",
  458. "test/sourcemaps/*.map"
  459. ],
  460. sauce_log: ["sc_*.log"]
  461. }
  462. });
  463. // Load these plugins to provide the necessary tasks
  464. grunt.loadNpmTasks("grunt-saucelabs");
  465. require("jit-grunt")(grunt);
  466. // by default, run tests
  467. grunt.registerTask("default", ["test"]);
  468. // Release
  469. grunt.registerTask("dist", [
  470. "shell:build"
  471. ]);
  472. // Create the browser version of less.js
  473. grunt.registerTask("browsertest-lessjs", [
  474. "shell:testbuild"
  475. ]);
  476. // Run all browser tests
  477. grunt.registerTask("browsertest", [
  478. "browsertest-lessjs",
  479. "connect",
  480. "jasmine"
  481. ]);
  482. // setup a web server to run the browser tests in a browser rather than phantom
  483. grunt.registerTask("browsertest-server", [
  484. "browsertest-lessjs",
  485. "jasmine::build",
  486. "connect::keepalive"
  487. ]);
  488. var previous_force_state = grunt.option("force");
  489. grunt.registerTask("force", function(set) {
  490. if (set === "on") {
  491. grunt.option("force", true);
  492. } else if (set === "off") {
  493. grunt.option("force", false);
  494. } else if (set === "restore") {
  495. grunt.option("force", previous_force_state);
  496. }
  497. });
  498. grunt.registerTask("sauce", [
  499. "browsertest-lessjs",
  500. "jasmine::build",
  501. "connect",
  502. "sauce-after-setup"
  503. ]);
  504. grunt.registerTask("sauce-after-setup", [
  505. "saucelabs-jasmine:all",
  506. "clean:sauce_log"
  507. ]);
  508. var testTasks = [
  509. "clean",
  510. "eslint",
  511. "shell:testbuild",
  512. "shell:test",
  513. "shell:opts",
  514. "shell:plugin",
  515. "connect",
  516. "jasmine"
  517. ];
  518. if (
  519. isNaN(Number(process.env.TRAVIS_PULL_REQUEST, 10)) &&
  520. Number(process.env.TRAVIS_NODE_VERSION) === 4 &&
  521. (process.env.TRAVIS_BRANCH === "master" ||
  522. process.env.TRAVIS_BRANCH === "3.x")
  523. ) {
  524. testTasks.push("force:on");
  525. testTasks.push("sauce-after-setup");
  526. testTasks.push("force:off");
  527. }
  528. // Run all tests
  529. grunt.registerTask("test", testTasks);
  530. // Run shell option tests (includes deprecated options)
  531. grunt.registerTask("shell-options", ["shell:opts"]);
  532. // Run shell plugin test
  533. grunt.registerTask("shell-plugin", ["shell:plugin"]);
  534. // Run all tests except browsertest
  535. grunt.registerTask("quicktest", testTasks.slice(0, -1));
  536. // generate a good test environment for testing sourcemaps
  537. grunt.registerTask("sourcemap-test", [
  538. "clean:sourcemap-test",
  539. "shell:build:lessc",
  540. "shell:sourcemap-test",
  541. "connect::keepalive"
  542. ]);
  543. // Run benchmark
  544. grunt.registerTask("benchmark", ["shell:benchmark"]);
  545. };