mirror of
https://github.com/withastro/astro.git
synced 2024-12-23 21:53:55 -05:00
7c49194ca2
* wip: add prompt for directory with validation
* feat: wire up dir response to cwd
* feat: improve error handling on non-empty dirs
* fix: update test helpers to execaSync
* chore: add .skipped to old tests for clarity
* deps: add mocha and chai to create-astro
* feat: add directory step test with fixture
* feat: update turbo to run create-astro tests again 🥳
* chore: changeset
139 lines
4.1 KiB
Text
139 lines
4.1 KiB
Text
import fs from 'fs';
|
|
import path from 'path';
|
|
import http from 'http';
|
|
import { green, red } from 'kleur/colors';
|
|
import { execa } from 'execa';
|
|
import glob from 'tiny-glob';
|
|
import { TEMPLATES } from '../dist/templates.js';
|
|
import { GITHUB_SHA, FIXTURES_DIR } from './helpers.js';
|
|
|
|
// helpers
|
|
async function fetch(url) {
|
|
return new Promise((resolve, reject) => {
|
|
http
|
|
.get(url, (res) => {
|
|
// not OK
|
|
if (res.statusCode !== 200) {
|
|
reject(res.statusCode);
|
|
return;
|
|
}
|
|
|
|
// OK
|
|
let body = '';
|
|
res.on('data', (chunk) => {
|
|
body += chunk;
|
|
});
|
|
res.on('end', () => resolve({ statusCode: res.statusCode, body }));
|
|
})
|
|
.on('error', (err) => {
|
|
// other error
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
|
|
function assert(a, b, message) {
|
|
if (a !== b) throw new Error(red(`✘ ${message}`));
|
|
}
|
|
|
|
async function testTemplate(template) {
|
|
const templateDir = path.join(FIXTURES_DIR, template);
|
|
|
|
// test 1: install
|
|
const DOES_HAVE = ['.gitignore', 'package.json', 'public', 'src'];
|
|
const DOES_NOT_HAVE = ['.git', 'meta.json'];
|
|
|
|
// test 1a: expect template contains essential files & folders
|
|
for (const file of DOES_HAVE) {
|
|
assert(fs.existsSync(path.join(templateDir, file)), true, `[${template}] has ${file}`);
|
|
}
|
|
// test 1b: expect template DOES NOT contain files supposed to be stripped away
|
|
for (const file of DOES_NOT_HAVE) {
|
|
assert(fs.existsSync(path.join(templateDir, file)), false, `[${template}] cleaned up ${file}`);
|
|
}
|
|
|
|
// test 2: build
|
|
const MUST_HAVE_FILES = ['index.html', '_astro'];
|
|
await execa('npm', ['run', 'build'], { cwd: templateDir });
|
|
const builtFiles = await glob('**/*', { cwd: path.join(templateDir, 'dist') });
|
|
// test 2a: expect all files built successfully
|
|
for (const file of MUST_HAVE_FILES) {
|
|
assert(builtFiles.includes(file), true, `[${template}] built ${file}`);
|
|
}
|
|
|
|
// test 3: dev server (should happen after build so dependency install can be reused)
|
|
|
|
// TODO: fix dev server test in CI
|
|
if (process.env.CI === true) {
|
|
return;
|
|
}
|
|
|
|
// start dev server in background & wait until ready
|
|
const templateIndex = TEMPLATES.findIndex(({ value }) => value === template);
|
|
const port = 3000 + templateIndex; // use different port per-template
|
|
const devServer = execa('npm', ['run', 'start', '--', '--port', port], { cwd: templateDir });
|
|
let sigkill = setTimeout(() => {
|
|
throw new Error(`Dev server failed to start`); // if 10s has gone by with no update, kill process
|
|
}, 10000);
|
|
|
|
// read stdout until "Server started" appears
|
|
await new Promise((resolve, reject) => {
|
|
devServer.stdout.on('data', (data) => {
|
|
clearTimeout(sigkill);
|
|
sigkill = setTimeout(() => {
|
|
reject(`Dev server failed to start`);
|
|
}, 10000);
|
|
if (data.toString('utf8').includes('Server started')) resolve();
|
|
});
|
|
devServer.stderr.on('data', (data) => {
|
|
reject(data.toString('utf8'));
|
|
});
|
|
});
|
|
clearTimeout(sigkill); // done!
|
|
|
|
// send request to dev server that should be ready
|
|
const { statusCode, body } = (await fetch(`http://localhost:${port}`)) || {};
|
|
|
|
// test 3a: expect 200 status code
|
|
assert(statusCode, 200, `[${template}] 200 response`);
|
|
// test 3b: expect non-empty response
|
|
assert(body.length > 0, true, `[${template}] non-empty response`);
|
|
|
|
// clean up
|
|
devServer.kill();
|
|
}
|
|
|
|
async function testAll() {
|
|
// setup
|
|
await Promise.all(
|
|
TEMPLATES.map(async ({ value: template }) => {
|
|
// setup: `npm init astro`
|
|
await execa(
|
|
'../../create-astro.mjs',
|
|
[template, '--template', template, '--commit', GITHUB_SHA, '--force-overwrite'],
|
|
{
|
|
cwd: FIXTURES_DIR,
|
|
}
|
|
);
|
|
// setup: `pnpm install` (note: running multiple `pnpm`s in parallel in CI will conflict)
|
|
await execa('pnpm', ['install', '--no-package-lock', '--silent'], {
|
|
cwd: path.join(FIXTURES_DIR, template),
|
|
});
|
|
})
|
|
);
|
|
|
|
// test (note: not parallelized because Snowpack HMR reuses same port in dev)
|
|
for (let n = 0; n < TEMPLATES.length; n += 1) {
|
|
const template = TEMPLATES[n].value;
|
|
|
|
try {
|
|
await testTemplate(template);
|
|
} catch (err) {
|
|
console.error(red(`✘ [${template}]`));
|
|
throw err;
|
|
}
|
|
|
|
console.info(green(`✔ [${template}] All tests passed (${n + 1}/${TEMPLATES.length})`));
|
|
}
|
|
}
|
|
testAll();
|