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();
|