diff options
author | 2022-09-22 11:37:01 -0700 | |
---|---|---|
committer | 2022-09-22 14:37:01 -0400 | |
commit | 7481ffda028d9028d8e28bc7c6e9960ab80acf0f (patch) | |
tree | f387438aabad2a3b4483fe1735c87fe71285fac6 /packages/create-astro/test | |
parent | e5f71142eb62bd72456e889dad5774347c3753f2 (diff) | |
download | astro-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.js | 23 | ||||
-rw-r--r-- | packages/create-astro/test/typescript-step.test.js | 117 | ||||
-rw-r--r-- | packages/create-astro/test/utils.js | 26 |
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 = []) { |