Skip to content

Commit 642cb7f

Browse files
committed
feat: use multiple commands to split functionality
1 parent b649844 commit 642cb7f

11 files changed

+387
-244
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# jQuery Test Runner
2+
3+
A test runner built by the jQuery team to run QUnit tests in real browsers using Selenium and BrowserStack.

bin/command.js

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/usr/bin/env node
2+
3+
import yargs from "yargs/yargs";
4+
import { readFile } from "node:fs/promises";
5+
import { resolve } from "node:path";
6+
import { browsers } from "../flags/browsers.js";
7+
import { getPlan, listBrowsers, stopWorkers } from "../browserstack/api.js";
8+
import { buildBrowserFromString } from "../browserstack/buildBrowserFromString.js";
9+
import { modules } from "../flags/modules.js";
10+
import { run } from "../run.js";
11+
import readYAML from "../lib/readYAML.js";
12+
import { createTestServer } from "../createTestServer.js";
13+
14+
const pkg = JSON.parse( await readFile( new URL( "../package.json", import.meta.url ) ) );
15+
16+
async function parseMiddleware( config, argv ) {
17+
const middleware = await Promise.all( [
18+
...( config.middleware ?? [] ),
19+
...( argv.middleware ?? [] )
20+
].map(
21+
async( mw ) => {
22+
const module = await import( resolve( process.cwd(), mw ) );
23+
return module.default;
24+
}
25+
) );
26+
return middleware;
27+
}
28+
29+
yargs( process.argv.slice( 2 ) )
30+
.version( pkg.version )
31+
.command( {
32+
command: "run [options]",
33+
description: "Run unit tests in real browsers using selenium or BrowserStack.",
34+
builder: ( yargs ) => {
35+
yargs.option( "config-file", {
36+
alias: "c",
37+
type: "string",
38+
description: "Path to a YAML configuration file. " +
39+
"Use this to avoid passing options via the command line."
40+
} )
41+
.option( "module", {
42+
alias: "m",
43+
type: "array",
44+
choices: modules,
45+
description:
46+
"Run tests for a specific module. " +
47+
"Pass multiple modules by repeating the option. " +
48+
"Defaults to all modules."
49+
} )
50+
.option( "browser", {
51+
alias: "b",
52+
type: "array",
53+
choices: browsers,
54+
description:
55+
"Run tests in a specific browser." +
56+
"Pass multiple browsers by repeating the option." +
57+
"If using BrowserStack, specify browsers using --browserstack.",
58+
default: [ "chrome" ]
59+
} )
60+
.option( "middleware", {
61+
alias: "mw",
62+
type: "array",
63+
description: "Add middleware to the test server by passing " +
64+
"the path to a module that exports a middleware factory function. " +
65+
"Pass multiple by repeating the option."
66+
} )
67+
.option( "headless", {
68+
alias: "h",
69+
type: "boolean",
70+
description:
71+
"Run tests in headless mode. Cannot be used with --debug or --browserstack.",
72+
conflicts: [ "debug", "browserstack" ]
73+
} )
74+
.option( "esm", {
75+
alias: "esmodules",
76+
type: "boolean",
77+
description: "Run tests using jQuery's source, " +
78+
"which is written with ECMAScript Modules."
79+
} )
80+
.option( "concurrency", {
81+
type: "number",
82+
description:
83+
"Run tests in parallel in multiple browsers. " +
84+
"Defaults to 8 in normal mode. In browserstack mode, " +
85+
"defaults to the maximum available under your BrowserStack plan."
86+
} )
87+
.option( "debug", {
88+
alias: "d",
89+
type: "boolean",
90+
description:
91+
"Leave the browser open for debugging. Cannot be used with --headless.",
92+
conflicts: [ "headless" ]
93+
} )
94+
.option( "retries", {
95+
alias: "r",
96+
type: "number",
97+
description: "Number of times to retry failed tests by refreshing the URL."
98+
} )
99+
.option( "hard-retries", {
100+
type: "number",
101+
description:
102+
"Number of times to retry failed tests by restarting the worker. " +
103+
"This is in addition to the normal retries " +
104+
"and are only used when the normal retries are exhausted."
105+
} )
106+
.option( "verbose", {
107+
alias: "v",
108+
type: "boolean",
109+
description: "Log additional information."
110+
} )
111+
.option( "isolate", {
112+
type: "boolean",
113+
description: "Run each module by itself in the test page. " +
114+
"This can extend testing time."
115+
} )
116+
.option( "browserstack", {
117+
type: "array",
118+
description:
119+
"Run tests in BrowserStack.\n" +
120+
"Requires BROWSERSTACK_USERNAME and " +
121+
"BROWSERSTACK_ACCESS_KEY environment variables.\n" +
122+
"The value can be empty for the default configuration, " +
123+
"or a string in the format of\n" +
124+
"\"browser_[browserVersion | :device]_os_osVersion\" (see --list-browsers).\n" +
125+
"Pass multiple browsers by repeating the option.\n" +
126+
"The --browser option is ignored when --browserstack has a value.\n" +
127+
"Otherwise, the --browser option will be used, " +
128+
"with the latest version/device for that browser, on a matching OS."
129+
} )
130+
.option( "run-id", {
131+
type: "string",
132+
description: "A unique identifier for the run in BrowserStack."
133+
} );
134+
},
135+
handler: async( { configFile, ...argv } ) => {
136+
console.log( "Running tests..." );
137+
const config = await readYAML( configFile );
138+
const middleware = await parseMiddleware( config, argv );
139+
return run( { ...config, ...argv, middleware } );
140+
}
141+
} )
142+
.command( {
143+
command: "serve [options]",
144+
description: "Run a simple server for loading tests in a browser.",
145+
builder: ( yargs ) => {
146+
yargs.option( "config-file", {
147+
alias: "c",
148+
type: "string",
149+
description: "Path to a YAML configuration file. " +
150+
"Use this to avoid passing options via the command line."
151+
} )
152+
.option( "port", {
153+
alias: "p",
154+
type: "number",
155+
description: "Port to listen on.",
156+
default: 3000
157+
} )
158+
.option( "quiet", {
159+
alias: "q",
160+
type: "boolean",
161+
description: "Whether to log requests to the console.",
162+
default: true
163+
} )
164+
.option( "middleware", {
165+
alias: "mw",
166+
type: "array",
167+
description: "Add middleware to the test server by passing " +
168+
"the path to a module that exports a middleware factory function. " +
169+
"Pass multiple by repeating the option."
170+
} );
171+
},
172+
handler: async( { configFile, quiet, ...argv } ) => {
173+
const config = await readYAML( configFile );
174+
const middleware = await parseMiddleware( config, argv );
175+
176+
/**
177+
* Run a simple server for loading tests in a browser.
178+
* Note: this server does not support middleware.
179+
* To add middleware, use createTestServer directly.
180+
*/
181+
const app = await createTestServer( { middleware, quiet } );
182+
183+
return app.listen( { ...config, ...argv, host: "0.0.0.0" }, function() {
184+
console.log( `Open tests at http://localhost:${ argv.port }/` );
185+
} );
186+
}
187+
} )
188+
.command( {
189+
command: "list-browsers [filter]",
190+
description:
191+
"List available BrowserStack browsers and exit.\n" +
192+
"Leave blank to view all browsers or pass " +
193+
"\"browser_[browserVersion | :device]_os_osVersion\" with each parameter " +
194+
"separated by an underscore to filter the list (any can be omitted).\n" +
195+
"\"latest\" can be used in place of \"browserVersion\" to find the latest version.\n" +
196+
"\"latest-n\" can be used to find the nth latest browser version.\n" +
197+
"Use a colon to indicate a device.\n" +
198+
"Examples: \"chrome__windows_10\", \"safari_latest\", " +
199+
"\"Mobile Safari\", \"Android Browser_:Google Pixel 8 Pro\".\n" +
200+
"Use quotes if spaces are necessary.",
201+
builder: ( yargs ) => {
202+
yargs.positional( "filter", {
203+
type: "string",
204+
description: "Filter the list of browsers."
205+
} );
206+
},
207+
handler: ( { filter } ) => {
208+
return listBrowsers( buildBrowserFromString( filter ) );
209+
}
210+
} )
211+
.command( {
212+
command: "stop-workers",
213+
description:
214+
"WARNING: This will stop all BrowserStack workers that may exist and exit," +
215+
"including any workers running from other projects.\n" +
216+
"This can be used as a failsafe when there are too many stray workers.",
217+
handler: () => {
218+
stopWorkers();
219+
}
220+
} )
221+
.command( {
222+
command: "browserstack-plan",
223+
description: "Show BrowserStack plan information and exit.",
224+
handler: async() => {
225+
console.log( await getPlan() );
226+
}
227+
} )
228+
.help()
229+
.parse();

command.js

-134
This file was deleted.

0 commit comments

Comments
 (0)