ci.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. const path = require("path"),
  2. fs = require('fs'),
  3. port = 5555,
  4. colors = {
  5. "passed" : "\x1B[32m",
  6. "failed": "\x1B[31m",
  7. "pending": "\x1B[33m",
  8. "excluded": "\x1B[0m",
  9. "none": "\x1B[0m"
  10. },
  11. symbols = {
  12. "passed" : ".",
  13. "failed": "F",
  14. "pending": "*",
  15. "excluded": "",
  16. "none": ""
  17. },
  18. host = `http://localhost:${port}`,
  19. useSauce = process.env.USE_SAUCE === 'true';
  20. let driver, server;
  21. function pageGenerator() {
  22. const ejs = require("ejs"),
  23. fg = require("fast-glob"),
  24. templatePath = path.resolve(__dirname, 'spec/support/index.html.ejs'),
  25. template = ejs.compile(fs.readFileSync(templatePath).toString()),
  26. patterns = [
  27. "lib/jasmine-core/jasmine.js",
  28. "lib/jasmine-core/json2.js",
  29. "lib/jasmine-core/jasmine-html.js",
  30. "lib/jasmine-core/boot.js",
  31. "src/core/requireCore.js",
  32. "src/core/base.js",
  33. "src/core/util.js",
  34. "src/core/Spec.js",
  35. "src/core/Env.js",
  36. "src/**/*.js",
  37. "spec/helpers/*.js",
  38. "spec/**/*[Ss]pec.js"
  39. ],
  40. ignore = [
  41. "spec/helpers/nodeDefineJasmineUnderTest.js",
  42. "spec/npmPackage/**/*",
  43. "lib/jasmine-core/node_boot.js"
  44. ];
  45. return function toHtml() {
  46. const files = fg.sync(patterns, {ignore});
  47. return template({files});
  48. }
  49. }
  50. function buildWebdriver() {
  51. const webdriver = require("selenium-webdriver"),
  52. Capability = webdriver.Capability;
  53. if (useSauce) {
  54. const username = process.env['SAUCE_USERNAME'],
  55. accessKey = process.env['SAUCE_ACCESS_KEY'];
  56. return new webdriver.Builder()
  57. .withCapabilities({
  58. name: `jasmine-core ${new Date().toISOString()}`,
  59. [Capability.PLATFORM]: process.env['SAUCE_OS'],
  60. [Capability.BROWSER_NAME]: process.env['JASMINE_BROWSER'],
  61. [Capability.VERSION]: process.env['SAUCE_BROWSER_VERSION'],
  62. build: `Core ${process.env['TRAVIS_BUILD_NUMBER'] || 'Ran locally'}`,
  63. tags: ['Jasmine-Core'],
  64. "tunnel-identifier": process.env['TRAVIS_JOB_NUMBER'] ? process.env['TRAVIS_JOB_NUMBER'].toString() : null
  65. })
  66. .usingServer(`http://${username}:${accessKey}@localhost:4445/wd/hub`)
  67. .build();
  68. } else {
  69. return new webdriver.Builder()
  70. .forBrowser(process.env["JASMINE_BROWSER"] || "firefox")
  71. .build();
  72. }
  73. }
  74. async function resultsWithoutCircularReferences(driver, resultType, index, batchSize) {
  75. return await driver.executeScript(
  76. `var results = jsApiReporter.${resultType}Results(${index}, ${batchSize});\n` +
  77. 'for (var i = 0; i < results.length; i++) {\n' +
  78. 'var expectations = results[i].failedExpectations;\n' +
  79. 'if (results[i].passedExpectations) {\n' +
  80. 'expectations = expectations.concat(results[i].passedExpectations);\n' +
  81. '}\n' +
  82. 'for (var j = 0; j < expectations.length; j++) {\n' +
  83. 'var expectation = expectations[j];\n' +
  84. "try { JSON.stringify(expectation.expected); } catch (e) { expectation.expected = '<circular expected>'; }\n" +
  85. "try { JSON.stringify(expectation.actual); } catch (e) { expectation.actual = '<circular actual>'; }\n" +
  86. '}\n' +
  87. '}\n' +
  88. 'return results;'
  89. );
  90. }
  91. function flatten(arr) {
  92. return Array.prototype.concat.apply([], arr);
  93. }
  94. async function getResults(driver) {
  95. const batchSize = 50,
  96. specResults = [],
  97. failedSuiteResults = [];
  98. let index = 0,
  99. slice = [];
  100. do {
  101. slice = await resultsWithoutCircularReferences(driver, 'spec', index, batchSize);
  102. specResults.push(slice);
  103. index += batchSize;
  104. } while (slice.length === batchSize);
  105. index = 0;
  106. do {
  107. slice = await resultsWithoutCircularReferences(driver, 'suite', index, batchSize);
  108. failedSuiteResults.push(slice.filter(function(suite) { return suite.status === 'failed' }));
  109. index += batchSize;
  110. } while (slice.length === batchSize);
  111. return {specResults: flatten(specResults), failedSuiteResults: flatten(failedSuiteResults)};
  112. }
  113. (async function () {
  114. await new Promise(resolve => {
  115. console.log("Creating an express app for browers to run the tests...")
  116. const express = require("express"),
  117. app = express(),
  118. html = pageGenerator();
  119. app.use(express.static(__dirname));
  120. app.get("/", (req, res) => res.send(html()));
  121. server = app.listen(port, resolve);
  122. });
  123. console.log("Running the tests in browser...")
  124. driver = buildWebdriver();
  125. await driver.get(`${host}/?throwFailures=false&failFast=false&random=true`)
  126. await new Promise(resolve => {
  127. const intervalId = setInterval(async () => {
  128. const isFinished = await driver.executeScript("return jsApiReporter && jsApiReporter.finished")
  129. if (isFinished) {
  130. clearInterval(intervalId)
  131. resolve();
  132. }
  133. }, 500)
  134. });
  135. const {specResults, failedSuiteResults} = await getResults(driver);
  136. console.log(specResults.map(spec => `${colors[spec.status]}${symbols[spec.status]}`).join("") + colors["none"]);
  137. const result = specResults.reduce((result, spec) => {
  138. result[spec.status] = [...result[spec.status], spec];
  139. return result;
  140. }, {pending: [], failed: [], passed: [], excluded: []});
  141. if (result.pending.length) {
  142. console.log(`${colors["pending"]}Pending:`);
  143. result.pending.forEach((spec, index) => {
  144. console.log(`${colors["none"]}${index}) ${spec.fullName}`)
  145. console.group();
  146. console.log(`${colors["pending"]}${spec.pendingReason || "no reason given"}`);
  147. console.groupEnd();
  148. console.log();
  149. });
  150. }
  151. if (result.failed.length) {
  152. console.log(`${colors["failed"]}Failed:`);
  153. result["failed"].forEach((spec, index) => {
  154. console.log(`${colors["none"]}${index}) ${spec.fullName}`)
  155. console.group();
  156. spec.failedExpectations.forEach((expect) => {
  157. console.log(`${colors["none"]}Message:`);
  158. console.group();
  159. console.log(`${colors["failed"]}${expect.message}`);
  160. console.groupEnd();
  161. console.log(`${colors["none"]}Stack:`);
  162. console.group();
  163. console.log(`${colors["failed"]}${expect.stack}`);
  164. console.groupEnd();
  165. console.groupEnd();
  166. })
  167. console.groupEnd();
  168. console.log();
  169. });
  170. }
  171. const details = await driver.executeScript(`
  172. return {
  173. overallStatus: jsApiReporter.runDetails.overallStatus,
  174. executionTime: jsApiReporter.executionTime(),
  175. random: jsApiReporter.runDetails.order.random,
  176. seed: jsApiReporter.runDetails.order.seed
  177. }`);
  178. console.log(`${colors["none"]}${specResults.length} spec(s), ${result.failed.length} failure(s), ${result.pending.length} pending spec(s)`);
  179. console.log(`Finished in ${details.executionTime / 1000} second(s)`);
  180. console.log(`Randomized with seed ${details.seed} ( ${host}/?random=${details.random}&seed=${details.seed} )`);
  181. process.exitCode = details.overallStatus === 'passed' ? 0 : 1;
  182. if (useSauce) {
  183. driver.executeScript(`sauce:job-result=${process.exitCode === 0}`);
  184. }
  185. })().finally(() => {
  186. return Promise.all([driver.close(), new Promise(resolve => server.close(resolve))]);
  187. });