diff --git a/packages/astro-rss/tsconfig.test.json b/packages/astro-rss/tsconfig.test.json index c94db9d8553c..7d6bc4428b35 100644 --- a/packages/astro-rss/tsconfig.test.json +++ b/packages/astro-rss/tsconfig.test.json @@ -3,7 +3,6 @@ "include": ["test/**/*.ts"], "exclude": ["test/fixtures/**"], "compilerOptions": { - "noEmit": true, "allowJs": true, "noUnusedLocals": false, "noUnusedParameters": false, diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json index 1f43630eaf1e..596eeed7417b 100644 --- a/packages/create-astro/package.json +++ b/packages/create-astro/package.json @@ -22,7 +22,8 @@ "build": "astro-scripts build \"src/index.ts\" --bundle && tsc", "build:ci": "astro-scripts build \"src/index.ts\" --bundle", "dev": "astro-scripts dev \"src/**/*.ts\"", - "test": "astro-scripts test \"test/**/*.test.js\"" + "test": "astro-scripts test \"test/**/*.test.ts\"", + "typecheck:tests": "tsc --build tsconfig.test.json" }, "files": [ "dist", diff --git a/packages/create-astro/test/context.test.js b/packages/create-astro/test/context.test.ts similarity index 83% rename from packages/create-astro/test/context.test.js rename to packages/create-astro/test/context.test.ts index 7836d4410914..fb689a2f47b5 100644 --- a/packages/create-astro/test/context.test.js +++ b/packages/create-astro/test/context.test.ts @@ -73,14 +73,20 @@ describe('context', () => { }); it('--add with --no-install conflicts', async () => { - const exitCode = await new Promise((resolve) => { - const originalExit = process.exit; - process.exit = (code) => { - process.exit = originalExit; - resolve(code); - }; - getContext(['--add', 'cloudflare', '--no-install']); - }); + const originalExit = process.exit; + let exitCode: number | string | null | undefined; + const patchedExit: typeof originalExit = (code) => { + exitCode = code; + throw code; + }; + process.exit = patchedExit; + try { + await getContext(['--add', 'cloudflare', '--no-install']); + } catch { + // expected: patchedExit throws to unwind getContext + } finally { + process.exit = originalExit; + } assert.equal(exitCode, 1); }); }); diff --git a/packages/create-astro/test/dependencies.test.js b/packages/create-astro/test/dependencies.test.ts similarity index 73% rename from packages/create-astro/test/dependencies.test.js rename to packages/create-astro/test/dependencies.test.ts index bde585130b37..5285abba6bcd 100644 --- a/packages/create-astro/test/dependencies.test.js +++ b/packages/create-astro/test/dependencies.test.ts @@ -1,18 +1,19 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { dependencies } from '../dist/index.js'; -import { setup } from './utils.js'; +import { type DependenciesContext, mockPrompt, setup } from './utils.ts'; describe('dependencies', () => { const fixture = setup(); it('--yes', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), + tasks: [], }; await dependencies(context); @@ -21,13 +22,14 @@ describe('dependencies', () => { }); it('--yes with third-party template warns', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, template: 'github:someone/starter', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), + tasks: [], }; await dependencies(context); @@ -36,13 +38,14 @@ describe('dependencies', () => { }); it('starlight templates do not warn', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, template: 'starlight/tailwind', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), + tasks: [], }; await dependencies(context); @@ -51,13 +54,14 @@ describe('dependencies', () => { }); it('starlight-prefixed third-party templates warn', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, template: 'starlightevil/foo', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), + tasks: [], }; await dependencies(context); @@ -66,13 +70,14 @@ describe('dependencies', () => { }); it('warns without --yes when install is enabled', async () => { - const context = { + const context: DependenciesContext = { cwd: '', install: true, template: 'github:someone/starter', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), + tasks: [], }; await dependencies(context); @@ -81,12 +86,13 @@ describe('dependencies', () => { }); it('prompt yes', async () => { - const context = { + const context: DependenciesContext = { cwd: '', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: true }), + prompt: mockPrompt({ deps: true }), install: undefined, + tasks: [], }; await dependencies(context); @@ -96,12 +102,13 @@ describe('dependencies', () => { }); it('prompt no', async () => { - const context = { + const context: DependenciesContext = { cwd: '', packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: false }), + prompt: mockPrompt({ deps: false }), install: undefined, + tasks: [], }; await dependencies(context); @@ -111,12 +118,13 @@ describe('dependencies', () => { }); it('--install', async () => { - const context = { + const context: DependenciesContext = { cwd: '', install: true, packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: false }), + prompt: mockPrompt({ deps: false }), + tasks: [], }; await dependencies(context); assert.ok(fixture.hasMessage('Skipping dependency installation')); @@ -124,12 +132,13 @@ describe('dependencies', () => { }); it('--no-install ', async () => { - const context = { + const context: DependenciesContext = { cwd: '', install: false, packageManager: 'npm', dryRun: true, - prompt: () => ({ deps: false }), + prompt: mockPrompt({ deps: false }), + tasks: [], }; await dependencies(context); @@ -140,17 +149,20 @@ describe('dependencies', () => { describe('--add', async () => { it('fails for non-supported integration', async () => { - let context = { + let context: DependenciesContext = { cwd: '', add: ['foo '], dryRun: true, - prompt: () => ({ deps: false }), + prompt: mockPrompt({ deps: false }), + packageManager: 'npm', + tasks: [], }; try { await dependencies(context); assert.fail('The function should throw an error'); } catch (error) { + assert.ok(error instanceof Error); assert.ok( error.message.includes('Invalid package name "foo "'), `Expected error about invalid package name, got: ${error.message}`, @@ -160,13 +172,16 @@ describe('dependencies', () => { cwd: '', add: ['react', 'bar lorem'], dryRun: true, - prompt: () => ({ deps: false }), + prompt: mockPrompt({ deps: false }), + packageManager: 'npm', + tasks: [], }; try { await dependencies(context); assert.fail('The function should throw an error'); } catch (error) { + assert.ok(error instanceof Error); assert.ok( error.message.includes('Invalid package name "bar lorem"'), `Expected error about invalid package name, got: ${error.message}`, diff --git a/packages/create-astro/test/git.test.js b/packages/create-astro/test/git.test.ts similarity index 70% rename from packages/create-astro/test/git.test.js rename to packages/create-astro/test/git.test.ts index 85854f0d59b4..f2ceb6dfb727 100644 --- a/packages/create-astro/test/git.test.js +++ b/packages/create-astro/test/git.test.ts @@ -4,26 +4,41 @@ import { mkdir, writeFile } from 'node:fs/promises'; import { after, before, describe, it } from 'node:test'; import { git } from '../dist/index.js'; -import { setup } from './utils.js'; +import { type GitContext, mockPrompt, setup } from './utils.ts'; describe('git', () => { const fixture = setup(); it('none', async () => { - const context = { cwd: '', dryRun: true, prompt: () => ({ git: false }) }; + const context: GitContext = { + cwd: '', + dryRun: true, + prompt: mockPrompt({ git: false }), + tasks: [], + }; await git(context); assert.ok(fixture.hasMessage('Skipping Git initialization')); }); it('yes (--dry-run)', async () => { - const context = { cwd: '', dryRun: true, prompt: () => ({ git: true }) }; + const context: GitContext = { + cwd: '', + dryRun: true, + prompt: mockPrompt({ git: true }), + tasks: [], + }; await git(context); assert.ok(fixture.hasMessage('Skipping Git initialization')); }); it('no (--dry-run)', async () => { - const context = { cwd: '', dryRun: true, prompt: () => ({ git: false }) }; + const context: GitContext = { + cwd: '', + dryRun: true, + prompt: mockPrompt({ git: false }), + tasks: [], + }; await git(context); assert.ok(fixture.hasMessage('Skipping Git initialization')); @@ -40,11 +55,12 @@ describe('git initialized', () => { }); it('already initialized', async () => { - const context = { + const context: GitContext = { git: true, cwd: './test/fixtures/not-empty', dryRun: false, - prompt: () => ({ git: false }), + prompt: mockPrompt({ git: false }), + tasks: [], }; await git(context); diff --git a/packages/create-astro/test/integrations.test.js b/packages/create-astro/test/integrations.test.ts similarity index 78% rename from packages/create-astro/test/integrations.test.js rename to packages/create-astro/test/integrations.test.ts index 4db46965ea65..a1ccf434dfa8 100644 --- a/packages/create-astro/test/integrations.test.js +++ b/packages/create-astro/test/integrations.test.ts @@ -1,18 +1,20 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { dependencies } from '../dist/index.js'; -import { setup } from './utils.js'; +import { type DependenciesContext, mockPrompt, setup } from './utils.ts'; describe('integrations', () => { const fixture = setup(); it('--add node', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['node'], + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); @@ -21,12 +23,14 @@ describe('integrations', () => { }); it('--add node --add react', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['node', 'react'], + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); @@ -37,12 +41,14 @@ describe('integrations', () => { }); it('--add node,react', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['node,react'], + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); @@ -53,11 +59,13 @@ describe('integrations', () => { }); it('-y', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); assert.ok(fixture.hasMessage('--dry-run Skipping dependency installation')); @@ -65,12 +73,14 @@ describe('integrations', () => { describe('Security: Command injection protection', () => { it('blocks semicolon command injection', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react;whoami'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -81,12 +91,14 @@ describe('integrations', () => { }); it('blocks command substitution with $()', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react$(whoami)'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -97,12 +109,14 @@ describe('integrations', () => { }); it('blocks command substitution with backticks', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react`whoami`'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -113,12 +127,14 @@ describe('integrations', () => { }); it('blocks pipe operators', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react|whoami'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -129,12 +145,14 @@ describe('integrations', () => { }); it('blocks ampersand operators', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react&&whoami'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -145,12 +163,14 @@ describe('integrations', () => { }); it('blocks redirect operators', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['react>file'], + prompt: mockPrompt({}), + tasks: [], }; await assert.rejects( @@ -161,12 +181,14 @@ describe('integrations', () => { }); it('allows scoped packages', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['@astrojs/tailwind'], + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); @@ -178,12 +200,14 @@ describe('integrations', () => { }); it('allows valid package names', async () => { - const context = { + const context: DependenciesContext = { cwd: '', yes: true, packageManager: 'npm', dryRun: true, add: ['my-package', 'package_2.0'], + prompt: mockPrompt({}), + tasks: [], }; await dependencies(context); diff --git a/packages/create-astro/test/intro.test.js b/packages/create-astro/test/intro.test.ts similarity index 57% rename from packages/create-astro/test/intro.test.js rename to packages/create-astro/test/intro.test.ts index d042dad7fc6b..8e0da3755927 100644 --- a/packages/create-astro/test/intro.test.js +++ b/packages/create-astro/test/intro.test.ts @@ -1,18 +1,28 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { intro } from '../dist/index.js'; -import { setup } from './utils.js'; +import { type IntroContext, setup } from './utils.ts'; describe('intro', () => { const fixture = setup(); it('no arguments', async () => { - await intro({ skipHouston: false, version: '0.0.0', username: 'user' }); + const context: IntroContext = { + skipHouston: false, + version: Promise.resolve('0.0.0'), + username: Promise.resolve('user'), + }; + await intro(context); assert.ok(fixture.hasMessage('Houston:')); assert.ok(fixture.hasMessage('Welcome to astro v0.0.0')); }); it('--skip-houston', async () => { - await intro({ skipHouston: true, version: '0.0.0', username: 'user' }); + const context: IntroContext = { + skipHouston: true, + version: Promise.resolve('0.0.0'), + username: Promise.resolve('user'), + }; + await intro(context); assert.equal(fixture.length(), 1); assert.ok(!fixture.hasMessage('Houston:')); assert.ok(fixture.hasMessage('Launch sequence initiated')); diff --git a/packages/create-astro/test/next.test.js b/packages/create-astro/test/next.test.ts similarity index 59% rename from packages/create-astro/test/next.test.js rename to packages/create-astro/test/next.test.ts index 5b9b22b30632..1f948b78e214 100644 --- a/packages/create-astro/test/next.test.js +++ b/packages/create-astro/test/next.test.ts @@ -1,20 +1,30 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { next } from '../dist/index.js'; -import { setup } from './utils.js'; +import { type NextContext, setup } from './utils.ts'; describe('next steps', () => { const fixture = setup(); it('no arguments', async () => { - await next({ skipHouston: false, cwd: './it/fixtures/not-empty', packageManager: 'npm' }); + const context: NextContext = { + skipHouston: false, + cwd: './it/fixtures/not-empty', + packageManager: 'npm', + }; + await next(context); assert.ok(fixture.hasMessage('Liftoff confirmed.')); assert.ok(fixture.hasMessage('npm run dev')); assert.ok(fixture.hasMessage('Good luck out there, astronaut!')); }); it('--skip-houston', async () => { - await next({ skipHouston: true, cwd: './it/fixtures/not-empty', packageManager: 'npm' }); + const context: NextContext = { + skipHouston: true, + cwd: './it/fixtures/not-empty', + packageManager: 'npm', + }; + await next(context); assert.ok(!fixture.hasMessage('Good luck out there, astronaut!')); }); }); diff --git a/packages/create-astro/test/package-name-validation.test.js b/packages/create-astro/test/package-name-validation.test.ts similarity index 100% rename from packages/create-astro/test/package-name-validation.test.js rename to packages/create-astro/test/package-name-validation.test.ts diff --git a/packages/create-astro/test/project-name.test.js b/packages/create-astro/test/project-name.test.ts similarity index 58% rename from packages/create-astro/test/project-name.test.js rename to packages/create-astro/test/project-name.test.ts index 0aebd1c79268..f4de3aef34bd 100644 --- a/packages/create-astro/test/project-name.test.js +++ b/packages/create-astro/test/project-name.test.ts @@ -1,37 +1,53 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { projectName } from '../dist/index.js'; -import { setup } from './utils.js'; +import { mockExit, mockPrompt, type ProjectNameContext, setup } from './utils.ts'; describe('project name', async () => { const fixture = setup(); it('pass in name', async () => { - const context = { projectName: '', cwd: './foo/bar/baz', prompt: () => {} }; + const context: ProjectNameContext = { + projectName: '', + cwd: './foo/bar/baz', + prompt: mockPrompt({}), + exit: mockExit, + }; await projectName(context); assert.equal(context.cwd, './foo/bar/baz'); assert.equal(context.projectName, 'baz'); }); it('dot', async () => { - const context = { projectName: '', cwd: '.', prompt: () => ({ name: 'foobar' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '.', + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, + }; await projectName(context); assert.ok(fixture.hasMessage('"." is not empty!')); assert.equal(context.projectName, 'foobar'); }); it('dot slash', async () => { - const context = { projectName: '', cwd: './', prompt: () => ({ name: 'foobar' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: './', + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, + }; await projectName(context); assert.ok(fixture.hasMessage('"./" is not empty!')); assert.equal(context.projectName, 'foobar'); }); it('empty', async () => { - const context = { + const context: ProjectNameContext = { projectName: '', cwd: './test/fixtures/empty', - prompt: () => ({ name: 'foobar' }), + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, }; await projectName(context); assert.ok(!fixture.hasMessage('"./test/fixtures/empty" is not empty!')); @@ -39,10 +55,11 @@ describe('project name', async () => { }); it('not empty', async () => { - const context = { + const context: ProjectNameContext = { projectName: '', cwd: './test/fixtures/not-empty', - prompt: () => ({ name: 'foobar' }), + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, }; await projectName(context); assert.ok(fixture.hasMessage('"./test/fixtures/not-empty" is not empty!')); @@ -50,73 +67,107 @@ describe('project name', async () => { }); it('basic', async () => { - const context = { projectName: '', cwd: '', prompt: () => ({ name: 'foobar' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '', + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, + }; await projectName(context); assert.equal(context.cwd, 'foobar'); assert.equal(context.projectName, 'foobar'); }); it('head and tail blank spaces should be trimmed', async () => { - const context = { projectName: '', cwd: '', prompt: () => ({ name: ' foobar ' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '', + prompt: mockPrompt({ name: ' foobar ' }), + exit: mockExit, + }; await projectName(context); assert.equal(context.cwd, 'foobar'); assert.equal(context.projectName, 'foobar'); }); it('normalize', async () => { - const context = { projectName: '', cwd: '', prompt: () => ({ name: 'Invalid Name' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '', + prompt: mockPrompt({ name: 'Invalid Name' }), + exit: mockExit, + }; await projectName(context); assert.equal(context.cwd, 'Invalid Name'); assert.equal(context.projectName, 'invalid-name'); }); it('remove leading/trailing dashes', async () => { - const context = { projectName: '', cwd: '', prompt: () => ({ name: '(invalid)' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '', + prompt: mockPrompt({ name: '(invalid)' }), + exit: mockExit, + }; await projectName(context); assert.equal(context.projectName, 'invalid'); }); it('handles scoped packages', async () => { - const context = { projectName: '', cwd: '', prompt: () => ({ name: '@astro/site' }) }; + const context: ProjectNameContext = { + projectName: '', + cwd: '', + prompt: mockPrompt({ name: '@astro/site' }), + exit: mockExit, + }; await projectName(context); assert.equal(context.cwd, '@astro/site'); assert.equal(context.projectName, '@astro/site'); }); it('--yes', async () => { - const context = { projectName: '', cwd: './foo/bar/baz', yes: true, prompt: () => {} }; + const context: ProjectNameContext = { + projectName: '', + cwd: './foo/bar/baz', + yes: true, + prompt: mockPrompt({}), + exit: mockExit, + }; await projectName(context); assert.equal(context.projectName, 'baz'); }); it('dry run with name', async () => { - const context = { + const context: ProjectNameContext = { projectName: '', cwd: './foo/bar/baz', dryRun: true, - prompt: () => {}, + prompt: mockPrompt({}), + exit: mockExit, }; await projectName(context); assert.equal(context.projectName, 'baz'); }); it('dry run with dot', async () => { - const context = { + const context: ProjectNameContext = { projectName: '', cwd: '.', dryRun: true, - prompt: () => ({ name: 'foobar' }), + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, }; await projectName(context); assert.equal(context.projectName, 'foobar'); }); it('dry run with empty', async () => { - const context = { + const context: ProjectNameContext = { projectName: '', cwd: './test/fixtures/empty', dryRun: true, - prompt: () => ({ name: 'foobar' }), + prompt: mockPrompt({ name: 'foobar' }), + exit: mockExit, }; await projectName(context); assert.equal(context.projectName, 'empty'); diff --git a/packages/create-astro/test/template-processing.test.js b/packages/create-astro/test/template-processing.test.ts similarity index 100% rename from packages/create-astro/test/template-processing.test.js rename to packages/create-astro/test/template-processing.test.ts diff --git a/packages/create-astro/test/template.test.js b/packages/create-astro/test/template.test.ts similarity index 53% rename from packages/create-astro/test/template.test.js rename to packages/create-astro/test/template.test.ts index 821ac9c2e9ac..5f934d5bec6b 100644 --- a/packages/create-astro/test/template.test.js +++ b/packages/create-astro/test/template.test.ts @@ -1,38 +1,69 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { template } from '../dist/index.js'; -import { setup } from './utils.js'; +import { mockExit, mockPrompt, setup, type TemplateContext } from './utils.ts'; describe('template', async () => { const fixture = setup(); it('none', async () => { - const context = { template: '', cwd: '', dryRun: true, prompt: () => ({ template: 'blog' }) }; + const context: TemplateContext = { + template: '', + dryRun: true, + prompt: mockPrompt({ template: 'blog' }), + exit: mockExit, + tasks: [], + }; await template(context); assert.ok(fixture.hasMessage('Skipping template copying')); assert.equal(context.template, 'blog'); }); it('minimal (--dry-run)', async () => { - const context = { template: 'minimal', cwd: '', dryRun: true, prompt: () => {} }; + const context: TemplateContext = { + template: 'minimal', + dryRun: true, + prompt: mockPrompt({}), + exit: mockExit, + tasks: [], + }; await template(context); assert.ok(fixture.hasMessage('Using minimal as project template')); }); it('basics (--dry-run)', async () => { - const context = { template: 'basics', cwd: '', dryRun: true, prompt: () => {} }; + const context: TemplateContext = { + template: 'basics', + dryRun: true, + prompt: mockPrompt({}), + exit: mockExit, + tasks: [], + }; await template(context); assert.ok(fixture.hasMessage('Using basics as project template')); }); it('blog (--dry-run)', async () => { - const context = { template: 'blog', cwd: '', dryRun: true, prompt: () => {} }; + const context: TemplateContext = { + template: 'blog', + dryRun: true, + prompt: mockPrompt({}), + exit: mockExit, + tasks: [], + }; await template(context); assert.ok(fixture.hasMessage('Using blog as project template')); }); it('minimal (--yes)', async () => { - const context = { template: 'minimal', cwd: '', dryRun: true, yes: true, prompt: () => {} }; + const context: TemplateContext = { + template: 'minimal', + dryRun: true, + yes: true, + prompt: mockPrompt({}), + exit: mockExit, + tasks: [], + }; await template(context); assert.ok(fixture.hasMessage('Using minimal as project template')); }); diff --git a/packages/create-astro/test/utils.js b/packages/create-astro/test/utils.js deleted file mode 100644 index 20063ec53255..000000000000 --- a/packages/create-astro/test/utils.js +++ /dev/null @@ -1,32 +0,0 @@ -import { before, beforeEach } from 'node:test'; -import { stripVTControlCharacters } from 'node:util'; -import { setStdout } from '../dist/index.js'; - -export function setup() { - const ctx = { messages: [] }; - before(() => { - setStdout( - Object.assign({}, process.stdout, { - write(buf) { - ctx.messages.push(stripVTControlCharacters(String(buf)).trim()); - return true; - }, - }), - ); - }); - beforeEach(() => { - ctx.messages = []; - }); - - return { - messages() { - return ctx.messages; - }, - length() { - return ctx.messages.length; - }, - hasMessage(content) { - return !!ctx.messages.find((msg) => msg.includes(content)); - }, - }; -} diff --git a/packages/create-astro/test/utils.ts b/packages/create-astro/test/utils.ts new file mode 100644 index 000000000000..8946819a00ef --- /dev/null +++ b/packages/create-astro/test/utils.ts @@ -0,0 +1,62 @@ +import { before, beforeEach } from 'node:test'; +import { stripVTControlCharacters } from 'node:util'; +import { setStdout } from '../dist/index.js'; +import type { Context } from '../src/actions/context.ts'; +import type { + dependencies, + git, + intro, + next, + projectName, + template, + verify, +} from '../dist/index.js'; + +export type { Context }; +export type DependenciesContext = Parameters[0]; +export type GitContext = Parameters[0]; +export type IntroContext = Parameters[0]; +export type NextContext = Parameters[0]; +export type ProjectNameContext = Parameters[0]; +export type TemplateContext = Parameters[0]; +export type VerifyContext = Parameters[0]; + +export function mockPrompt(answers: Record): Context['prompt'] { + const fn = async (q: { name: string }) => { + return { [q.name]: answers[q.name] }; + }; + return fn as unknown as Context['prompt']; +} + +export const mockExit: Context['exit'] = (code) => { + throw code; +}; + +export function setup() { + const ctx: { messages: string[] } = { messages: [] }; + before(() => { + setStdout( + Object.assign({}, process.stdout, { + write(buf: Uint8Array | string) { + ctx.messages.push(stripVTControlCharacters(String(buf)).trim()); + return true; + }, + }), + ); + }); + beforeEach(() => { + ctx.messages = []; + }); + + return { + messages() { + return ctx.messages; + }, + length() { + return ctx.messages.length; + }, + hasMessage(content: string) { + return !!ctx.messages.find((msg) => msg.includes(content)); + }, + }; +} diff --git a/packages/create-astro/test/verify.test.js b/packages/create-astro/test/verify.test.ts similarity index 63% rename from packages/create-astro/test/verify.test.js rename to packages/create-astro/test/verify.test.ts index ff335014517c..a1447e2ab41e 100644 --- a/packages/create-astro/test/verify.test.js +++ b/packages/create-astro/test/verify.test.ts @@ -1,22 +1,24 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { verify } from '../dist/index.js'; -import { setup } from './utils.js'; +import { mockExit, setup, type VerifyContext } from './utils.ts'; describe('verify', async () => { const fixture = setup(); - const exit = (code) => { - throw code; - }; + const baseContext = { + version: Promise.resolve('0.0.0'), + ref: 'latest', + exit: mockExit, + } satisfies Partial; it('basics', async () => { - const context = { template: 'basics', exit }; + const context: VerifyContext = { ...baseContext, template: 'basics' }; await verify(context); assert.equal(fixture.messages().length, 0, 'Did not expect `verify` to log any messages'); }); it('missing', async () => { - const context = { template: 'missing', exit }; + const context: VerifyContext = { ...baseContext, template: 'missing' }; let err = null; try { await verify(context); @@ -28,13 +30,13 @@ describe('verify', async () => { }); it('starlight', async () => { - const context = { template: 'starlight', exit }; + const context: VerifyContext = { ...baseContext, template: 'starlight' }; await verify(context); assert.equal(fixture.messages().length, 0, 'Did not expect `verify` to log any messages'); }); it('starlight/tailwind', async () => { - const context = { template: 'starlight/tailwind', exit }; + const context: VerifyContext = { ...baseContext, template: 'starlight/tailwind' }; await verify(context); assert.equal(fixture.messages().length, 0, 'Did not expect `verify` to log any messages'); }); diff --git a/packages/create-astro/tsconfig.test.json b/packages/create-astro/tsconfig.test.json new file mode 100644 index 000000000000..cff674d824b9 --- /dev/null +++ b/packages/create-astro/tsconfig.test.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["test/**/*.ts"], + "exclude": ["test/fixtures/**"], + "compilerOptions": { + "allowJs": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "rewriteRelativeImportExtensions": true + }, + "references": [ + { + "path": "../astro/tsconfig.test.json" + } + ] +} diff --git a/packages/telemetry/tsconfig.test.json b/packages/telemetry/tsconfig.test.json index c94db9d8553c..7d6bc4428b35 100644 --- a/packages/telemetry/tsconfig.test.json +++ b/packages/telemetry/tsconfig.test.json @@ -3,7 +3,6 @@ "include": ["test/**/*.ts"], "exclude": ["test/fixtures/**"], "compilerOptions": { - "noEmit": true, "allowJs": true, "noUnusedLocals": false, "noUnusedParameters": false, diff --git a/packages/upgrade/tsconfig.test.json b/packages/upgrade/tsconfig.test.json index c94db9d8553c..7d6bc4428b35 100644 --- a/packages/upgrade/tsconfig.test.json +++ b/packages/upgrade/tsconfig.test.json @@ -3,7 +3,6 @@ "include": ["test/**/*.ts"], "exclude": ["test/fixtures/**"], "compilerOptions": { - "noEmit": true, "allowJs": true, "noUnusedLocals": false, "noUnusedParameters": false,