refactor: simpler solution to resolving e2e test fixtures

This commit is contained in:
Tony Sullivan 2022-05-12 14:42:49 -06:00
parent 85172f7d5d
commit 4ae372eeda
2 changed files with 9 additions and 178 deletions

View file

@ -1,39 +1,4 @@
import { execa } from 'execa';
import { polyfill } from '@astrojs/webapi';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { resolveConfig, loadConfig } from '../dist/core/config.js';
import dev from '../dist/core/dev/index.js';
import build from '../dist/core/build/index.js';
import preview from '../dist/core/preview/index.js';
import { nodeLogDestination } from '../dist/core/logger/node.js';
import os from 'os';
import stripAnsi from 'strip-ansi';
// polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
polyfill(globalThis, {
exclude: 'window document',
});
/**
* @typedef {import('node-fetch').Response} Response
* @typedef {import('../src/core/dev/index').DevServer} DevServer
* @typedef {import('../src/@types/astro').AstroConfig} AstroConfig
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
* @typedef {import('../src/core/app/index').App} App
*
*
* @typedef {Object} Fixture
* @property {typeof build} build
* @property {(url: string, opts: any) => Promise<Response>} fetch
* @property {(path: string) => Promise<string>} readFile
* @property {(path: string) => Promise<string[]>} readdir
* @property {() => Promise<DevServer>} startDevServer
* @property {() => Promise<PreviewServer>} preview
* @property {() => Promise<void>} clean
* @property {() => Promise<App>} loadTestAdapterApp
*/
import { loadFixture as baseLoadFixture } from '../test/test-utils.js';
/**
* Load Astro fixture
@ -55,148 +20,14 @@ polyfill(globalThis, {
* Clean-up
* .clean() - Async. Removes the projects dist folder.
*/
export async function loadFixture(inlineConfig) {
export function loadFixture(inlineConfig) {
if (!inlineConfig || !inlineConfig.root)
throw new Error("Must provide { root: './fixtures/...' }");
// load config
let cwd = inlineConfig.root;
delete inlineConfig.root;
if (typeof cwd === 'string') {
try {
cwd = new URL(cwd.replace(/\/?$/, '/'));
} catch (err1) {
cwd = new URL(cwd.replace(/\/?$/, '/'), import.meta.url);
}
}
// Load the config.
let config = await loadConfig({ cwd: fileURLToPath(cwd) });
config = merge(config, { ...inlineConfig, root: cwd });
// Note: the inline config doesn't run through config validation where these normalizations usually occur
if (typeof inlineConfig.site === 'string') {
config.site = new URL(inlineConfig.site);
}
if (inlineConfig.base && !inlineConfig.base.endsWith('/')) {
config.base = inlineConfig.base + '/';
}
/** @type {import('../src/core/logger/core').LogOptions} */
const logging = {
dest: nodeLogDestination,
level: 'error',
};
/** @type {import('@astrojs/telemetry').AstroTelemetry} */
const telemetry = {
record() {
return Promise.resolve();
},
};
return {
build: (opts = {}) => build(config, { mode: 'development', logging, telemetry, ...opts }),
startDevServer: async (opts = {}) => {
const devResult = await dev(config, { logging, telemetry, ...opts });
config.server.port = devResult.address.port; // update port
return devResult;
},
config,
fetch: (url, init) =>
fetch(`http://${'127.0.0.1'}:${config.server.port}${url.replace(/^\/?/, '/')}`, init),
preview: async (opts = {}) => {
const previewServer = await preview(config, { logging, telemetry, ...opts });
return previewServer;
},
readFile: (filePath) =>
fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.outDir), 'utf8'),
readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.outDir)),
clean: () => fs.promises.rm(config.outDir, { maxRetries: 10, recursive: true, force: true }),
loadTestAdapterApp: async () => {
const url = new URL('./server/entry.mjs', config.outDir);
const { createApp } = await import(url);
return createApp();
},
};
// resolve the relative root (i.e. "./fixtures/tailwindcss") to a full filepath
// without this, the main `loadFixture` helper will resolve relative to `packages/astro/test`
return baseLoadFixture({
...inlineConfig,
root: new URL(inlineConfig.root, import.meta.url).toString()
})
}
/**
* Basic object merge utility. Returns new copy of merged Object.
* @param {Object} a
* @param {Object} b
* @returns {Object}
*/
function merge(a, b) {
const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
const c = {};
for (const k of allKeys) {
const needsObjectMerge =
typeof a[k] === 'object' &&
typeof b[k] === 'object' &&
(Object.keys(a[k]).length || Object.keys(b[k]).length) &&
!Array.isArray(a[k]) &&
!Array.isArray(b[k]);
if (needsObjectMerge) {
c[k] = merge(a[k] || {}, b[k] || {});
continue;
}
c[k] = a[k];
if (b[k] !== undefined) c[k] = b[k];
}
return c;
}
const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url));
/** 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;
}
export async function parseCliDevStart(proc) {
let stdout = '';
let stderr = '';
for await (const chunk of proc.stdout) {
stdout += chunk;
if (chunk.includes('Local')) break;
}
if (!stdout) {
for await (const chunk of proc.stderr) {
stderr += chunk;
break;
}
}
proc.kill();
stdout = stripAnsi(stdout);
stderr = stripAnsi(stderr);
if (stderr) {
throw new Error(stderr);
}
const messages = stdout
.split('\n')
.filter((ln) => !!ln.trim())
.map((ln) => ln.replace(/[🚀┃]/g, '').replace(/\s+/g, ' ').trim());
return { messages };
}
export async function cliServerLogSetup(flags = [], cmd = 'dev') {
const proc = cli(cmd, ...flags);
const { messages } = await parseCliDevStart(proc);
const local = messages.find((msg) => msg.includes('Local'))?.replace(/Local\s*/g, '');
const network = messages.find((msg) => msg.includes('Network'))?.replace(/Network\s*/g, '');
return { local, network };
}
export const isWindows = os.platform() === 'win32';

View file

@ -659,7 +659,7 @@ importers:
chai-as-promised: 7.1.1_chai@4.3.6
mocha: 9.2.2
packages/astro/e2e/fixtures/basic:
packages/astro/e2e/fixtures/tailwindcss:
specifiers:
'@astrojs/tailwind': workspace:*
astro: workspace:*