|
4 | 4 | // npm install browser-ui-test |
5 | 5 | // ``` |
6 | 6 |
|
7 | | -const fs = require("fs"); |
8 | 7 | const path = require("path"); |
9 | | -const os = require("os"); |
10 | | -const {Options, runTest} = require("browser-ui-test"); |
11 | | - |
12 | | -function showHelp() { |
13 | | - console.log("docs-rs-gui-js options:"); |
14 | | - console.log(" --file [PATH] : file to run (can be repeated)"); |
15 | | - console.log(" --debug : show extra information about script run"); |
16 | | - console.log(" --show-text : render font in pages"); |
17 | | - console.log(" --no-headless : disable headless mode"); |
18 | | - console.log(" --help : show this message then quit"); |
19 | | - console.log(" --jobs [NUMBER] : number of threads to run tests on"); |
20 | | -} |
21 | | - |
22 | | -function isNumeric(s) { |
23 | | - return /^\d+$/.test(s); |
24 | | -} |
25 | | - |
26 | | -function parseOptions(args) { |
27 | | - const opts = { |
28 | | - "files": [], |
29 | | - "debug": false, |
30 | | - "show_text": false, |
31 | | - "no_headless": false, |
32 | | - "jobs": -1, |
33 | | - }; |
34 | | - const correspondences = { |
35 | | - "--debug": "debug", |
36 | | - "--show-text": "show_text", |
37 | | - "--no-headless": "no_headless", |
38 | | - }; |
39 | | - |
40 | | - for (let i = 0; i < args.length; ++i) { |
41 | | - if (args[i] === "--file" |
42 | | - || args[i] === "--jobs") { |
43 | | - i += 1; |
44 | | - if (i >= args.length) { |
45 | | - console.log("Missing argument after `" + args[i - 1] + "` option."); |
46 | | - return null; |
47 | | - } |
48 | | - if (args[i - 1] === "--jobs") { |
49 | | - if (!isNumeric(args[i])) { |
50 | | - console.log( |
51 | | - "`--jobs` option expects a positive number, found `" + args[i] + "`"); |
52 | | - return null; |
53 | | - } |
54 | | - opts["jobs"] = parseInt(args[i]); |
55 | | - } else if (args[i - 1] !== "--file") { |
56 | | - opts[correspondences[args[i - 1]]] = args[i]; |
57 | | - } else { |
58 | | - opts["files"].push(args[i]); |
59 | | - } |
60 | | - } else if (args[i] === "--help") { |
61 | | - showHelp(); |
62 | | - process.exit(0); |
63 | | - } else if (correspondences[args[i]]) { |
64 | | - opts[correspondences[args[i]]] = true; |
65 | | - } else { |
66 | | - console.log("Unknown option `" + args[i] + "`."); |
67 | | - console.log("Use `--help` to see the list of options"); |
68 | | - return null; |
69 | | - } |
70 | | - } |
71 | | - return opts; |
72 | | -} |
73 | | - |
74 | | -/// Print single char status information without \n |
75 | | -function char_printer(n_tests) { |
76 | | - const max_per_line = 10; |
77 | | - let current = 0; |
78 | | - return { |
79 | | - successful: function() { |
80 | | - current += 1; |
81 | | - if (current % max_per_line === 0) { |
82 | | - process.stdout.write(`. (${current}/${n_tests})${os.EOL}`); |
83 | | - } else { |
84 | | - process.stdout.write("."); |
85 | | - } |
86 | | - }, |
87 | | - erroneous: function() { |
88 | | - current += 1; |
89 | | - if (current % max_per_line === 0) { |
90 | | - process.stderr.write(`F (${current}/${n_tests})${os.EOL}`); |
91 | | - } else { |
92 | | - process.stderr.write("F"); |
93 | | - } |
94 | | - }, |
95 | | - finish: function() { |
96 | | - if (current % max_per_line === 0) { |
97 | | - // Don't output if we are already at a matching line end |
98 | | - console.log(""); |
99 | | - } else { |
100 | | - const spaces = " ".repeat(max_per_line - (current % max_per_line)); |
101 | | - process.stdout.write(`${spaces} (${current}/${n_tests})${os.EOL}${os.EOL}`); |
102 | | - } |
103 | | - }, |
104 | | - }; |
105 | | -} |
106 | | - |
107 | | -/// Sort array by .file_name property |
108 | | -function by_filename(a, b) { |
109 | | - return a.file_name - b.file_name; |
110 | | -} |
| 8 | +const spawn = require("child_process").spawn; |
111 | 9 |
|
112 | 10 | async function main(argv) { |
113 | | - const opts = parseOptions(argv.slice(2)); |
114 | | - if (opts === null) { |
115 | | - process.exit(1); |
116 | | - } |
117 | | - |
118 | | - // Print successful tests too |
119 | | - let debug = false; |
120 | | - // Run tests in sequentially |
121 | | - let headless = true; |
122 | | - const options = new Options(); |
123 | | - try { |
124 | | - // This is more convenient that setting fields one by one. |
125 | | - const args = []; |
126 | | - if (typeof process.env.SERVER_URL !== "undefined") { |
127 | | - args.push("--variable", "DOC_PATH", process.env.SERVER_URL); |
128 | | - } else { |
129 | | - args.push("--variable", "DOC_PATH", "http://127.0.0.1:3000"); |
| 11 | + let server = "http://127.0.0.1:3000"; |
| 12 | + if (typeof process.env.SERVER_URL !== "undefined") { |
| 13 | + server = process.env.SERVER_URL; |
| 14 | + } |
| 15 | + let nodeModulePath = "./node_modules"; |
| 16 | + if (typeof process.env.NODE_MODULE_PATH !== "undefined") { |
| 17 | + nodeModulePath = process.env.NODE_MODULE_PATH; |
| 18 | + } |
| 19 | + await spawn("node", [ |
| 20 | + path.join(nodeModulePath, "browser-ui-test/src/index.js"), |
| 21 | + "--display-format", |
| 22 | + "compact", |
| 23 | + "--variable", |
| 24 | + "DOC_PATH", |
| 25 | + server, |
| 26 | + "--test-folder", |
| 27 | + __dirname, |
| 28 | + ...argv.slice(2), |
| 29 | + ], {stdio: "inherit", stderr: "inherit"}).on("exit", code => { |
| 30 | + if (code !== 0) { |
| 31 | + process.exit(1); |
130 | 32 | } |
131 | | - if (opts["debug"]) { |
132 | | - debug = true; |
133 | | - args.push("--debug"); |
134 | | - } |
135 | | - if (opts["show_text"]) { |
136 | | - args.push("--show-text"); |
137 | | - } |
138 | | - if (opts["no_headless"]) { |
139 | | - args.push("--no-headless"); |
140 | | - headless = false; |
141 | | - } |
142 | | - options.parseArguments(args); |
143 | | - } catch (error) { |
144 | | - console.error(`invalid argument: ${error}`); |
145 | | - process.exit(1); |
146 | | - } |
147 | | - |
148 | | - let failed = false; |
149 | | - let files; |
150 | | - if (opts["files"].length === 0) { |
151 | | - files = fs.readdirSync(__dirname); |
152 | | - } else { |
153 | | - files = opts["files"]; |
154 | | - } |
155 | | - files = files.filter(file => path.extname(file) === ".goml"); |
156 | | - if (files.length === 0) { |
157 | | - console.error("No test selected"); |
158 | | - process.exit(2); |
159 | | - } |
160 | | - files.sort(); |
161 | | - |
162 | | - if (!headless) { |
163 | | - opts["jobs"] = 1; |
164 | | - console.log("`--no-headless` option is active, disabling concurrency for running tests."); |
165 | | - } |
166 | | - let jobs = opts["jobs"]; |
167 | | - |
168 | | - if (opts["jobs"] < 1) { |
169 | | - jobs = files.length; |
170 | | - process.setMaxListeners(files.length + 1); |
171 | | - } else if (headless) { |
172 | | - process.setMaxListeners(opts["jobs"] + 1); |
173 | | - } |
174 | | - console.log(`Running ${files.length} docs.rs GUI (${jobs} concurrently) ...`); |
175 | | - |
176 | | - const tests_queue = []; |
177 | | - const results = { |
178 | | - successful: [], |
179 | | - failed: [], |
180 | | - errored: [], |
181 | | - }; |
182 | | - const status_bar = char_printer(files.length); |
183 | | - for (let i = 0; i < files.length; ++i) { |
184 | | - const file_name = files[i]; |
185 | | - const testPath = path.join(__dirname, file_name); |
186 | | - const callback = runTest(testPath, {"options": options}) |
187 | | - .then(out => { |
188 | | - const [output, nb_failures] = out; |
189 | | - results[nb_failures === 0 ? "successful" : "failed"].push({ |
190 | | - file_name: testPath, |
191 | | - output: output, |
192 | | - }); |
193 | | - if (nb_failures > 0) { |
194 | | - status_bar.erroneous(); |
195 | | - failed = true; |
196 | | - } else { |
197 | | - status_bar.successful(); |
198 | | - } |
199 | | - }) |
200 | | - .catch(err => { |
201 | | - results.errored.push({ |
202 | | - file_name: testPath + file_name, |
203 | | - output: err, |
204 | | - }); |
205 | | - status_bar.erroneous(); |
206 | | - failed = true; |
207 | | - }) |
208 | | - .finally(() => { |
209 | | - // We now remove the promise from the tests_queue. |
210 | | - tests_queue.splice(tests_queue.indexOf(callback), 1); |
211 | | - }); |
212 | | - tests_queue.push(callback); |
213 | | - if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) { |
214 | | - await Promise.race(tests_queue); |
215 | | - } |
216 | | - } |
217 | | - if (tests_queue.length > 0) { |
218 | | - await Promise.all(tests_queue); |
219 | | - } |
220 | | - status_bar.finish(); |
221 | | - |
222 | | - if (debug) { |
223 | | - results.successful.sort(by_filename); |
224 | | - results.successful.forEach(r => { |
225 | | - console.log(r.output); |
226 | | - }); |
227 | | - } |
228 | | - |
229 | | - if (results.failed.length > 0) { |
230 | | - console.log(""); |
231 | | - results.failed.sort(by_filename); |
232 | | - results.failed.forEach(r => { |
233 | | - console.log(r.file_name, r.output); |
234 | | - }); |
235 | | - } |
236 | | - if (results.errored.length > 0) { |
237 | | - console.log(os.EOL); |
238 | | - // print run errors on the bottom so developers see them better |
239 | | - results.errored.sort(by_filename); |
240 | | - results.errored.forEach(r => { |
241 | | - console.error(r.file_name, r.output); |
242 | | - }); |
243 | | - } |
244 | | - |
245 | | - if (failed) { |
246 | | - process.exit(1); |
247 | | - } |
| 33 | + }); |
248 | 34 | } |
249 | 35 |
|
250 | 36 | main(process.argv); |
0 commit comments