aboutsummaryrefslogtreecommitdiff
path: root/packages/create-astro/test/create-astro.test.js
blob: 4335f77e1fc1204fdb8bc0074ccae89228c8cfe5 (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
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import http from 'http';
import { suite } from 'uvu';
import execa from 'execa';
import del from 'del';
import glob from 'tiny-glob';
import * as assert from 'uvu/assert';
import { TEMPLATES } from '../dist/templates.js';

// config
const GITHUB_SHA = process.env.GITHUB_SHA || execa.sync('git', ['rev-parse', 'HEAD']).stdout; // process.env.GITHUB_SHA will be set in CI; if testing locally execa() will gather this
const MAX_TEST_TIME = 60000; // maximum time a test may take (60s)
const TIMER = {}; // keep track of every test’s run time (uvu requires manual setup for this)
const FIXTURES_DIR = path.join(fileURLToPath(path.dirname(import.meta.url)), 'fixtures');

// helper
async function fetch(url) {
  return new Promise((resolve) => {
    http
      .get(url, (res) => {
        let body = '';
        res.on('data', (chunk) => {
          body += chunk;
        });
        res.on('end', () => resolve({ statusCode: res.statusCode, body }));
      })
      .on('error', (err) => {
        reject(err);
      });
  });
}

// test
const CreateAstro = suite('npm init astro');

CreateAstro.before(async () => {
  // clean install dir
  await del(FIXTURES_DIR);
  await fs.promises.mkdir(FIXTURES_DIR);

  // install all templates & deps before running tests
  await Promise.all(
    TEMPLATES.map(async ({ value: template }) => {
      const templateDir = path.join(FIXTURES_DIR, template);
      await execa('../../create-astro.mjs', [templateDir, '--template', template, '--commit', GITHUB_SHA, '--force-overwrite'], {
        cwd: FIXTURES_DIR,
      });
      await execa('yarn', ['--frozen-lockfile', '--silent'], { cwd: templateDir });
    })
  );
});

// enforce MAX_TEST_TIME
CreateAstro.before.each(({ __test__ }) => {
  if (TIMER[__test__]) throw new Error(`Test "${__test__}" already declared`);
  TIMER[__test__] = setTimeout(() => {
    throw new Error(`"${__test__}" did not finish within allowed time`);
  }, MAX_TEST_TIME);
});
CreateAstro.after.each(({ __test__ }) => {
  clearTimeout(TIMER[__test__]);
});

for (let n = 0; n < TEMPLATES.length; n++) {
  const template = TEMPLATES[n].value;
  const templateDir = path.join(FIXTURES_DIR, template);

  CreateAstro(`${template} (install)`, async () => {
    const DOES_HAVE = ['.gitignore', 'package.json', 'public', 'src'];
    const DOES_NOT_HAVE = ['.git', 'meta.json'];

    // test: template contains essential files & folders
    for (const file of DOES_HAVE) {
      assert.ok(fs.existsSync(path.join(templateDir, file)), `missing ${file}`);
    }

    // test: template DOES NOT contain files supposed to be stripped away
    for (const file of DOES_NOT_HAVE) {
      assert.not.ok(fs.existsSync(path.join(templateDir, file)), `failed to clean up ${file}`);
    }
  });

  CreateAstro(`${template} (dev)`, async () => {
    // start dev server
    const port = 3000 + n; // start new port per test
    const devServer = execa('yarn', ['start', '--port', port], { cwd: templateDir });
    await new Promise((resolve) => {
      setTimeout(() => resolve(), 15000);
    }); // give dev server flat 15s to set up
    // TODO: try to ping dev server ASAP rather than waiting flat 15s

    // ping dev server
    const { statusCode, body } = await fetch(`http://localhost:${port}`);

    // expect 200 to be returned with some response
    assert.equal(statusCode, 200, 'didn’t respond with 200');
    assert.ok(body, 'returned empty response');

    // clean up
    devServer.kill();
  });

  CreateAstro(`${template} (build)`, async () => {
    const MUST_HAVE_FILES = ['index.html', '_astro'];

    // build template
    await execa('yarn', ['build'], { cwd: templateDir });

    // scan build dir
    const builtFiles = await glob('**/*', { cwd: path.join(templateDir, 'dist') });
    for (const file of MUST_HAVE_FILES) {
      assert.ok(builtFiles.includes(file), `didn’t build ${file}`);
    }
  });
}

// run tests
CreateAstro.run();