From 64bb796c0fee551b8b2349b5246946f60080565b Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 15 Oct 2024 15:34:42 +0800 Subject: [PATCH] Use real filesystem for unit testing (#12172) --- .gitignore | 3 +- biome.jsonc | 1 + package.json | 3 + packages/astro/package.json | 9 +- .../src/vite-plugin-astro-server/plugin.ts | 3 + .../content-mixed-errors/astro.config.mjs | 4 - .../content-mixed-errors/package.json | 16 -- .../src/content/authors/placeholder.json | 3 - .../src/content/blog/placeholder.md | 3 - .../src/pages/authors.astro | 10 - .../content-mixed-errors/src/pages/blog.astro | 7 - .../test/units/_temp-fixtures/package.json | 8 + .../astro/test/units/config/format.test.js | 20 +- .../content-collections/frontmatter.test.js | 36 +-- packages/astro/test/units/correct-path.js | 70 ------ packages/astro/test/units/dev/base.test.js | 52 ++-- .../collections-mixed-content-errors.test.js | 193 ++++++++------- .../units/dev/collections-renderentry.test.js | 97 ++++---- packages/astro/test/units/dev/dev.test.js | 130 ++-------- .../test/units/dev/head-injection.test.js | 13 +- .../astro/test/units/dev/hydration.test.js | 17 +- packages/astro/test/units/dev/restart.test.js | 161 ++++++------ .../astro/test/units/render/chunk.test.js | 17 +- .../test/units/render/components.test.js | 39 ++- .../test/units/routing/endpoints.test.js | 8 +- .../astro/test/units/routing/manifest.test.js | 229 +++++++----------- .../test/units/routing/route-matching.test.js | 11 +- .../units/routing/route-sanitization.test.js | 9 +- .../test/units/routing/trailing-slash.test.js | 9 +- .../test/units/runtime/endpoints.test.js | 8 +- packages/astro/test/units/teardown.js | 19 ++ packages/astro/test/units/test-utils.js | 109 +-------- .../vite-plugin-astro-server/request.test.js | 53 ++-- .../vite-plugin-astro-server/response.test.js | 9 +- patches/fs-fixture@2.4.0.patch | 24 ++ pnpm-lock.yaml | 103 ++------ pnpm-workspace.yaml | 2 + scripts/cmd/test.js | 10 + 38 files changed, 578 insertions(+), 940 deletions(-) delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/astro.config.mjs delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/package.json delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/src/content/authors/placeholder.json delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/src/content/blog/placeholder.md delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/src/pages/authors.astro delete mode 100644 packages/astro/test/fixtures/content-mixed-errors/src/pages/blog.astro create mode 100644 packages/astro/test/units/_temp-fixtures/package.json delete mode 100644 packages/astro/test/units/correct-path.js create mode 100644 packages/astro/test/units/teardown.js create mode 100644 patches/fs-fixture@2.4.0.patch diff --git a/.gitignore b/.gitignore index 8e6d783543..d6a28ec1b1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,8 @@ package-lock.json packages/astro/src/**/*.prebuilt.ts packages/astro/src/**/*.prebuilt-dev.ts -!packages/astro/vendor/vite/dist +packages/astro/test/units/_temp-fixtures/* +!packages/astro/test/units/_temp-fixtures/package.json packages/integrations/**/.netlify/ # exclude IntelliJ/WebStorm stuff diff --git a/biome.jsonc b/biome.jsonc index a949301439..227f37a08d 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -6,6 +6,7 @@ "**/dist/**", "**/smoke/**", "**/fixtures/**", + "**/_temp-fixtures/**", "**/vendor/**", "**/.vercel/**", ], diff --git a/package.json b/package.json index 7c2919886a..683384edf3 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,9 @@ "allowAny": [ "astro" ] + }, + "patchedDependencies": { + "fs-fixture@2.4.0": "patches/fs-fixture@2.4.0.patch" } } } diff --git a/packages/astro/package.json b/packages/astro/package.json index 09e71a8309..054bc92f34 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -112,14 +112,15 @@ "build": "pnpm run prebuild && astro-scripts build \"src/**/*.{ts,js}\" --copy-wasm && tsc", "build:ci": "pnpm run prebuild && astro-scripts build \"src/**/*.{ts,js}\" --copy-wasm", "dev": "astro-scripts dev --copy-wasm --prebuild \"src/runtime/server/astro-island.ts\" --prebuild \"src/runtime/client/{idle,load,media,only,visible}.ts\" \"src/**/*.{ts,js}\"", - "test": "pnpm run test:node && pnpm run test:types", - "test:match": "pnpm run test:node --match", + "test": "pnpm run test:unit && pnpm run test:integration && pnpm run test:types", + "test:match": "astro-scripts test \"test/**/*.test.js\" --match", "test:e2e": "pnpm test:e2e:chrome && pnpm test:e2e:firefox", "test:e2e:match": "playwright test -g", "test:e2e:chrome": "playwright test", "test:e2e:firefox": "playwright test --config playwright.firefox.config.js", "test:types": "tsc --project tsconfig.tests.json", - "test:node": "astro-scripts test \"test/**/*.test.js\"" + "test:unit": "astro-scripts test \"test/units/**/*.test.js\" --teardown ./test/units/teardown.js", + "test:integration": "astro-scripts test \"test/*.test.js\"" }, "dependencies": { "@astrojs/compiler": "^2.10.3", @@ -210,9 +211,9 @@ "eol": "^0.10.0", "execa": "^8.0.1", "expect-type": "^1.1.0", + "fs-fixture": "^2.4.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", - "memfs": "^4.14.0", "node-mocks-http": "^1.16.1", "parse-srcset": "^1.0.2", "rehype-autolink-headings": "^7.1.0", diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 3b75a3843e..55f12216dc 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -77,6 +77,9 @@ export default function createVitePluginAstroServer({ } process.on('unhandledRejection', handleUnhandledRejection); + viteServer.httpServer?.on('close', () => { + process.off('unhandledRejection', handleUnhandledRejection); + }); return () => { // Push this middleware to the front of the stack so that it can intercept responses. diff --git a/packages/astro/test/fixtures/content-mixed-errors/astro.config.mjs b/packages/astro/test/fixtures/content-mixed-errors/astro.config.mjs deleted file mode 100644 index 882e6515a6..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/astro.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { defineConfig } from 'astro/config'; - -// https://astro.build/config -export default defineConfig({}); diff --git a/packages/astro/test/fixtures/content-mixed-errors/package.json b/packages/astro/test/fixtures/content-mixed-errors/package.json deleted file mode 100644 index d90bfabda5..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@test/content-mixed-errors", - "type": "module", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "astro dev", - "start": "astro dev", - "build": "astro build", - "preview": "astro preview", - "astro": "astro" - }, - "dependencies": { - "astro": "workspace:*" - } -} diff --git a/packages/astro/test/fixtures/content-mixed-errors/src/content/authors/placeholder.json b/packages/astro/test/fixtures/content-mixed-errors/src/content/authors/placeholder.json deleted file mode 100644 index 64ae1c04c5..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/src/content/authors/placeholder.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "Placeholder" -} diff --git a/packages/astro/test/fixtures/content-mixed-errors/src/content/blog/placeholder.md b/packages/astro/test/fixtures/content-mixed-errors/src/content/blog/placeholder.md deleted file mode 100644 index f7f65691b4..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/src/content/blog/placeholder.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Placeholder post ---- diff --git a/packages/astro/test/fixtures/content-mixed-errors/src/pages/authors.astro b/packages/astro/test/fixtures/content-mixed-errors/src/pages/authors.astro deleted file mode 100644 index 8352a3d27d..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/src/pages/authors.astro +++ /dev/null @@ -1,10 +0,0 @@ ---- -import { getCollection } from 'astro:content'; -try { - await getCollection('authors') -} catch (e) { - return e -} ---- - -

Worked

diff --git a/packages/astro/test/fixtures/content-mixed-errors/src/pages/blog.astro b/packages/astro/test/fixtures/content-mixed-errors/src/pages/blog.astro deleted file mode 100644 index 0d5d2836ed..0000000000 --- a/packages/astro/test/fixtures/content-mixed-errors/src/pages/blog.astro +++ /dev/null @@ -1,7 +0,0 @@ ---- -import { getCollection } from 'astro:content'; - -await getCollection('blog') ---- - -

Worked

diff --git a/packages/astro/test/units/_temp-fixtures/package.json b/packages/astro/test/units/_temp-fixtures/package.json new file mode 100644 index 0000000000..3ecea0bfe3 --- /dev/null +++ b/packages/astro/test/units/_temp-fixtures/package.json @@ -0,0 +1,8 @@ +{ + "name": "astro-temp-fixtures", + "description": "This directory contains nested directories of dynamically created unit test fixtures. The deps here can be used by them", + "dependencies": { + "@astrojs/mdx": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/units/config/format.test.js b/packages/astro/test/units/config/format.test.js index 7b0c88d730..66938a03a5 100644 --- a/packages/astro/test/units/config/format.test.js +++ b/packages/astro/test/units/config/format.test.js @@ -1,25 +1,19 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; -import { createFs, runInContainer } from '../test-utils.js'; - -const root = new URL('../../fixtures/tailwindcss-ts/', import.meta.url); +import { createFixture, runInContainer } from '../test-utils.js'; describe('Astro config formats', () => { it('An mjs config can import TypeScript modules', async () => { - const fs = createFs( - { - '/src/pages/index.astro': ``, - '/src/stuff.ts': `export default 'works';`, - '/astro.config.mjs': ` + const fixture = await createFixture({ + '/src/pages/index.astro': ``, + '/src/stuff.ts': `export default 'works';`, + '/astro.config.mjs': `\ import stuff from './src/stuff.ts'; export default {} `, - }, - root, - ); + }); - await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, () => { + await runInContainer({ inlineConfig: { root: fixture.path } }, () => { assert.equal( true, true, diff --git a/packages/astro/test/units/content-collections/frontmatter.test.js b/packages/astro/test/units/content-collections/frontmatter.test.js index 2a3cd31ec4..4f587a90fe 100644 --- a/packages/astro/test/units/content-collections/frontmatter.test.js +++ b/packages/astro/test/units/content-collections/frontmatter.test.js @@ -1,33 +1,16 @@ -import nodeFS from 'node:fs'; -import path from 'node:path'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; import { attachContentServerListeners } from '../../../dist/content/index.js'; -import { createFs, runInContainer, triggerFSEvent } from '../test-utils.js'; - -const root = new URL('../../fixtures/alias/', import.meta.url); - -function getTypesDts() { - const typesdtsURL = new URL('../../../templates/content/types.d.ts', import.meta.url); - const relpath = path - .relative(fileURLToPath(root), fileURLToPath(typesdtsURL)) - .replace(/\\/g, '/'); - return { - [relpath]: nodeFS.readFileSync(typesdtsURL, 'utf-8'), - }; -} +import { createFixture, runInContainer } from '../test-utils.js'; describe('frontmatter', () => { it('errors in content/ does not crash server', async () => { - const fs = createFs( - { - ...getTypesDts(), - '/src/content/posts/blog.md': ` + const fixture = await createFixture({ + '/src/content/posts/blog.md': `\ --- title: One --- `, - '/src/content/config.ts': ` + '/src/content/config.ts': `\ import { defineCollection, z } from 'astro:content'; const posts = defineCollection({ @@ -38,7 +21,7 @@ describe('frontmatter', () => { posts }; `, - '/src/pages/index.astro': ` + '/src/pages/index.astro': `\ --- --- @@ -48,14 +31,12 @@ describe('frontmatter', () => { `, - }, - root, - ); + }); - await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => { + await runInContainer({ inlineConfig: { root: fixture.path } }, async (container) => { await attachContentServerListeners(container); - fs.writeFileFromRootSync( + await fixture.writeFile( '/src/content/posts/blog.md', ` --- @@ -64,7 +45,6 @@ describe('frontmatter', () => { --- `, ); - triggerFSEvent(container, fs, '/src/content/posts/blog.md', 'change'); await new Promise((resolve) => setTimeout(resolve, 100)); // Note, if we got here, it didn't crash }); diff --git a/packages/astro/test/units/correct-path.js b/packages/astro/test/units/correct-path.js deleted file mode 100644 index 026baeaf69..0000000000 --- a/packages/astro/test/units/correct-path.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * correctPath.js - * Taken from: - * https://github.com/streamich/fs-monkeys - */ - -const isWin = process.platform === 'win32'; - -/*! - * removeTrailingSeparator - * - * Inlined from: - * Copyright (c) darsain. - * Released under the ISC License. - */ -function removeTrailingSeparator(str) { - let i = str.length - 1; - if (i < 2) { - return str; - } - while (isSeparator(str, i)) { - i--; - } - return str.substr(0, i + 1); -} - -function isSeparator(str, i) { - let char = str[i]; - return i > 0 && (char === '/' || (isWin && char === '\\')); -} - -/*! - * normalize-path - * - * Inlined from: - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ -function normalizePath(str, stripTrailing) { - if (typeof str !== 'string') { - throw new TypeError('expected a string'); - } - str = str.replace(/[\\/]+/g, '/'); - if (stripTrailing !== false) { - str = removeTrailingSeparator(str); - } - return str; -} - -/*! - * unixify - * - * Inlined from: - * Copyright (c) 2014, 2017, Jon Schlinkert. - * Released under the MIT License. - */ -export function unixify(filepath, stripTrailing = true) { - if (isWin) { - filepath = normalizePath(filepath, stripTrailing); - return filepath.replace(/^([a-zA-Z]+:|\.\/)/, ''); - } - return filepath; -} - -/* - * Corrects a windows path to unix format (including \\?\c:...) - */ -export function correctPath(filepath) { - return unixify(filepath.replace(/^\\\\\?\\.:\\/, '\\')); -} diff --git a/packages/astro/test/units/dev/base.test.js b/packages/astro/test/units/dev/base.test.js index e625df4789..f230ad563c 100644 --- a/packages/astro/test/units/dev/base.test.js +++ b/packages/astro/test/units/dev/base.test.js @@ -1,26 +1,19 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; -import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js'; - -const root = new URL('../../fixtures/alias/', import.meta.url); +import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js'; describe('base configuration', () => { describe('with trailingSlash: "never"', () => { describe('index route', () => { it('Requests that include a trailing slash 404', async () => { - const fs = createFs( - { - '/src/pages/index.astro': `

testing

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/pages/index.astro': `

testing

`, + }); await runInContainer( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, base: '/docs', trailingSlash: 'never', }, @@ -38,18 +31,15 @@ describe('base configuration', () => { }); it('Requests that exclude a trailing slash 200', async () => { - const fs = createFs( - { - '/src/pages/index.astro': `

testing

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/pages/index.astro': `

testing

`, + }); await runInContainer( { fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, base: '/docs', trailingSlash: 'never', }, @@ -69,18 +59,14 @@ describe('base configuration', () => { describe('sub route', () => { it('Requests that include a trailing slash 404', async () => { - const fs = createFs( - { - '/src/pages/sub/index.astro': `

testing

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/pages/sub/index.astro': `

testing

`, + }); await runInContainer( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, base: '/docs', trailingSlash: 'never', }, @@ -98,18 +84,14 @@ describe('base configuration', () => { }); it('Requests that exclude a trailing slash 200', async () => { - const fs = createFs( - { - '/src/pages/sub/index.astro': `

testing

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/pages/sub/index.astro': `

testing

`, + }); await runInContainer( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, base: '/docs', trailingSlash: 'never', }, diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index 3417650fc9..9da0c776f2 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -1,129 +1,146 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; import _sync from '../../../dist/core/sync/index.js'; -import { createFsWithFallback } from '../test-utils.js'; +import { createFixture } from '../test-utils.js'; -const root = new URL('../../fixtures/content-mixed-errors/', import.meta.url); - -async function sync({ fs }) { +async function sync(root) { try { - await _sync( - { - root: fileURLToPath(root), - logLevel: 'silent', - }, - { - fs, - }, - ); + await _sync({ + root, + logLevel: 'silent', + }); return 0; - } catch (_) { + } catch { return 1; } } +const baseFileTree = { + '/src/content/authors/placeholder.json': `{ "name": "Placeholder" }`, + '/src/content/blog/placeholder.md': `\ +--- +title: Placeholder post +--- +`, + '/src/pages/authors.astro': `\ +--- +import { getCollection } from 'astro:content'; +try { + await getCollection('authors') +} catch (e) { + return e +} +--- + +

Worked

+`, + '/src/pages/blog.astro': `\ +--- +import { getCollection } from 'astro:content'; + +await getCollection('blog') +--- + +

Worked

`, +}; + describe('Content Collections - mixed content errors', () => { it('raises "mixed content" error when content in data collection', async () => { - const fs = createFsWithFallback( - { - '/src/content/authors/ben.md': `--- + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/authors/ben.md': `\ +--- name: Ben --- -# Ben`, - '/src/content/authors/tony.json': `{ "name": "Tony" }`, - '/src/content/config.ts': ` +# Ben +`, + '/src/content/authors/tony.json': `{ "name": "Tony" }`, + '/src/content/config.ts': `\ +import { z, defineCollection } from 'astro:content'; - import { z, defineCollection } from 'astro:content'; +const authors = defineCollection({ + type: 'data', + schema: z.object({ + name: z.string(), + }), +}); - const authors = defineCollection({ - type: 'data', - schema: z.object({ - name: z.string(), - }), - }); +export const collections = { authors }; +`, + }); - export const collections = { authors };`, - }, - root, - ); - - assert.equal(await sync({ fs }), 1); + assert.equal(await sync(fixture.path), 1); }); it('raises "mixed content" error when data in content collection', async () => { - const fs = createFsWithFallback( - { - '/src/content/blog/post.md': `--- + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/blog/post.md': `\ +--- title: Post --- -# Post`, - '/src/content/blog/post.yaml': `title: YAML Post`, - '/src/content/config.ts': ` +# Post +`, + '/src/content/blog/post.yaml': `title: YAML Post`, + '/src/content/config.ts': `\ +import { z, defineCollection } from 'astro:content'; - import { z, defineCollection } from 'astro:content'; +const blog = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + }), +}); - const blog = defineCollection({ - type: 'content', - schema: z.object({ - title: z.string(), - }), - }); +export const collections = { blog }; +`, + }); - export const collections = { blog };`, - }, - root, - ); - - assert.equal(await sync({ fs }), 1); + assert.equal(await sync(fixture.path), 1); }); it('raises error when data collection configured as content collection', async () => { - const fs = createFsWithFallback( - { - '/src/content/banners/welcome.json': `{ "src": "/example", "alt": "Welcome" }`, - '/src/content/config.ts': ` + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/banners/welcome.json': `{ "src": "/example", "alt": "Welcome" }`, + '/src/content/config.ts': `\ +import { z, defineCollection } from 'astro:content'; - import { z, defineCollection } from 'astro:content'; +const banners = defineCollection({ + schema: z.object({ + src: z.string(), + alt: z.string(), + }), +}); - const banners = defineCollection({ - schema: z.object({ - src: z.string(), - alt: z.string(), - }), - }); +export const collections = { banners }; +`, + }); - export const collections = { banners };`, - }, - root, - ); - - assert.equal(await sync({ fs }), 1); + assert.equal(await sync(fixture.path), 1); }); it('does not raise error for empty collection with config', async () => { - const fs = createFsWithFallback( - { - // Add placeholder to ensure directory exists - '/src/content/i18n/_placeholder.txt': 'Need content here', - '/src/content/config.ts': ` - import { z, defineCollection } from 'astro:content'; + const fixture = await createFixture({ + ...baseFileTree, + // Add placeholder to ensure directory exists + '/src/content/i18n/_placeholder.txt': 'Need content here', + '/src/content/config.ts': `\ +import { z, defineCollection } from 'astro:content'; - const i18n = defineCollection({ - type: 'data', - schema: z.object({ - greeting: z.string(), - }), - }); +const i18n = defineCollection({ + type: 'data', + schema: z.object({ + greeting: z.string(), + }), +}); - export const collections = { i18n };`, - }, - root, - ); +export const collections = { i18n }; +`, + }); - const res = await sync({ fs }); - assert.equal(res, 0); + assert.equal(await sync(fixture.path), 0); }); }); diff --git a/packages/astro/test/units/dev/collections-renderentry.test.js b/packages/astro/test/units/dev/collections-renderentry.test.js index 082cd6b2f7..6bd906c9c8 100644 --- a/packages/astro/test/units/dev/collections-renderentry.test.js +++ b/packages/astro/test/units/dev/collections-renderentry.test.js @@ -1,12 +1,39 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; import * as cheerio from 'cheerio'; import { attachContentServerListeners } from '../../../dist/content/server-listeners.js'; -import { createFsWithFallback, createRequestAndResponse, runInContainer } from '../test-utils.js'; +import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js'; -const root = new URL('../../fixtures/content/', import.meta.url); +const baseFileTree = { + 'astro.config.mjs': `\ +import mdx from '@astrojs/mdx'; +export default { + integrations: [mdx()] +}; +`, + '/src/content/blog/promo/_launch-week-styles.css': `\ +body { + font-family: 'Comic Sans MS', sans-serif; +} +`, + '/src/content/blog/promo/launch-week.mdx': `\ +--- +title: 'Launch week!' +description: 'Join us for the exciting launch of SPACE BLOG' +publishedDate: 'Sat May 21 2022 00:00:00 GMT-0400 (Eastern Daylight Time)' +tags: ['announcement'] +--- + +import './_launch-week-styles.css'; + +Join us for the space blog launch! + +- THIS THURSDAY +- Houston, TX +- Dress code: **interstellar casual** ✨ +`, +}; /** @type {typeof runInContainer} */ async function runInContainerWithContentListeners(params, callback) { @@ -18,9 +45,9 @@ async function runInContainerWithContentListeners(params, callback) { describe('Content Collections - render()', () => { it('can be called in a page component', async () => { - const fs = createFsWithFallback( - { - '/src/content/config.ts': ` + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/config.ts': ` import { z, defineCollection } from 'astro:content'; const blog = defineCollection({ @@ -32,7 +59,7 @@ describe('Content Collections - render()', () => { export const collections = { blog }; `, - '/src/pages/index.astro': ` + '/src/pages/index.astro': ` --- import { getCollection } from 'astro:content'; const blog = await getCollection('blog'); @@ -47,15 +74,12 @@ describe('Content Collections - render()', () => { `, - }, - root, - ); + }); await runInContainerWithContentListeners( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, vite: { server: { middlewareMode: true } }, }, }, @@ -79,9 +103,9 @@ describe('Content Collections - render()', () => { }); it('can be used in a layout component', async () => { - const fs = createFsWithFallback( - { - '/src/components/Layout.astro': ` + const fixture = await createFixture({ + ...baseFileTree, + '/src/components/Layout.astro': ` --- import { getCollection } from 'astro:content'; const blog = await getCollection('blog'); @@ -99,7 +123,7 @@ describe('Content Collections - render()', () => { `, - '/src/pages/index.astro': ` + '/src/pages/index.astro': ` --- import Layout from '../components/Layout.astro'; --- @@ -107,15 +131,12 @@ describe('Content Collections - render()', () => {

Index page

`, - }, - root, - ); + }); await runInContainerWithContentListeners( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, vite: { server: { middlewareMode: true } }, }, }, @@ -139,9 +160,9 @@ describe('Content Collections - render()', () => { }); it('can be used in a slot', async () => { - const fs = createFsWithFallback( - { - '/src/content/config.ts': ` + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/config.ts': ` import { z, defineCollection } from 'astro:content'; const blog = defineCollection({ @@ -153,7 +174,7 @@ describe('Content Collections - render()', () => { export const collections = { blog }; `, - '/src/components/Layout.astro': ` + '/src/components/Layout.astro': ` @@ -164,7 +185,7 @@ describe('Content Collections - render()', () => { `, - '/src/pages/index.astro': ` + '/src/pages/index.astro': ` --- import Layout from '../components/Layout.astro'; import { getCollection } from 'astro:content'; @@ -177,15 +198,12 @@ describe('Content Collections - render()', () => { `, - }, - root, - ); + }); await runInContainerWithContentListeners( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, vite: { server: { middlewareMode: true } }, }, }, @@ -209,9 +227,9 @@ describe('Content Collections - render()', () => { }); it('can be called from any js/ts file', async () => { - const fs = createFsWithFallback( - { - '/src/content/config.ts': ` + const fixture = await createFixture({ + ...baseFileTree, + '/src/content/config.ts': ` import { z, defineCollection } from 'astro:content'; const blog = defineCollection({ @@ -223,7 +241,7 @@ describe('Content Collections - render()', () => { export const collections = { blog }; `, - '/src/launch-week.ts': ` + '/src/launch-week.ts': ` import { getCollection } from 'astro:content'; export let Content; @@ -234,7 +252,7 @@ describe('Content Collections - render()', () => { Content = mod.Content; `, - '/src/pages/index.astro': ` + '/src/pages/index.astro': ` --- import { Content } from '../launch-week.ts'; --- @@ -246,15 +264,12 @@ describe('Content Collections - render()', () => { `, - }, - root, - ); + }); await runInContainerWithContentListeners( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, vite: { server: { middlewareMode: true } }, }, }, diff --git a/packages/astro/test/units/dev/dev.test.js b/packages/astro/test/units/dev/dev.test.js index c82232768a..74f1d1e92e 100644 --- a/packages/astro/test/units/dev/dev.test.js +++ b/packages/astro/test/units/dev/dev.test.js @@ -1,21 +1,12 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; import * as cheerio from 'cheerio'; -import { - createFs, - createRequestAndResponse, - runInContainer, - triggerFSEvent, -} from '../test-utils.js'; - -const root = new URL('../../fixtures/alias/', import.meta.url); +import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js'; describe('dev container', () => { it('can render requests', async () => { - const fs = createFs( - { - '/src/pages/index.astro': ` + const fixture = await createFixture({ + '/src/pages/index.astro': ` --- const name = 'Testing'; --- @@ -26,11 +17,9 @@ describe('dev container', () => { `, - }, - root, - ); + }); - await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => { + await runInContainer({ inlineConfig: { root: fixture.path } }, async (container) => { const { req, res, text } = createRequestAndResponse({ method: 'GET', url: '/', @@ -43,89 +32,16 @@ describe('dev container', () => { }); }); - it('HMR only short circuits on previously cached modules', async () => { - const fs = createFs( - { - '/src/components/Header.astro': ` -

{Astro.props.title}

- `, - '/src/pages/index.astro': ` - --- - import Header from '../components/Header.astro'; - const name = 'Testing'; - --- - - {name} - -
- - - `, - }, - root, - ); - - await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => { - let r = createRequestAndResponse({ - method: 'GET', - url: '/', - }); - container.handle(r.req, r.res); - let html = await r.text(); - let $ = cheerio.load(html); - assert.equal($('body.one').length, 1); - - fs.writeFileFromRootSync( - '/src/components/Header.astro', - ` -

{Astro.props.title}

- `, - ); - triggerFSEvent(container, fs, '/src/components/Header.astro', 'change'); - - fs.writeFileFromRootSync( - '/src/pages/index.astro', - ` - --- - import Header from '../components/Header.astro'; - const name = 'Testing'; - --- - - {name} - -
- - - `, - ); - triggerFSEvent(container, fs, '/src/pages/index.astro', 'change'); - - r = createRequestAndResponse({ - method: 'GET', - url: '/', - }); - container.handle(r.req, r.res); - html = await r.text(); - $ = cheerio.load(html); - assert.equal($('body.one').length, 0); - assert.equal($('body.two').length, 1); - }); - }); - it('Allows dynamic segments in injected routes', async () => { - const fs = createFs( - { - '/src/components/test.astro': `

{Astro.params.slug}

`, - '/src/pages/test-[slug].astro': `

{Astro.params.slug}

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/components/test.astro': `

{Astro.params.slug}

`, + '/src/pages/test-[slug].astro': `

{Astro.params.slug}

`, + }); await runInContainer( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, output: 'server', integrations: [ { @@ -164,19 +80,15 @@ describe('dev container', () => { }); it('Serves injected 404 route for any 404', async () => { - const fs = createFs( - { - '/src/components/404.astro': `

Custom 404

`, - '/src/pages/page.astro': `

Regular page

`, - }, - root, - ); + const fixture = await createFixture({ + '/src/components/404.astro': `

Custom 404

`, + '/src/pages/page.astro': `

Regular page

`, + }); await runInContainer( { - fs, inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, output: 'server', integrations: [ { @@ -226,10 +138,14 @@ describe('dev container', () => { }); it('items in public/ are not available from root when using a base', async () => { + const fixture = await createFixture({ + '/public/test.txt': `Test`, + }); + await runInContainer( { inlineConfig: { - root: fileURLToPath(root), + root: fixture.path, base: '/sub/', }, }, @@ -260,7 +176,11 @@ describe('dev container', () => { }); it('items in public/ are available from root when not using a base', async () => { - await runInContainer({ inlineConfig: { root: fileURLToPath(root) } }, async (container) => { + const fixture = await createFixture({ + '/public/test.txt': `Test`, + }); + + await runInContainer({ inlineConfig: { root: fixture.path } }, async (container) => { // Try the root path let r = createRequestAndResponse({ method: 'GET', diff --git a/packages/astro/test/units/dev/head-injection.test.js b/packages/astro/test/units/dev/head-injection.test.js index 0796ba45f7..fa61cea58a 100644 --- a/packages/astro/test/units/dev/head-injection.test.js +++ b/packages/astro/test/units/dev/head-injection.test.js @@ -1,14 +1,13 @@ import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; import * as cheerio from 'cheerio'; -import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js'; +import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js'; const root = new URL('../../fixtures/alias/', import.meta.url); describe('head injection', () => { it('Dynamic injection from component created in the page frontmatter', async () => { - const fs = createFs( + const fixture = await createFixture( { '/src/components/Other.astro': `