summaryrefslogtreecommitdiff
path: root/packages/create-astro/test
diff options
context:
space:
mode:
authorGravatar Michael Rienstra <mrienstra@gmail.com> 2022-09-22 11:37:01 -0700
committerGravatar GitHub <noreply@github.com> 2022-09-22 14:37:01 -0400
commit7481ffda028d9028d8e28bc7c6e9960ab80acf0f (patch)
treef387438aabad2a3b4483fe1735c87fe71285fac6 /packages/create-astro/test
parente5f71142eb62bd72456e889dad5774347c3753f2 (diff)
downloadastro-7481ffda028d9028d8e28bc7c6e9960ab80acf0f.tar.gz
astro-7481ffda028d9028d8e28bc7c6e9960ab80acf0f.tar.zst
astro-7481ffda028d9028d8e28bc7c6e9960ab80acf0f.zip
`create-astro`: always create `tsconfig.json` (#4810)
* `create-astro`: always create `tsconfig.json` Currently, we only make sure `tsconfig.json` exists when `strict` or `strictest` is selected. Both `default` & `optout` are intended to correspond to `base` -- and will do so for all [23 official templates](https://github.com/withastro/astro/tree/main/examples), but not necessarily for third-party templates. The [example command for installing a third-party template](https://github.com/withastro/astro/blob/a800bf7/packages/create-astro/README.md?plain=1#L31-L35) is (rather conveniently for the sake of this PR!) an example of a template without a `tsconfig.json` file, and installing it with the `default` ("Relaxed") Typescript option results in no `tsconfig.json` file, rather than a `tsconfig.json` file containing `{ "extends": "astro/tsconfigs/base" }` as would be expected. This PR addresses this scenario. It also explicitly sets the `tsconfig.json` file to `{ "extends": "astro/tsconfigs/base" }` when `default` (which I renamed to `base`, still presented to the user as "Relaxed") or `optout` is selected (`optout` has always printed a warning about the importance of `tsconfig.json` & `src/env.d.ts` but otherwise behaved identically to `default`). This is necessary in two scenarios: 1. When the `tsconfig.json` file was created by this script. 2. When it either didn't already include `"extends"`, or it extended a different config by default. For example, some third-party templates might default to `strict`, in which case I'm guessing we'd want to respect the user's choice and change that to `base`. * update `del` 6.1.1 --> 7.0.0 * test: prevent excess writes (without this it triggers many times) * test: create-astro typescript prompt * changeset * fix: recursive `mkdirSync` * test: longer timeout for `windows-latest` OS (see if this fixes failing tests) * better glob path creation, don't hardcode `/` * test: longer timeout for windows-latest OS (since I'm about to trigger another CI run by pushing a commit, might as well try this too) * create-astro test: show last CLI output on timeout * drop variable timeout Typescript tests are slower than directory tests, but they are all usually less than 5000 ms. Less complexity, easier to maintain. * DRY new error output * Update lockfile * Sync lockfile with main * Update lockfile Co-authored-by: Princesseuh <princssdev@gmail.com>
Diffstat (limited to 'packages/create-astro/test')
-rw-r--r--packages/create-astro/test/directory-step.test.js23
-rw-r--r--packages/create-astro/test/typescript-step.test.js117
-rw-r--r--packages/create-astro/test/utils.js26
3 files changed, 153 insertions, 13 deletions
diff --git a/packages/create-astro/test/directory-step.test.js b/packages/create-astro/test/directory-step.test.js
index 0031f97fd..26737ca94 100644
--- a/packages/create-astro/test/directory-step.test.js
+++ b/packages/create-astro/test/directory-step.test.js
@@ -1,6 +1,8 @@
import path from 'path';
import { promises, existsSync } from 'fs';
-import { PROMPT_MESSAGES, testDir, setup, promiseWithTimeout, timeout } from './utils.js';
+import {
+ PROMPT_MESSAGES, testDir, setup, promiseWithTimeout, timeout
+} from './utils.js';
const inputs = {
nonEmptyDir: './fixtures/select-directory/nonempty-dir',
@@ -12,9 +14,10 @@ const inputs = {
describe('[create-astro] select directory', function () {
this.timeout(timeout);
it('should prompt for directory when none is provided', function () {
- return promiseWithTimeout((resolve) => {
+ return promiseWithTimeout((resolve, onStdout) => {
const { stdout } = setup();
stdout.on('data', (chunk) => {
+ onStdout(chunk);
if (chunk.includes(PROMPT_MESSAGES.directory)) {
resolve();
}
@@ -22,9 +25,10 @@ describe('[create-astro] select directory', function () {
});
});
it('should NOT proceed on a non-empty directory', function () {
- return promiseWithTimeout((resolve) => {
+ return promiseWithTimeout((resolve, onStdout) => {
const { stdout } = setup([inputs.nonEmptyDir]);
stdout.on('data', (chunk) => {
+ onStdout(chunk);
if (chunk.includes(PROMPT_MESSAGES.directory)) {
resolve();
}
@@ -46,9 +50,10 @@ describe('[create-astro] select directory', function () {
if (!existsSync(resolvedEmptyDirPath)) {
await promises.mkdir(resolvedEmptyDirPath);
}
- return promiseWithTimeout((resolve) => {
+ return promiseWithTimeout((resolve, onStdout) => {
const { stdout } = setup([inputs.emptyDir]);
stdout.on('data', (chunk) => {
+ onStdout(chunk);
if (chunk.includes(PROMPT_MESSAGES.template)) {
resolve();
}
@@ -56,9 +61,10 @@ describe('[create-astro] select directory', function () {
});
});
it('should proceed when directory does not exist', function () {
- return promiseWithTimeout((resolve) => {
+ return promiseWithTimeout((resolve, onStdout) => {
const { stdout } = setup([inputs.nonexistentDir]);
stdout.on('data', (chunk) => {
+ onStdout(chunk);
if (chunk.includes(PROMPT_MESSAGES.template)) {
resolve();
}
@@ -66,14 +72,17 @@ describe('[create-astro] select directory', function () {
});
});
it('should error on bad directory selection in prompt', function () {
- return promiseWithTimeout((resolve) => {
+ return promiseWithTimeout((resolve, onStdout) => {
+ let wrote = false;
const { stdout, stdin } = setup();
stdout.on('data', (chunk) => {
+ onStdout(chunk);
if (chunk.includes('is not empty!')) {
resolve();
}
- if (chunk.includes(PROMPT_MESSAGES.directory)) {
+ if (!wrote && chunk.includes(PROMPT_MESSAGES.directory)) {
stdin.write(`${inputs.nonEmptyDir}\x0D`);
+ wrote = true;
}
});
});
diff --git a/packages/create-astro/test/typescript-step.test.js b/packages/create-astro/test/typescript-step.test.js
new file mode 100644
index 000000000..abec21646
--- /dev/null
+++ b/packages/create-astro/test/typescript-step.test.js
@@ -0,0 +1,117 @@
+import { expect } from 'chai';
+import { deleteSync } from 'del';
+import { existsSync, mkdirSync, readdirSync, readFileSync } from 'fs';
+import path from 'path';
+import {
+ PROMPT_MESSAGES, testDir, setup, promiseWithTimeout, timeout
+} from './utils.js';
+
+const inputs = {
+ emptyDir: './fixtures/select-typescript/empty-dir',
+};
+
+function isEmpty(dirPath) {
+ return !existsSync(dirPath) || readdirSync(dirPath).length === 0;
+}
+
+function ensureEmptyDir() {
+ const dirPath = path.resolve(testDir, inputs.emptyDir);
+ if (!existsSync(dirPath)) {
+ mkdirSync(dirPath, { recursive: true });
+ } else if (!isEmpty(dirPath)) {
+ const globPath = path.resolve(dirPath, '*');
+ deleteSync(globPath, { dot: true });
+ }
+}
+
+function getTsConfig(installDir) {
+ const filePath = path.resolve(testDir, installDir, 'tsconfig.json');
+ return JSON.parse(readFileSync(filePath, 'utf-8'));
+}
+
+describe('[create-astro] select typescript', function () {
+ this.timeout(timeout);
+
+ beforeEach(ensureEmptyDir);
+
+ afterEach(ensureEmptyDir);
+
+ it('should prompt for typescript when none is provided', async function () {
+ return promiseWithTimeout((resolve, onStdout) => {
+ const { stdout } = setup([
+ inputs.emptyDir,
+ '--template', 'minimal',
+ '--install', '0',
+ '--git', '0'
+ ]);
+ stdout.on('data', (chunk) => {
+ onStdout(chunk);
+ if (chunk.includes(PROMPT_MESSAGES.typescript)) {
+ resolve();
+ }
+ });
+ }, () => lastStdout);
+ });
+
+ it('should not prompt for typescript when provided', async function () {
+ return promiseWithTimeout((resolve, onStdout) => {
+ const { stdout } = setup([
+ inputs.emptyDir,
+ '--template', 'minimal',
+ '--install', '0',
+ '--git', '0',
+ '--typescript', 'base'
+ ]);
+ stdout.on('data', (chunk) => {
+ onStdout(chunk);
+ if (chunk.includes(PROMPT_MESSAGES.typescriptSucceed)) {
+ resolve();
+ }
+ });
+ }, () => lastStdout);
+ });
+
+ it('should use "strict" config when specified', async function () {
+ return promiseWithTimeout((resolve, onStdout) => {
+ let wrote = false;
+ const { stdout, stdin } = setup([
+ inputs.emptyDir,
+ '--template', 'minimal',
+ '--install', '0',
+ '--git', '0'
+ ]);
+ stdout.on('data', (chunk) => {
+ onStdout(chunk);
+ if (!wrote && chunk.includes(PROMPT_MESSAGES.typescript)) {
+ stdin.write('\x1B\x5B\x42\x0D');
+ wrote = true;
+ }
+ if (chunk.includes(PROMPT_MESSAGES.typescriptSucceed)) {
+ const tsConfigJson = getTsConfig(inputs.emptyDir);
+ expect(tsConfigJson).to.deep.equal({'extends': 'astro/tsconfigs/strict'});
+ resolve();
+ }
+ });
+ }, () => lastStdout);
+ });
+
+ it('should create tsconfig.json when missing', async function () {
+ return promiseWithTimeout((resolve, onStdout) => {
+ const { stdout } = setup([
+ inputs.emptyDir,
+ '--template', 'cassidoo/shopify-react-astro',
+ '--install', '0',
+ '--git', '0',
+ '--typescript', 'base'
+ ]);
+ stdout.on('data', (chunk) => {
+ onStdout(chunk);
+ if (chunk.includes(PROMPT_MESSAGES.typescriptSucceed)) {
+ const tsConfigJson = getTsConfig(inputs.emptyDir);
+ expect(tsConfigJson).to.deep.equal({'extends': 'astro/tsconfigs/base'});
+ resolve();
+ }
+ });
+ }, () => lastStdout);
+ });
+});
diff --git a/packages/create-astro/test/utils.js b/packages/create-astro/test/utils.js
index b085ef083..9be444b5b 100644
--- a/packages/create-astro/test/utils.js
+++ b/packages/create-astro/test/utils.js
@@ -1,31 +1,45 @@
import { execa } from 'execa';
-import { fileURLToPath } from 'url';
import { dirname } from 'path';
+import stripAnsi from 'strip-ansi';
+import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
export const testDir = dirname(__filename);
export const timeout = 5000;
-const createAstroError = new Error(
- 'Timed out waiting for create-astro to respond with expected output.'
-);
+const timeoutError = function (details) {
+ let errorMsg =
+ 'Timed out waiting for create-astro to respond with expected output.';
+ if (details) {
+ errorMsg += '\nLast output: "' + details + '"';
+ }
+ return new Error(errorMsg);
+}
export function promiseWithTimeout(testFn) {
return new Promise((resolve, reject) => {
+ let lastStdout;
+ function onStdout (chunk) {
+ lastStdout = stripAnsi(chunk.toString()).trim() || lastStdout;
+ }
+
const timeoutEvent = setTimeout(() => {
- reject(createAstroError);
+ reject(timeoutError(lastStdout));
}, timeout);
function resolver() {
clearTimeout(timeoutEvent);
resolve();
}
- testFn(resolver);
+
+ testFn(resolver, onStdout);
});
}
export const PROMPT_MESSAGES = {
directory: 'Where would you like to create your new project?',
template: 'Which template would you like to use?',
+ typescript: 'How would you like to setup TypeScript?',
+ typescriptSucceed: 'Next steps'
};
export function setup(args = []) {