aboutsummaryrefslogtreecommitdiff
path: root/packages/create-astro/test/create-astro.test.js
blob: b856c640b4b2cf976acf350637ebd7bdf4d937f4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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: `npm install` (note: running multiple `yarn`s in parallel in CI will conflict)
			await execa('npm', ['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();