Skip to content

Commit 8d7be85

Browse files
committed
feat: add baseUrl option
1 parent 408dbbc commit 8d7be85

File tree

4 files changed

+82
-32
lines changed

4 files changed

+82
-32
lines changed

bin/command.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { run } from "../run.js";
1010
import readYAML from "../lib/readYAML.js";
1111
import { createTestServer } from "../createTestServer.js";
1212

13+
const DEFAULT_PORT = 3000;
1314
const pkg = JSON.parse( await readFile( new URL( "../package.json", import.meta.url ) ) );
1415

1516
function parseFlags( flags ) {
@@ -43,6 +44,13 @@ yargs( process.argv.slice( 2 ) )
4344
description: "Path to a YAML configuration file. " +
4445
"Use this to avoid passing options via the command line."
4546
} )
47+
.option( "base-url", {
48+
alias: "u",
49+
type: "string",
50+
description: "Base URL for the test server. " +
51+
"Expected to always start and end with a slash (/). " +
52+
"Defaults to \"/test/\"."
53+
} )
4654
.option( "flag", {
4755
alias: "f",
4856
type: "array",
@@ -60,10 +68,10 @@ yargs( process.argv.slice( 2 ) )
6068
type: "array",
6169
choices: browsers,
6270
description:
63-
"Run tests in a specific browser." +
64-
"Pass multiple browsers by repeating the option." +
65-
"If using BrowserStack, specify browsers using --browserstack.",
66-
default: [ "chrome" ]
71+
"Run tests in a specific browser. " +
72+
"Pass multiple browsers by repeating the option. " +
73+
"If using BrowserStack, specify browsers using --browserstack. " +
74+
"Defaults to Chrome."
6775
} )
6876
.option( "middleware", {
6977
alias: "mw",
@@ -108,7 +116,7 @@ yargs( process.argv.slice( 2 ) )
108116
.option( "verbose", {
109117
alias: "v",
110118
type: "boolean",
111-
description: "Log additional information."
119+
description: "Log additional information, including all test server requests."
112120
} )
113121
.option( "browserstack", {
114122
type: "array",
@@ -143,6 +151,7 @@ yargs( process.argv.slice( 2 ) )
143151
...( argv.isolatedFlag ?? [] )
144152
];
145153
const middleware = await parseMiddleware( config, argv );
154+
146155
return run( { ...config, ...argv, flag, isolatedFlag, middleware } );
147156
}
148157
} )
@@ -156,17 +165,22 @@ yargs( process.argv.slice( 2 ) )
156165
description: "Path to a YAML configuration file. " +
157166
"Use this to avoid passing options via the command line."
158167
} )
168+
.option( "base-url", {
169+
alias: "u",
170+
type: "string",
171+
description: "Base URL for the test server. " +
172+
"Expected to always start and end with a slash (/). " +
173+
"Defaults to \"/test/\"."
174+
} )
159175
.option( "port", {
160176
alias: "p",
161177
type: "number",
162-
description: "Port to listen on.",
163-
default: 3000
178+
description: "Port to listen on. Defaults to 3000."
164179
} )
165180
.option( "quiet", {
166181
alias: "q",
167182
type: "boolean",
168-
description: "Whether to log requests to the console.",
169-
default: true
183+
description: "Whether to log requests to the console. Default: false."
170184
} )
171185
.option( "middleware", {
172186
alias: "mw",
@@ -176,7 +190,7 @@ yargs( process.argv.slice( 2 ) )
176190
"Pass multiple by repeating the option."
177191
} );
178192
},
179-
handler: async( { configFile, quiet, ...argv } ) => {
193+
handler: async( { baseUrl, configFile, quiet, ...argv } ) => {
180194
console.log( "Starting server..." );
181195
const config = await readYAML( configFile );
182196
const middleware = await parseMiddleware( config, argv );
@@ -186,10 +200,11 @@ yargs( process.argv.slice( 2 ) )
186200
* Note: this server does not support middleware.
187201
* To add middleware, use createTestServer directly.
188202
*/
189-
const app = await createTestServer( { middleware, quiet } );
203+
const app = await createTestServer( { baseUrl, middleware, quiet } );
190204

191-
return app.listen( { ...config, ...argv, host: "0.0.0.0" }, function() {
192-
console.log( `Open tests at http://localhost:${ argv.port }/` );
205+
const port = argv.port ?? config.port ?? DEFAULT_PORT;
206+
return app.listen( { port, host: "0.0.0.0" }, function() {
207+
console.log( `Open tests at http://localhost:${ port }/` );
193208
} );
194209
}
195210
} )

createTestServer.js

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,30 @@ import { readFile, stat } from "node:fs/promises";
33
import { createReadStream } from "node:fs";
44
import getRawBody from "raw-body";
55

6-
export async function createTestServer( { report, middleware: userMiddleware = [], quiet } = {} ) {
7-
const indexHTML = await readFile( "./test/index.html", "utf8" );
6+
7+
function urlWithIndex( baseUrl ) {
8+
let url = baseUrl;
9+
if ( !url.startsWith( "/" ) ) {
10+
url = `/${ url }`;
11+
}
12+
if ( !url.endsWith( "/index.html" ) ) {
13+
url = `${ url }/index.html`;
14+
}
15+
return url;
16+
}
17+
18+
export async function createTestServer( {
19+
baseUrl, // Expected to always end in /
20+
report,
21+
middleware: userMiddleware = [],
22+
quiet
23+
} = {} ) {
24+
const urlWithoutSlash = baseUrl.slice( 0, -1 );
25+
const indexUrl = urlWithIndex( urlWithoutSlash );
26+
27+
// Get the index HTML ahead-of-time,
28+
// to which we will add the QUnit listener script.
29+
const indexHTML = await readFile( `.${ indexUrl }`, "utf8" );
830

931
// Support connect-style middleware
1032
const middlewares = [];
@@ -60,17 +82,19 @@ export async function createTestServer( { report, middleware: userMiddleware = [
6082
}
6183

6284
// Redirect home to test page
63-
use( ( req, res, next ) => {
64-
if ( req.parsedUrl.pathname === "/" ) {
65-
res.redirect( "/test/" );
66-
} else {
67-
next();
68-
}
69-
} );
85+
if ( baseUrl !== "/" ) {
86+
use( ( req, res, next ) => {
87+
if ( req.parsedUrl.pathname === "/" ) {
88+
res.redirect( baseUrl );
89+
} else {
90+
next();
91+
}
92+
} );
93+
}
7094

7195
// Redirect to trailing slash
7296
use( ( req, res, next ) => {
73-
if ( req.parsedUrl.pathname === "/test" ) {
97+
if ( req.parsedUrl.pathname === urlWithoutSlash ) {
7498
res.redirect( 308, `${ req.parsedUrl.pathname }/${ req.parsedUrl.search }` );
7599
} else {
76100
next();
@@ -81,8 +105,8 @@ export async function createTestServer( { report, middleware: userMiddleware = [
81105
use( ( req, res, next ) => {
82106
if (
83107
( req.method === "GET" || req.method === "HEAD" ) &&
84-
( req.parsedUrl.pathname === "/test/" ||
85-
req.parsedUrl.pathname === "/test/index.html" )
108+
( req.parsedUrl.pathname === baseUrl ||
109+
req.parsedUrl.pathname === indexUrl )
86110
) {
87111
res.writeHead( 200, { "Content-Type": "text/html" } );
88112
res.end(

lib/buildTestUrl.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { generateModuleId } from "./generateHash.js";
22

33
export function buildTestUrl( {
4+
baseUrl,
45
browserstack,
56
flags,
67
isolatedFlag,
@@ -36,5 +37,5 @@ export function buildTestUrl( {
3637
// BrowserStack supplies a custom domain for local testing,
3738
// which is especially necessary for iOS testing.
3839
const host = browserstack ? "bs-local.com" : "localhost";
39-
return `http://${ host }:${ port }/test/?${ query }`;
40+
return `http://${ host }:${ port }${ baseUrl }?${ query }`;
4041
}

run.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ import {
1818
} from "./queue.js";
1919

2020
const EXIT_HOOK_WAIT_TIMEOUT = 60 * 1000;
21+
const rendsWithSlash = /\/$/;
2122

2223
export async function run( {
24+
baseUrl = "/test/",
2325
browser: browserNames = [],
2426
browserstack,
2527
concurrency,
@@ -33,7 +35,14 @@ export async function run( {
3335
runId,
3436
verbose
3537
} ) {
36-
console.log( isolatedFlags );
38+
if ( !baseUrl ) {
39+
throw new Error( "No baseUrl specified." );
40+
}
41+
42+
// Ensure baseUrl ends with a slash
43+
if ( !rendsWithSlash.test( baseUrl ) ) {
44+
baseUrl += "/";
45+
}
3746
if ( !browserNames.length ) {
3847
browserNames = [ "chrome" ];
3948
}
@@ -67,10 +76,9 @@ export async function run( {
6776
// hook it up to the reporter
6877
const reports = Object.create( null );
6978
const app = await createTestServer( {
79+
baseUrl,
7080
middleware,
71-
72-
// Hide test server request logs in CLI output
73-
quiet: true,
81+
quiet: !verbose,
7482
report: async( message ) => {
7583
switch ( message.type ) {
7684
case "testEnd": {
@@ -244,6 +252,7 @@ export async function run( {
244252
reports[ reportId ] = { browser, flags, headless, id: reportId, isolatedFlag };
245253

246254
const url = buildTestUrl( {
255+
baseUrl,
247256
browserstack,
248257
flags,
249258
isolatedFlag,
@@ -253,6 +262,7 @@ export async function run( {
253262
} );
254263

255264
const options = {
265+
baseUrl,
256266
browserstack,
257267
concurrency,
258268
debug,
@@ -293,14 +303,14 @@ export async function run( {
293303
for ( const report of Object.values( reports ) ) {
294304
if ( !report.total ) {
295305
stop = true;
296-
const allFlags = [
306+
const reportFlags = [
297307
...report.flags,
298308
...( report.isolatedFlag ? [ report.isolatedFlag ] : [] )
299309
];
300310
console.error(
301311
chalk.red(
302312
`No tests were run for page with flags "${
303-
allFlags.join( "&" )
313+
reportFlags.join( "&" )
304314
}" in ${
305315
getBrowserString( report.browser )
306316
} (${ report.id })`

0 commit comments

Comments
 (0)