diff --git a/packages/astro/test/content-collection-picture-render.test.js b/packages/astro/test/content-collection-picture-render.test.ts similarity index 94% rename from packages/astro/test/content-collection-picture-render.test.js rename to packages/astro/test/content-collection-picture-render.test.ts index 71ec5753de59..af10b1fc8c27 100644 --- a/packages/astro/test/content-collection-picture-render.test.js +++ b/packages/astro/test/content-collection-picture-render.test.ts @@ -1,15 +1,14 @@ import assert from 'node:assert/strict'; import { before, describe, it } from 'node:test'; import * as cheerio from 'cheerio'; -import { loadFixture } from './test-utils.js'; +import { type Fixture, loadFixture } from './test-utils.js'; // Regression test for https://github.com/withastro/astro/issues/16036 // Using the component on a prerendered page combined with render() // on content collection entries caused a TDZ error during build: // "ReferenceError: Cannot access '$$Picture' before initialization" describe('Content collection with Picture component and render()', () => { - /** @type {import("./test-utils.js").Fixture} */ - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ root: './fixtures/content-collection-picture-render/' }); diff --git a/packages/astro/test/content-collection-references.test.js b/packages/astro/test/content-collection-references.test.ts similarity index 83% rename from packages/astro/test/content-collection-references.test.js rename to packages/astro/test/content-collection-references.test.ts index 58fd163ac31e..b4376b5060d7 100644 --- a/packages/astro/test/content-collection-references.test.js +++ b/packages/astro/test/content-collection-references.test.ts @@ -1,11 +1,54 @@ import * as assert from 'node:assert/strict'; import { after, before, describe, it } from 'node:test'; +import type { CheerioAPI } from 'cheerio'; import * as cheerio from 'cheerio'; -import { fixLineEndings, loadFixture } from './test-utils.js'; +import { type DevServer, type Fixture, fixLineEndings, loadFixture } from './test-utils.js'; + +type EntryRef = { id: string; collection: string }; + +type BlogEntry = { + id: string; + collection: string; + body: string; + filePath: string; + digest: string; + data: { + title: string; + banner: EntryRef; + author: EntryRef; + relatedPosts?: EntryRef[]; + }; +}; + +type BannerEntry = { + id: string; + collection: string; + filePath: string; + digest: string; + data: { + alt: string; + src: { src: string; width: number; height: number; format: string }; + }; +}; + +type AuthorEntry = { + id: string; + collection: string; + filePath: string; + digest: string; + data: { name: string; twitter: string }; +}; + +type WelcomeData = { + welcomePost: BlogEntry; + banner: BannerEntry; + author: AuthorEntry; + relatedPosts: BlogEntry[]; +}; describe('Content Collections - references', () => { - let fixture; - let devServer; + let fixture: Fixture; + let devServer: DevServer; before(async () => { fixture = await loadFixture({ root: './fixtures/content-collection-references/' }); }); @@ -30,7 +73,7 @@ describe('Content Collections - references', () => { }); describe(`JSON result`, () => { - let json; + let json: WelcomeData; before(async () => { if (mode === 'prod') { const rawJson = await fixture.readFile('/welcome-data.json'); @@ -113,7 +156,7 @@ describe('Content Collections - references', () => { }); describe(`Render result`, () => { - let $; + let $: CheerioAPI; before(async () => { if (mode === 'prod') { const html = await fixture.readFile('/welcome/index.html'); @@ -128,7 +171,7 @@ describe('Content Collections - references', () => { it('Renders `banner` data', () => { const banner = $('img[data-banner]'); assert.equal(banner.length, 1); - assert.ok(banner.attr('src').includes('the-future')); + assert.ok(banner.attr('src')!.includes('the-future')); assert.equal( banner.attr('alt'), 'Futuristic landscape with chrome buildings and blue skies', diff --git a/packages/astro/test/content-collection-tla-svg.test.js b/packages/astro/test/content-collection-tla-svg.test.ts similarity index 90% rename from packages/astro/test/content-collection-tla-svg.test.js rename to packages/astro/test/content-collection-tla-svg.test.ts index 34c01137da0d..1fadf4eb51bb 100644 --- a/packages/astro/test/content-collection-tla-svg.test.js +++ b/packages/astro/test/content-collection-tla-svg.test.ts @@ -1,14 +1,13 @@ import assert from 'node:assert/strict'; import { before, describe, it } from 'node:test'; import * as cheerio from 'cheerio'; -import { loadFixture } from './test-utils.js'; +import { type Fixture, loadFixture } from './test-utils.js'; // Regression test for https://github.com/withastro/astro/issues/15575 // SVG images in content collection image() fields combined with top-level await // caused a circular module dependency deadlock during build. describe('Content collection with SVG image and TLA', () => { - /** @type {import("./test-utils.js").Fixture} */ - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ root: './fixtures/content-collection-tla-svg/' }); @@ -43,7 +42,7 @@ describe('Content collection with SVG image and TLA', () => { const $svg = $('.inline-svg').first(); assert.ok($svg.length, 'Expected inline SVG element to be rendered'); - assert.equal($svg.prop('tagName').toLowerCase(), 'svg'); + assert.equal($svg.prop('tagName')!.toLowerCase(), 'svg'); }); }); }); diff --git a/packages/astro/test/content-collections-render.test.js b/packages/astro/test/content-collections-render.test.ts similarity index 94% rename from packages/astro/test/content-collections-render.test.js rename to packages/astro/test/content-collections-render.test.ts index 4e25d6250227..ede99bda1e2a 100644 --- a/packages/astro/test/content-collections-render.test.js +++ b/packages/astro/test/content-collections-render.test.ts @@ -2,12 +2,11 @@ import * as assert from 'node:assert/strict'; import { after, before, describe, it } from 'node:test'; import * as cheerio from 'cheerio'; import testAdapter from './test-adapter.js'; -import { loadFixture } from './test-utils.js'; +import { type DevServer, type Fixture, loadFixture } from './test-utils.js'; describe('Content Collections - render()', () => { describe('Build - SSG', () => { - /** @type {import('./test-utils').Fixture} */ - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ @@ -66,8 +65,7 @@ describe('Content Collections - render()', () => { }); describe('Build - SSR', () => { - /** @type {import('./test-utils').Fixture} */ - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ @@ -112,7 +110,7 @@ describe('Content Collections - render()', () => { const html = await response.text(); const $ = cheerio.load(html); - const set = new Set(); + const set = new Set(); $('link[rel=stylesheet]').each((_, linkEl) => { const href = linkEl.attribs.href; @@ -121,7 +119,7 @@ describe('Content Collections - render()', () => { }); $('style').each((_, styleEl) => { - const textContent = styleEl.children[0].data; + const textContent = (styleEl.children[0] as { data: string }).data; assert.equal(set.has(textContent), false); set.add(textContent); }); @@ -151,9 +149,8 @@ describe('Content Collections - render()', () => { }); describe('Dev - SSG', () => { - let devServer; - /** @type {import('./test-utils').Fixture} */ - let fixture; + let devServer: DevServer; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ diff --git a/packages/astro/test/content-collections-type-inference.test.js b/packages/astro/test/content-collections-type-inference.test.ts similarity index 89% rename from packages/astro/test/content-collections-type-inference.test.js rename to packages/astro/test/content-collections-type-inference.test.ts index a8d0916f1342..01b46d41af19 100644 --- a/packages/astro/test/content-collections-type-inference.test.js +++ b/packages/astro/test/content-collections-type-inference.test.ts @@ -1,16 +1,13 @@ -// @ts-check import assert from 'node:assert/strict'; import { execSync } from 'node:child_process'; import * as fs from 'node:fs'; import { before, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { loadFixture } from './test-utils.js'; +import { type Fixture, loadFixture } from './test-utils.js'; describe('Content collection type inference', () => { - /** @type {Awaited>} */ - let fixture; - /** @type {string} */ - let fixtureRoot; + let fixture: Fixture; + let fixtureRoot: string; before(async () => { fixture = await loadFixture({ @@ -71,8 +68,8 @@ describe('Content collection type inference', () => { encoding: 'utf-8', }); } catch (err) { - const stdout = /** @type {{ stdout?: string }} */ (err).stdout ?? ''; - const stderr = /** @type {{ stderr?: string }} */ (err).stderr ?? ''; + const stdout = (err as { stdout?: string }).stdout ?? ''; + const stderr = (err as { stderr?: string }).stderr ?? ''; assert.fail( `TypeScript type-checking failed on fixture.\n` + `This means the content collection type inference is broken.\n\n` + diff --git a/packages/astro/test/content-collections.test.js b/packages/astro/test/content-collections.test.ts similarity index 81% rename from packages/astro/test/content-collections.test.js rename to packages/astro/test/content-collections.test.ts index 9ee4e658087d..f27554cb80e9 100644 --- a/packages/astro/test/content-collections.test.js +++ b/packages/astro/test/content-collections.test.ts @@ -4,18 +4,50 @@ import * as cheerio from 'cheerio'; import * as devalue from 'devalue'; import testAdapter from './test-adapter.js'; import { preventNodeBuiltinDependencyPlugin } from './test-plugins.js'; -import { loadFixture } from './test-utils.js'; +import { type App, type Fixture, loadFixture } from './test-utils.js'; + +type WithSchemaConfigEntry = { + id: string; + data: { title: string; isDraft: boolean; lang: string; publishedAt: Date }; +}; + +type WithCustomSlugsEntry = { id: string; data: { slug: string } }; + +type WithUnionSchemaEntry = + | { id: string; data: { type: 'post'; title: string; description: string } } + | { id: string; data: { type: 'newsletter'; subject: string } }; + +type WithSymlinkedContentEntry = { id: string; data: { title: string } }; + +type WithSymlinkedDataEntry = { + id: string; + data: { alt: string; src: { src: string; width: number; height: number; format: string } }; +}; + +type CollectionsJson = { + withSchemaConfig: WithSchemaConfigEntry[]; + withCustomSlugs: WithCustomSlugsEntry[]; + withUnionSchema: WithUnionSchemaEntry[]; + withSymlinkedContent: WithSymlinkedContentEntry[]; + withSymlinkedData: WithSymlinkedDataEntry[]; +}; + +type EntriesJson = { + oneWithSchemaConfig: WithSchemaConfigEntry; + twoWithCustomSlugs: WithCustomSlugsEntry; + postWithUnionSchema: Extract; +}; describe('Content Collections', () => { describe('Query', () => { - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ root: './fixtures/content-collections/' }); await fixture.build({ force: true }); }); describe('Collection', () => { - let json; + let json: CollectionsJson; before(async () => { const rawJson = await fixture.readFile('/collections.json'); json = devalue.parse(rawJson); @@ -58,14 +90,14 @@ describe('Content Collections', () => { const post = json.withUnionSchema.find((item) => item.id === 'post'); assert.notEqual(post, undefined); - assert.deepEqual(post.data, { + assert.deepEqual(post!.data, { type: 'post', title: 'My Post', description: 'This is my post', }); const newsletter = json.withUnionSchema.find((item) => item.id === 'newsletter'); assert.notEqual(newsletter, undefined); - assert.deepEqual(newsletter.data, { + assert.deepEqual(newsletter!.data, { type: 'newsletter', subject: 'My Newsletter', }); @@ -77,7 +109,7 @@ describe('Content Collections', () => { const ids = json.withSymlinkedContent.map((item) => item.id); assert.deepEqual(ids.sort(), ['first', 'second', 'third'].sort()); assert.equal( - json.withSymlinkedContent.find(({ id }) => id === 'first').data.title, + json.withSymlinkedContent.find(({ id }) => id === 'first')!.data.title, 'First Blog', ); }); @@ -97,7 +129,7 @@ describe('Content Collections', () => { }); describe('Entry', () => { - let json; + let json: EntriesJson; before(async () => { const rawJson = await fixture.readFile('/entries.json'); json = devalue.parse(rawJson); @@ -145,7 +177,7 @@ describe('Content Collections', () => { }); }); - const blogSlugToContents = { + const blogSlugToContents: Record = { 'first-post': { title: 'First post', element: 'blockquote', @@ -169,7 +201,7 @@ describe('Content Collections', () => { }; describe('Static paths integration', () => { - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ root: './fixtures/content-static-paths-integration/' }); @@ -183,7 +215,7 @@ describe('Content Collections', () => { }); it('Renders titles', async () => { - for (const slug in blogSlugToContents) { + for (const slug of Object.keys(blogSlugToContents)) { const post = await fixture.readFile(`/posts/${slug}/index.html`); const $ = cheerio.load(post); assert.equal($('h1').text(), blogSlugToContents[slug].title); @@ -191,7 +223,7 @@ describe('Content Collections', () => { }); it('Renders content', async () => { - for (const slug in blogSlugToContents) { + for (const slug of Object.keys(blogSlugToContents)) { const post = await fixture.readFile(`/posts/${slug}/index.html`); const $ = cheerio.load(post); assert.equal( @@ -205,11 +237,11 @@ describe('Content Collections', () => { describe('With spaces in path', () => { it('Does not throw', async () => { const fixture = await loadFixture({ root: './fixtures/content with spaces in folder name/' }); - let error = null; + let error: string | null = null; try { await fixture.build({ force: true }); } catch (e) { - error = e.message; + error = (e as Error).message; } assert.equal(error, null); }); @@ -219,13 +251,13 @@ describe('Content Collections', () => { const fixture = await loadFixture({ root: './fixtures/content-collections-with-config-mjs/', }); - let error; + let error: string | undefined; try { await fixture.build({ force: true }); } catch (e) { - error = e.message; + error = (e as Error).message; } - assert.ok(error.startsWith('Found legacy content config file in')); + assert.ok(error!.startsWith('Found legacy content config file in')); }); }); @@ -234,13 +266,13 @@ describe('Content Collections', () => { const fixture = await loadFixture({ root: './fixtures/content-collections-empty-md-file/', }); - let error; + let error: string | undefined; try { await fixture.build({ force: true }); } catch (e) { - error = e.message; + error = (e as Error).message; } - assert.match(error, /\*\*title\*\*: Required/); + assert.match(error!, /\*\*title\*\*: Required/); }); }); @@ -249,13 +281,13 @@ describe('Content Collections', () => { const fixture = await loadFixture({ root: './fixtures/content-collections-number-id/', }); - let error; + let error: string | undefined; try { await fixture.build({ force: true }); } catch (e) { - error = e.message; + error = (e as Error).message; } - assert.match(error, /returned an entry with an invalid `id`/); + assert.match(error!, /returned an entry with an invalid `id`/); }); }); @@ -264,11 +296,11 @@ describe('Content Collections', () => { const fixture = await loadFixture({ root: './fixtures/content-collections-empty-dir/', }); - let error; + let error: string | undefined; try { await fixture.build({ force: true }); } catch (e) { - error = e.message; + error = (e as Error).message; } assert.equal(error, undefined); @@ -281,7 +313,7 @@ describe('Content Collections', () => { }); describe('SSR integration', () => { - let app; + let app: App; before(async () => { const fixture = await loadFixture({ @@ -305,7 +337,7 @@ describe('Content Collections', () => { }); it('Renders titles', async () => { - for (const slug in blogSlugToContents) { + for (const slug of Object.keys(blogSlugToContents)) { const request = new Request('http://example.com/posts/' + slug); const response = await app.render(request); const body = await response.text(); @@ -315,7 +347,7 @@ describe('Content Collections', () => { }); it('Renders content', async () => { - for (const slug in blogSlugToContents) { + for (const slug of Object.keys(blogSlugToContents)) { const request = new Request('http://example.com/posts/' + slug); const response = await app.render(request); const body = await response.text(); @@ -337,7 +369,7 @@ describe('Content Collections', () => { }); describe('Base configuration', () => { - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ @@ -349,18 +381,18 @@ describe('Content Collections', () => { it('Includes base in links', async () => { const html = await fixture.readFile('/docs/index.html'); const $ = cheerio.load(html); - assert.equal($('link').attr('href').startsWith('/docs'), true); + assert.equal($('link').attr('href')!.startsWith('/docs'), true); }); it('Includes base in scripts', async () => { const html = await fixture.readFile('/docs/index.html'); const $ = cheerio.load(html); - assert.equal($('script').attr('src').startsWith('/docs'), true); + assert.equal($('script').attr('src')!.startsWith('/docs'), true); }); }); describe('Mutation', () => { - let fixture; + let fixture: Fixture; before(async () => { fixture = await loadFixture({ diff --git a/packages/astro/test/content-frontmatter.test.js b/packages/astro/test/content-frontmatter.test.ts similarity index 86% rename from packages/astro/test/content-frontmatter.test.js rename to packages/astro/test/content-frontmatter.test.ts index 7a4a577d40b9..671cdefc095a 100644 --- a/packages/astro/test/content-frontmatter.test.js +++ b/packages/astro/test/content-frontmatter.test.ts @@ -3,13 +3,13 @@ import fs from 'node:fs'; import path from 'node:path'; import { after, before, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { loadFixture } from './test-utils.js'; +import { type DevServer, type Fixture, loadFixture } from './test-utils.js'; describe('frontmatter (loadFixture)', () => { - let fixture; - let devServer; - let blogPath; - let originalContent; + let fixture: Fixture; + let devServer: DevServer; + let blogPath: string; + let originalContent: string; before(async () => { fixture = await loadFixture({ @@ -41,7 +41,7 @@ describe('frontmatter (loadFixture)', () => { const res2 = await fixture.fetch('/'); assert.equal(res2.status, 200, 'Server should still respond after a content error'); } catch (err) { - assert.fail(err); + assert.fail(err as Error); } }); }); diff --git a/packages/astro/test/content-intellisense.test.js b/packages/astro/test/content-intellisense.test.ts similarity index 89% rename from packages/astro/test/content-intellisense.test.js rename to packages/astro/test/content-intellisense.test.ts index d40545d4d13a..ceb28ddf1f04 100644 --- a/packages/astro/test/content-intellisense.test.js +++ b/packages/astro/test/content-intellisense.test.ts @@ -1,19 +1,25 @@ import assert from 'node:assert/strict'; import { before, describe, it } from 'node:test'; -import { loadFixture } from './test-utils.js'; +import { type Fixture, loadFixture } from './test-utils.js'; + +type IntellisenseManifest = { + collections: { hasSchema: boolean; name: string }[]; + entries: Record; +}; + +type IntellisenseCollections = Record< + string, + Array<{ id: string; data: unknown; filePath: string; collection: string }> +>; describe('Content Intellisense', () => { - /** @type {import("./test-utils.js").Fixture} */ - let fixture; + let fixture: Fixture; - /** @type {string[]} */ - let collectionsDir = []; + let collectionsDir: string[] = []; - /** @type {{collections: {hasSchema: boolean, name: string}[], entries: Record}} */ - let manifest = undefined; + let manifest: IntellisenseManifest; - /** @type {Record>} */ - let collections; + let collections: IntellisenseCollections; before(async () => { fixture = await loadFixture({ root: './fixtures/content-intellisense/' });