From b214b095f3180d68a2cc003e3f67f448ac825738 Mon Sep 17 00:00:00 2001 From: Jonathan Neal Date: Wed, 22 Dec 2021 14:23:15 -0500 Subject: [PATCH] Improve CLI & CLI Testing (#2245) * Fix astro --version, astro --help * Improve CLI testing * nit: fix for windows * nit: try different async cli tests * fix: core dev * nit: cleanup core * nit: change port for config test * nit: write config differently than project-root * nit: cleanup AstroDevServer properties --- packages/astro/src/cli/index.ts | 38 ++++++++------- packages/astro/src/core/dev/index.ts | 32 ++++++------- packages/astro/src/core/preview/index.ts | 4 +- packages/astro/test/cli.test.js | 47 +++++++++++++++++++ packages/astro/test/config.test.js | 38 +++++++-------- .../fixtures/config-path/config/my-config.mjs | 7 +-- packages/astro/test/test-utils.js | 14 +++--- 7 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 packages/astro/test/cli.test.js diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts index 03c84a711..092ff2198 100644 --- a/packages/astro/src/cli/index.ts +++ b/packages/astro/src/cli/index.ts @@ -64,7 +64,7 @@ function resolveArgs(flags: Arguments): CLIState { /** Display --help flag */ function printHelp() { - console.error(` ${colors.bold('astro')} - Futuristic web development tool. + console.log(` ${colors.bold('astro')} - Futuristic web development tool. ${colors.bold('Commands:')} astro dev Run Astro in development mode. astro build Build a pre-compiled production version of your site. @@ -85,8 +85,11 @@ function printHelp() { /** Display --version flag */ async function printVersion() { - const pkg = JSON.parse(await fs.promises.readFile(new URL('../package.json', import.meta.url), 'utf8')); - console.error(pkg.version); + const pkgURL = new URL('../../package.json', import.meta.url); + const pkg = JSON.parse(await fs.promises.readFile(pkgURL, 'utf8')); + const pkgVersion = pkg.version; + + console.log(pkgVersion); } /** Merge CLI flags & config options (CLI flags take priority) */ @@ -105,11 +108,21 @@ export async function cli(args: string[]) { const options = { ...state.options }; const projectRoot = options.projectRoot || flags._[3]; + switch (state.cmd) { + case 'help': + printHelp(); + return process.exit(0); + case 'version': + await printVersion(); + return process.exit(0); + } + // logLevel let logging: LogOptions = { dest: defaultLogDestination, level: 'info', }; + if (flags.verbose) logging.level = 'debug'; if (flags.silent) logging.level = 'silent'; let config: AstroConfig; @@ -118,7 +131,7 @@ export async function cli(args: string[]) { mergeCLIFlags(config, options); } catch (err) { if (err instanceof z.ZodError) { - console.log(formatConfigError(err)); + console.error(formatConfigError(err)); } else { console.error(colors.red((err as any).toString() || err)); } @@ -126,23 +139,15 @@ export async function cli(args: string[]) { } switch (state.cmd) { - case 'help': { - printHelp(); - process.exit(1); - return; - } - case 'version': { - await printVersion(); - process.exit(0); - return; - } case 'dev': { try { - const server = await devServer(config, { logging }); + await devServer(config, { logging }); + await new Promise(() => {}); // don’t close dev server } catch (err) { throwAndExit(err); } + return; } case 'build': { @@ -156,8 +161,7 @@ export async function cli(args: string[]) { } case 'check': { const ret = await check(config); - process.exit(ret); - return; + return process.exit(ret); } case 'preview': { try { diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts index c29d7b7b8..bf007fb5d 100644 --- a/packages/astro/src/core/dev/index.ts +++ b/packages/astro/src/core/dev/index.ts @@ -57,27 +57,27 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l /** Dev server */ export class AstroDevServer { - app = connect(); - httpServer: http.Server | undefined; + app: connect.Server = connect(); + config: AstroConfig; + devRoot: string; hostname: string; + httpServer: http.Server | undefined; + logging: LogOptions; + manifest: ManifestData; + mostRecentRoute?: RouteData; + origin: string; port: number; - private config: AstroConfig; - private logging: LogOptions; - private manifest: ManifestData; - private mostRecentRoute?: RouteData; - private site: URL | undefined; - private devRoot: string; - private url: URL; - private origin: string; - private routeCache: RouteCache = {}; - private viteServer: vite.ViteDevServer | undefined; + routeCache: RouteCache = {}; + site: URL | undefined; + url: URL; + viteServer: vite.ViteDevServer | undefined; constructor(config: AstroConfig, options: DevOptions) { this.config = config; this.hostname = config.devOptions.hostname || 'localhost'; this.logging = options.logging; this.port = config.devOptions.port; - this.origin = `http://localhost:${this.port}`; + this.origin = `http://${this.hostname}:${this.port}`; this.site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined; this.devRoot = this.site ? this.site.pathname : '/'; this.url = new URL(this.devRoot, this.origin); @@ -204,7 +204,7 @@ export class AstroDevServer { public listen(devStart: number): Promise { let showedPortTakenMsg = false; return new Promise((resolve, reject) => { - const listen = () => { + const appListen = () => { this.httpServer = this.app.listen(this.port, this.hostname, () => { info(this.logging, 'astro', msg.devStart({ startupTime: performance.now() - devStart })); info(this.logging, 'astro', msg.devHost({ host: `http://${this.hostname}:${this.port}${this.devRoot}` })); @@ -220,7 +220,7 @@ export class AstroDevServer { showedPortTakenMsg = true; // only print this once } this.port++; - return listen(); // retry + return appListen(); // retry } else { error(this.logging, 'astro', err.stack); this.httpServer?.removeListener('error', onError); @@ -228,7 +228,7 @@ export class AstroDevServer { } }; - listen(); + appListen(); }); } diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index b39bd009b..f6cfd7ad3 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -38,8 +38,8 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO }).pipe(res); }); - let port = config.devOptions.port; - const { hostname } = config.devOptions; + let { hostname, port } = config.devOptions; + let httpServer: http.Server; /** Expose dev server to `port` */ diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js new file mode 100644 index 000000000..9b758ff02 --- /dev/null +++ b/packages/astro/test/cli.test.js @@ -0,0 +1,47 @@ +import { expect } from 'chai'; +import { cli } from './test-utils.js'; +import { promises as fs } from 'fs'; +import { fileURLToPath } from 'url'; + +describe('astro cli', () => { + it('astro', async () => { + const proc = await cli(); + + expect(proc.stdout).to.include('astro - Futuristic web development tool'); + }); + + it('astro --version', async () => { + const pkgURL = new URL('../package.json', import.meta.url); + const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version); + + const proc = await cli('--version'); + + expect(proc.stdout).to.equal(pkgVersion); + }); + + it('astro dev', async () => { + const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url); + + const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL)); + + let stdout = ''; + + for await (const chunk of proc.stdout) { + stdout += chunk; + + if (chunk.includes('Local:')) break; + } + + proc.kill(); + + expect(stdout).to.include('Server started'); + }); + + it('astro build', async () => { + const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url); + + const proc = await cli('build', '--project-root', fileURLToPath(projectRootURL)); + + expect(proc.stdout).to.include('Done'); + }); +}); diff --git a/packages/astro/test/config.test.js b/packages/astro/test/config.test.js index 021148a11..66e805add 100644 --- a/packages/astro/test/config.test.js +++ b/packages/astro/test/config.test.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import { devCLI, loadFixture } from './test-utils.js'; +import { cli, loadFixture } from './test-utils.js'; +import { fileURLToPath } from 'url'; describe('config', () => { let hostnameFixture; @@ -15,41 +16,40 @@ describe('config', () => { }); it('can be specified via --hostname flag', async () => { - const cwd = './fixtures/config-hostname/'; - const cwdURL = new URL(cwd, import.meta.url); - const args = ['--hostname', '127.0.0.1']; - const proc = devCLI(cwdURL, args); + const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url); + const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--hostname', '127.0.0.1'); - proc.stdout.setEncoding('utf8'); + let stdout = ''; for await (const chunk of proc.stdout) { - if (/Local:/.test(chunk)) { - expect(chunk).to.include('127.0.0.1'); - break; - } + stdout += chunk; + + if (chunk.includes('Local:')) break; } proc.kill(); + + expect(stdout).to.include('127.0.0.1'); }); }); describe('path', () => { it('can be passed via --config', async () => { - const cwd = './fixtures/config-path/'; - const cwdURL = new URL(cwd, import.meta.url); - const configPath = new URL('./config/my-config.mjs', cwdURL).pathname; - const args = ['--config', configPath]; - const proc = devCLI(cwdURL, args); + const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url); + const configFileURL = new URL('./fixtures/config-path/config/my-config.mjs', import.meta.url); + const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--config', configFileURL.pathname); - proc.stdout.setEncoding('utf8'); + let stdout = ''; for await (const chunk of proc.stdout) { - if (/Server started/.test(chunk)) { - break; - } + stdout += chunk; + + if (chunk.includes('Local:')) break; } proc.kill(); + + expect(stdout).to.include('127.0.0.1'); }); }); diff --git a/packages/astro/test/fixtures/config-path/config/my-config.mjs b/packages/astro/test/fixtures/config-path/config/my-config.mjs index 6c6bb575d..e873034e1 100644 --- a/packages/astro/test/fixtures/config-path/config/my-config.mjs +++ b/packages/astro/test/fixtures/config-path/config/my-config.mjs @@ -1,5 +1,6 @@ export default { - renderers: [ - '@astrojs/renderer-preact' - ] + devOptions: { + hostname: '127.0.0.1', + port: 8080, + }, } diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index eeffb8676..e4eb2c336 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -99,11 +99,13 @@ function merge(a, b) { return c; } -const cliURL = new URL('../astro.js', import.meta.url); +const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url)); -/** Start Dev server via CLI */ -export function devCLI(root, additionalArgs = []) { - const args = [cliURL.pathname, 'dev', '--project-root', root.pathname].concat(additionalArgs); - const proc = execa('node', args); - return proc; +/** Returns a process running the Astro CLI. */ +export function cli(/** @type {string[]} */ ...args) { + const spawned = execa('node', [cliPath, ...args]); + + spawned.stdout.setEncoding('utf8'); + + return spawned; }