0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

Use real filesystem for unit testing (#12172)

This commit is contained in:
Bjorn Lu 2024-10-15 15:34:42 +08:00 committed by GitHub
parent 5ab2d980aa
commit 64bb796c0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 578 additions and 940 deletions

3
.gitignore vendored
View file

@ -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

View file

@ -6,6 +6,7 @@
"**/dist/**",
"**/smoke/**",
"**/fixtures/**",
"**/_temp-fixtures/**",
"**/vendor/**",
"**/.vercel/**",
],

View file

@ -87,6 +87,9 @@
"allowAny": [
"astro"
]
},
"patchedDependencies": {
"fs-fixture@2.4.0": "patches/fs-fixture@2.4.0.patch"
}
}
}

View file

@ -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",

View file

@ -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.

View file

@ -1,4 +0,0 @@
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({});

View file

@ -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:*"
}
}

View file

@ -1,3 +0,0 @@
{
"name": "Placeholder"
}

View file

@ -1,3 +0,0 @@
---
title: Placeholder post
---

View file

@ -1,10 +0,0 @@
---
import { getCollection } from 'astro:content';
try {
await getCollection('authors')
} catch (e) {
return e
}
---
<h1>Worked</h1>

View file

@ -1,7 +0,0 @@
---
import { getCollection } from 'astro:content';
await getCollection('blog')
---
<h1>Worked</h1>

View file

@ -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:*"
}
}

View file

@ -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(
{
const fixture = await createFixture({
'/src/pages/index.astro': ``,
'/src/stuff.ts': `export default 'works';`,
'/astro.config.mjs': `
'/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,

View file

@ -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': `\
---
---
<html>
@ -48,14 +31,12 @@ describe('frontmatter', () => {
</body>
</html>
`,
},
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
});

View file

@ -1,70 +0,0 @@
/**
* correctPath.js <https://github.com/streamich/fs-monkey/blob/af36a890d8070b25b9eae7178824f653bad5621f/src/correctPath.js>
* Taken from:
* https://github.com/streamich/fs-monkeys
*/
const isWin = process.platform === 'win32';
/*!
* removeTrailingSeparator <https://github.com/darsain/remove-trailing-separator>
*
* 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 <https://github.com/jonschlinkert/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 <https://github.com/jonschlinkert/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(/^\\\\\?\\.:\\/, '\\'));
}

View file

@ -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(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>testing</h1>`,
},
root,
);
});
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(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>testing</h1>`,
},
root,
);
});
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(
{
const fixture = await createFixture({
'/src/pages/sub/index.astro': `<h1>testing</h1>`,
},
root,
);
});
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(
{
const fixture = await createFixture({
'/src/pages/sub/index.astro': `<h1>testing</h1>`,
},
root,
);
});
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
base: '/docs',
trailingSlash: 'never',
},

View file

@ -1,40 +1,62 @@
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),
await _sync({
root,
logLevel: 'silent',
},
{
fs,
},
);
});
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
}
---
<h1>Worked</h1>
`,
'/src/pages/blog.astro': `\
---
import { getCollection } from 'astro:content';
await getCollection('blog')
---
<h1>Worked</h1>`,
};
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`,
# Ben
`,
'/src/content/authors/tony.json': `{ "name": "Tony" }`,
'/src/content/config.ts': `
'/src/content/config.ts': `\
import { z, defineCollection } from 'astro:content';
const authors = defineCollection({
@ -44,25 +66,25 @@ name: Ben
}),
});
export const collections = { authors };`,
},
root,
);
export const collections = { authors };
`,
});
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`,
# Post
`,
'/src/content/blog/post.yaml': `title: YAML Post`,
'/src/content/config.ts': `
'/src/content/config.ts': `\
import { z, defineCollection } from 'astro:content';
const blog = defineCollection({
@ -72,20 +94,18 @@ title: Post
}),
});
export const collections = { blog };`,
},
root,
);
export const collections = { blog };
`,
});
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(
{
const fixture = await createFixture({
...baseFileTree,
'/src/content/banners/welcome.json': `{ "src": "/example", "alt": "Welcome" }`,
'/src/content/config.ts': `
'/src/content/config.ts': `\
import { z, defineCollection } from 'astro:content';
const banners = defineCollection({
@ -95,20 +115,19 @@ title: Post
}),
});
export const collections = { banners };`,
},
root,
);
export const collections = { banners };
`,
});
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(
{
const fixture = await createFixture({
...baseFileTree,
// Add placeholder to ensure directory exists
'/src/content/i18n/_placeholder.txt': 'Need content here',
'/src/content/config.ts': `
'/src/content/config.ts': `\
import { z, defineCollection } from 'astro:content';
const i18n = defineCollection({
@ -118,12 +137,10 @@ title: Post
}),
});
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);
});
});

View file

@ -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,8 +45,8 @@ async function runInContainerWithContentListeners(params, callback) {
describe('Content Collections - render()', () => {
it('can be called in a page component', async () => {
const fs = createFsWithFallback(
{
const fixture = await createFixture({
...baseFileTree,
'/src/content/config.ts': `
import { z, defineCollection } from 'astro:content';
@ -47,15 +74,12 @@ describe('Content Collections - render()', () => {
</body>
</html>
`,
},
root,
);
});
await runInContainerWithContentListeners(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},
@ -79,8 +103,8 @@ describe('Content Collections - render()', () => {
});
it('can be used in a layout component', async () => {
const fs = createFsWithFallback(
{
const fixture = await createFixture({
...baseFileTree,
'/src/components/Layout.astro': `
---
import { getCollection } from 'astro:content';
@ -107,15 +131,12 @@ describe('Content Collections - render()', () => {
<h1 slot="title">Index page</h2>
</Layout>
`,
},
root,
);
});
await runInContainerWithContentListeners(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},
@ -139,8 +160,8 @@ describe('Content Collections - render()', () => {
});
it('can be used in a slot', async () => {
const fs = createFsWithFallback(
{
const fixture = await createFixture({
...baseFileTree,
'/src/content/config.ts': `
import { z, defineCollection } from 'astro:content';
@ -177,15 +198,12 @@ describe('Content Collections - render()', () => {
<Content slot="main" />
</Layout>
`,
},
root,
);
});
await runInContainerWithContentListeners(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},
@ -209,8 +227,8 @@ describe('Content Collections - render()', () => {
});
it('can be called from any js/ts file', async () => {
const fs = createFsWithFallback(
{
const fixture = await createFixture({
...baseFileTree,
'/src/content/config.ts': `
import { z, defineCollection } from 'astro:content';
@ -246,15 +264,12 @@ describe('Content Collections - render()', () => {
</body>
</html>
`,
},
root,
);
});
await runInContainerWithContentListeners(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},

View file

@ -1,20 +1,11 @@
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(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
---
const name = 'Testing';
@ -26,11 +17,9 @@ describe('dev container', () => {
</body>
</html>
`,
},
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': `
<h1>{Astro.props.title}</h1>
`,
'/src/pages/index.astro': `
---
import Header from '../components/Header.astro';
const name = 'Testing';
---
<html>
<head><title>{name}</title></head>
<body class="one">
<Header title={name} />
</body>
</html>
`,
},
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',
`
<h1>{Astro.props.title}</h1>
`,
);
triggerFSEvent(container, fs, '/src/components/Header.astro', 'change');
fs.writeFileFromRootSync(
'/src/pages/index.astro',
`
---
import Header from '../components/Header.astro';
const name = 'Testing';
---
<html>
<head><title>{name}</title></head>
<body class="two">
<Header title={name} />
</body>
</html>
`,
);
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(
{
const fixture = await createFixture({
'/src/components/test.astro': `<h1>{Astro.params.slug}</h1>`,
'/src/pages/test-[slug].astro': `<h1>{Astro.params.slug}</h1>`,
},
root,
);
});
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(
{
const fixture = await createFixture({
'/src/components/404.astro': `<h1>Custom 404</h1>`,
'/src/pages/page.astro': `<h1>Regular page</h1>`,
},
root,
);
});
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',

View file

@ -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': `
<style>
@ -64,9 +63,8 @@ describe('head injection', () => {
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},
@ -87,7 +85,7 @@ describe('head injection', () => {
});
it('Dynamic injection from a layout component', async () => {
const fs = createFs(
const fixture = await createFixture(
{
'/src/components/Other.astro': `
<style>
@ -164,9 +162,8 @@ describe('head injection', () => {
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
vite: { server: { middlewareMode: true } },
},
},

View file

@ -1,17 +1,13 @@
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('hydration', () => {
it(
'should not crash when reassigning a hydrated component',
{ skip: true, todo: "It seems that `components/Client.svelte` isn't found" },
async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
---
import Svelte from '../components/Client.svelte';
@ -26,15 +22,12 @@ describe('hydration', () => {
</body>
</html>
`,
},
root,
);
});
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
logLevel: 'silent',
},
},

View file

@ -1,24 +1,26 @@
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import * as cheerio from 'cheerio';
import {
createContainerWithAutomaticRestart,
startContainer,
} from '../../../dist/core/dev/index.js';
import { createFs, createRequestAndResponse, triggerFSEvent } from '../test-utils.js';
import { createFixture, createRequestAndResponse } from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
/** @type {import('astro').AstroInlineConfig} */
const defaultInlineConfig = {
logLevel: 'silent',
};
function isStarted(container) {
return !!container.viteServer.httpServer?.listening;
}
describe('dev container restarts', () => {
// Checking for restarts may hang if no restarts happen, so set a 20s timeout for each test
describe('dev container restarts', { timeout: 20000 }, () => {
it('Surfaces config errors on restarts', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
<html>
<head><title>Test</title></head>
@ -27,16 +29,14 @@ describe('dev container restarts', () => {
</body>
</html>
`,
'/astro.config.mjs': `
`,
},
root,
);
'/astro.config.mjs': ``,
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
try {
@ -52,39 +52,36 @@ describe('dev container restarts', () => {
// Create an error
let restartComplete = restart.restarted();
fs.writeFileFromRootSync('/astro.config.mjs', 'const foo = bar');
// Vite watches the real filesystem, so we have to mock this part. It's not so bad.
await fixture.writeFile('/astro.config.mjs', 'const foo = bar');
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fs.getFullyResolvedPath('/astro.config.mjs'),
fixture.getPath('/astro.config.mjs').replace(/\\/g, '/'),
);
// Wait for the restart to finish
let hmrError = await restartComplete;
assert.notEqual(typeof hmrError, 'undefined');
assert.ok(hmrError instanceof Error);
// Do it a second time to make sure we are still watching
restartComplete = restart.restarted();
fs.writeFileFromRootSync('/astro.config.mjs', 'const foo = bar2');
// Vite watches the real filesystem, so we have to mock this part. It's not so bad.
await fixture.writeFile('/astro.config.mjs', 'const foo = bar2');
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fs.getFullyResolvedPath('/astro.config.mjs'),
fixture.getPath('/astro.config.mjs').replace(/\\/g, '/'),
);
hmrError = await restartComplete;
assert.notEqual(typeof hmrError, 'undefined');
assert.ok(hmrError instanceof Error);
} finally {
await restart.container.close();
}
});
it('Restarts the container if previously started', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
<html>
<head><title>Test</title></head>
@ -94,13 +91,13 @@ describe('dev container restarts', () => {
</html>
`,
'/astro.config.mjs': ``,
},
root,
);
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
await startContainer(restart.container);
assert.equal(isStarted(restart.container), true);
@ -108,7 +105,12 @@ describe('dev container restarts', () => {
try {
// Trigger a change
let restartComplete = restart.restarted();
triggerFSEvent(restart.container, fs, '/astro.config.mjs', 'change');
await fixture.writeFile('/astro.config.mjs', '');
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fixture.getPath('/astro.config.mjs').replace(/\\/g, '/'),
);
await restartComplete;
assert.equal(isStarted(restart.container), true);
@ -118,18 +120,16 @@ describe('dev container restarts', () => {
});
it('Is able to restart project using Tailwind + astro.config.ts', async () => {
const troot = new URL('../../fixtures/tailwindcss-ts/', import.meta.url);
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': ``,
'/astro.config.ts': ``,
},
troot,
);
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
await startContainer(restart.container);
assert.equal(isStarted(restart.container), true);
@ -137,7 +137,12 @@ describe('dev container restarts', () => {
try {
// Trigger a change
let restartComplete = restart.restarted();
triggerFSEvent(restart.container, fs, '/astro.config.ts', 'change');
await fixture.writeFile('/astro.config.ts', '');
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fixture.getPath('/astro.config.mjs').replace(/\\/g, '/'),
);
await restartComplete;
assert.equal(isStarted(restart.container), true);
@ -147,24 +152,27 @@ describe('dev container restarts', () => {
});
it('Is able to restart project on package.json changes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': ``,
},
root,
);
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
await startContainer(restart.container);
assert.equal(isStarted(restart.container), true);
try {
let restartComplete = restart.restarted();
fs.writeFileSync('/package.json', `{}`);
triggerFSEvent(restart.container, fs, '/package.json', 'change');
await fixture.writeFile('/package.json', `{}`);
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fixture.getPath('/package.json').replace(/\\/g, '/'),
);
await restartComplete;
} finally {
await restart.container.close();
@ -172,16 +180,15 @@ describe('dev container restarts', () => {
});
it('Is able to restart on viteServer.restart API call', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': ``,
},
root,
);
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
await startContainer(restart.container);
assert.equal(isStarted(restart.container), true);
@ -196,26 +203,28 @@ describe('dev container restarts', () => {
});
it('Is able to restart project on .astro/settings.json changes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': ``,
'/.astro/settings.json': `{}`,
},
root,
);
});
const restart = await createContainerWithAutomaticRestart({
fs,
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
inlineConfig: {
...defaultInlineConfig,
root: fixture.path,
},
});
await startContainer(restart.container);
assert.equal(isStarted(restart.container), true);
try {
let restartComplete = restart.restarted();
fs.mkdirSync('/.astro/', { recursive: true });
fs.writeFileSync('/.astro/settings.json', `{ }`);
triggerFSEvent(restart.container, fs, '/.astro/settings.json', 'change');
await fixture.writeFile('/.astro/settings.json', `{ }`);
// TODO: fix this hack
restart.container.viteServer.watcher.emit(
'change',
fixture.getPath('/.astro/settings.json').replace(/\\/g, '/'),
);
await restartComplete;
} finally {
await restart.container.close();

View file

@ -1,30 +1,23 @@
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';
const root = new URL('../../fixtures/alias/', import.meta.url);
import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js';
describe('core/render chunk', () => {
it('does not throw on user object with type', async () => {
const fs = createFs(
{
'/src/pages/index.astro': `
const fixture = await createFixture({
'/src/pages/index.astro': `\
---
const value = { type: 'foobar' }
---
<div id="chunk">{value}</div>
`,
},
root,
);
});
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
logLevel: 'silent',
integrations: [],
},

View file

@ -1,15 +1,11 @@
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';
const root = new URL('../../fixtures/alias/', import.meta.url);
import { createFixture, createRequestAndResponse, runInContainer } from '../test-utils.js';
describe('core/render components', () => {
it('should sanitize dynamic tags', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
---
const TagA = 'p style=color:red;'
@ -23,15 +19,12 @@ describe('core/render components', () => {
</body>
</html>
`,
},
root,
);
});
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
logLevel: 'silent',
integrations: [],
},
@ -58,8 +51,7 @@ describe('core/render components', () => {
});
it('should merge `class` and `class:list`', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `
---
import Class from '../components/Class.astro';
@ -79,15 +71,12 @@ describe('core/render components', () => {
'/src/components/BothLiteral.astro': `<pre id="both-literal" set:html={JSON.stringify(Astro.props)} />`,
'/src/components/BothFlipped.astro': `<pre id="both-flipped" set:html={JSON.stringify(Astro.props)} />`,
'/src/components/BothSpread.astro': `<pre id="both-spread" set:html={JSON.stringify(Astro.props)} />`,
},
root,
);
});
await runInContainer(
{
fs,
inlineConfig: {
root: fileURLToPath(root),
root: fixture.path,
logLevel: 'silent',
integrations: [],
},

View file

@ -1,16 +1,14 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createContainer } from '../../../dist/core/dev/container.js';
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
const root = new URL('../../fixtures/api-routes/', import.meta.url);
const fileSystem = {
'/src/pages/response-redirect.ts': `export const GET = ({ url }) => Response.redirect("https://example.com/destination", 307)`,
'/src/pages/response.ts': `export const GET = ({ url }) => new Response(null, { headers: { Location: "https://example.com/destination" }, status: 307 })`,
@ -23,9 +21,9 @@ describe('endpoints', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
adapter: testAdapter(),
});

View file

@ -1,11 +1,8 @@
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { Logger } from '../../../dist/core/logger/core.js';
import { createRouteManifest } from '../../../dist/core/routing/manifest/create.js';
import { createBasicSettings, createFs } from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
import { createBasicSettings, createFixture } from '../test-utils.js';
function getManifestRoutes(manifest) {
return manifest.routes.map((route) => ({
@ -41,21 +38,17 @@ function assertRouteRelations(routes, relations) {
describe('routing - createRouteManifest', () => {
it('using trailingSlash: "never" does not match the index route when it contains a trailing slash', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
base: '/search',
trailingSlash: 'never',
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
const [{ pattern }] = manifest.routes;
assert.equal(pattern.test(''), true);
@ -63,15 +56,12 @@ describe('routing - createRouteManifest', () => {
});
it('endpoint routes are sorted before page routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/[contact].astro': `<h1>test</h1>`,
'/src/pages/[contact].ts': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
base: '/search',
trailingSlash: 'never',
experimental: {
@ -91,9 +81,8 @@ describe('routing - createRouteManifest', () => {
];
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [
@ -117,18 +106,15 @@ describe('routing - createRouteManifest', () => {
});
it('static routes are sorted before dynamic and rest routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/[dynamic].astro': `<h1>test</h1>`,
'/src/pages/[...rest].astro': `<h1>test</h1>`,
'/src/pages/static.astro': `<h1>test</h1>`,
'/src/pages/static-[dynamic].astro': `<h1>test</h1>`,
'/src/pages/index.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
base: '/search',
trailingSlash: 'never',
experimental: {
@ -137,9 +123,8 @@ describe('routing - createRouteManifest', () => {
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assertRouteRelations(getManifestRoutes(manifest), [
@ -154,8 +139,7 @@ describe('routing - createRouteManifest', () => {
it('route sorting with multi-layer index page conflict', async () => {
// Reproducing regression from https://github.com/withastro/astro/issues/10071
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/a/1.astro': `<h1>test</h1>`,
'/src/pages/a/2.astro': `<h1>test</h1>`,
'/src/pages/a/3.astro': `<h1>test</h1>`,
@ -164,11 +148,9 @@ describe('routing - createRouteManifest', () => {
'/src/pages/test/[...slug].astro': `<h1>test</h1>`,
'/src/pages/test/index.astro': `<h1>test</h1>`,
'/src/pages/index.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
base: '/search',
trailingSlash: 'never',
experimental: {
@ -177,9 +159,8 @@ describe('routing - createRouteManifest', () => {
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assertRouteRelations(getManifestRoutes(manifest), [
@ -200,8 +181,7 @@ describe('routing - createRouteManifest', () => {
});
it('route sorting respects the file tree', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/[dynamic_folder]/static.astro': `<h1>test</h1>`,
'/src/pages/[dynamic_folder]/index.astro': `<h1>test</h1>`,
'/src/pages/[dynamic_folder]/[...rest].astro': `<h1>test</h1>`,
@ -213,11 +193,9 @@ describe('routing - createRouteManifest', () => {
'/src/pages/[...other].astro': `<h1>test</h1>`,
'/src/pages/static.astro': `<h1>test</h1>`,
'/src/pages/index.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
base: '/search',
trailingSlash: 'never',
experimental: {
@ -226,9 +204,8 @@ describe('routing - createRouteManifest', () => {
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assertRouteRelations(getManifestRoutes(manifest), [
@ -263,15 +240,12 @@ describe('routing - createRouteManifest', () => {
});
it('injected routes are sorted in legacy mode above filesystem routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>test</h1>`,
'/src/pages/blog/[...slug].astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -289,9 +263,8 @@ describe('routing - createRouteManifest', () => {
];
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [
@ -315,15 +288,12 @@ describe('routing - createRouteManifest', () => {
});
it('injected routes are sorted alongside filesystem routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>test</h1>`,
'/src/pages/blog/[...slug].astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -345,9 +315,8 @@ describe('routing - createRouteManifest', () => {
];
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [
@ -371,15 +340,12 @@ describe('routing - createRouteManifest', () => {
});
it('redirects are sorted in legacy mode below the filesystem routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>test</h1>`,
'/src/pages/blog/contributing.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -392,9 +358,8 @@ describe('routing - createRouteManifest', () => {
},
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [
@ -418,15 +383,12 @@ describe('routing - createRouteManifest', () => {
});
it('redirects are sorted alongside the filesystem routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/index.astro': `<h1>test</h1>`,
'/src/pages/blog/contributing.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -445,9 +407,8 @@ describe('routing - createRouteManifest', () => {
},
});
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [
@ -471,14 +432,11 @@ describe('routing - createRouteManifest', () => {
});
it('report colliding static routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/contributing.astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -496,9 +454,8 @@ describe('routing - createRouteManifest', () => {
];
const manifestOptions = {
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
};
const { logger, logs } = getLogger();
@ -523,15 +480,12 @@ describe('routing - createRouteManifest', () => {
});
it('report colliding SSR dynamic routes', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/[foo].astro': `<h1>test</h1>`,
'/src/pages/[bar].astro': `<h1>test</h1>`,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -542,9 +496,8 @@ describe('routing - createRouteManifest', () => {
});
const manifestOptions = {
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
};
const { logger, logs } = getLogger();
@ -569,16 +522,13 @@ describe('routing - createRouteManifest', () => {
});
it('should concatenate each part of the segment. issues#10122', async () => {
const fs = createFs(
{
const fixture = await createFixture({
'/src/pages/a-[b].astro': `<h1>test</h1>`,
'/src/pages/blog/a-[b].233.ts': ``,
},
root,
);
});
const settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
base: '/search',
trailingSlash: 'never',
@ -599,9 +549,8 @@ describe('routing - createRouteManifest', () => {
];
const manifest = createRouteManifest({
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
});
assert.deepEqual(getManifestRoutes(manifest), [

View file

@ -11,12 +11,11 @@ import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-serve
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
const fileSystem = {
'/src/pages/[serverDynamic].astro': `
---
@ -131,15 +130,14 @@ describe('Route matching', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
trailingSlash: 'never',
output: 'hybrid',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});
@ -149,9 +147,8 @@ describe('Route matching', () => {
pipeline = DevPipeline.create(undefined, { loader, logger: defaultLogger, manifest, settings });
manifestData = createRouteManifest(
{
cwd: fileURLToPath(root),
cwd: fixture.path,
settings,
fsMod: fs,
},
defaultLogger,
);

View file

@ -1,17 +1,15 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import * as cheerio from 'cheerio';
import { createContainer } from '../../../dist/core/dev/container.js';
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
const fileSystem = {
'/src/pages/[...testSlashTrim].astro': `
---
@ -34,15 +32,14 @@ describe('Route sanitization', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
trailingSlash: 'never',
output: 'hybrid',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});

View file

@ -1,16 +1,14 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createContainer } from '../../../dist/core/dev/container.js';
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
const root = new URL('../../fixtures/api-routes/', import.meta.url);
const fileSystem = {
'/src/pages/api.ts': `export const GET = () => Response.json({ success: true })`,
};
@ -20,15 +18,14 @@ describe('trailingSlash', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
trailingSlash: 'always',
output: 'server',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});

View file

@ -1,11 +1,10 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createContainer } from '../../../dist/core/dev/container.js';
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
@ -20,14 +19,13 @@ describe('endpoints', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem, root);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});

View file

@ -0,0 +1,19 @@
import fs from 'node:fs';
export default function teardown(testPassed) {
// Delete all directories within `_temp-fixtures` directory if all test passed
if (testPassed) {
try {
const tempFixturesDir = new URL('./_temp-fixtures/', import.meta.url);
const entries = fs.readdirSync(tempFixturesDir);
for (const entry of entries) {
if (entry === 'package.json' || entry === 'node_modules') continue;
const dir = new URL(entry, tempFixturesDir);
fs.rmSync(dir, { recursive: true });
}
} catch (e) {
console.error('Failed to delete temp fixtures');
throw e;
}
}
}

View file

@ -1,8 +1,7 @@
import { EventEmitter } from 'node:events';
import realFS from 'node:fs';
import npath from 'node:path';
import { fileURLToPath } from 'node:url';
import { Volume } from 'memfs';
import { createFixture as _createFixture } from 'fs-fixture';
import httpMocks from 'node-mocks-http';
import { getDefaultClientDirectives } from '../../dist/core/client-directive/index.js';
import { resolveConfig } from '../../dist/core/config/index.js';
@ -13,7 +12,6 @@ import { nodeLogDestination } from '../../dist/core/logger/node.js';
import { NOOP_MIDDLEWARE_FN } from '../../dist/core/middleware/noop-middleware.js';
import { Pipeline } from '../../dist/core/render/index.js';
import { RouteCache } from '../../dist/core/render/route-cache.js';
import { unixify } from './correct-path.js';
/** @type {import('../../src/core/logger/core').Logger} */
export const defaultLogger = new Logger({
@ -27,102 +25,21 @@ export const silentLogging = {
level: 'error',
};
class VirtualVolume extends Volume {
#root = '';
constructor(root) {
super();
this.#root = root;
}
#forcePath(p) {
if (p instanceof URL) {
p = unixify(fileURLToPath(p));
} else {
p = unixify(p);
}
return p;
}
getFullyResolvedPath(pth) {
return npath.posix.join(this.#root, pth);
}
readFile(p, ...args) {
return super.readFile(this.#forcePath(p), ...args);
}
existsSync(p) {
return super.existsSync(this.#forcePath(p));
}
writeFileFromRootSync(pth, ...rest) {
return super.writeFileSync(this.getFullyResolvedPath(pth), ...rest);
}
}
class VirtualVolumeWithFallback extends VirtualVolume {
// Fallback to the real fs
readFile(p, ...args) {
const cb = args[args.length - 1];
const argsMinusCallback = args.slice(0, args.length - 1);
return super.readFile(p, ...argsMinusCallback, function (err, data) {
if (err) {
realFS.readFile(p, ...argsMinusCallback, function (err2, data2) {
if (err2) {
cb(err);
} else {
cb(null, data2);
}
});
} else {
cb(null, data);
}
});
}
readFileSync(p, ...args) {
try {
return super.readFileSync(p, ...args);
} catch {
return realFS.readFileSync(p, ...args);
}
}
}
export function createFs(json, root, VolumeImpl = VirtualVolume) {
if (typeof root !== 'string') {
root = unixify(fileURLToPath(root));
}
const structure = {};
for (const [key, value] of Object.entries(json)) {
const fullpath = npath.posix.join(root, key);
structure[fullpath] = value;
}
const fs = new VolumeImpl(root);
fs.fromJSON(structure);
return fs;
}
export function createFsWithFallback(json, root) {
return createFs(json, root, VirtualVolumeWithFallback);
}
const tempFixturesDir = fileURLToPath(new URL('./_temp-fixtures/', import.meta.url));
/**
*
* @param {import('../../src/core/dev/container').Container} container
* @param {typeof import('node:fs')} fs
* @param {string} shortPath
* @param {'change'} eventType
* @param {import('fs-fixture').FileTree} tree
*/
export function triggerFSEvent(container, fs, shortPath, eventType) {
container.viteServer.watcher.emit(eventType, fs.getFullyResolvedPath(shortPath));
if (!fileURLToPath(container.settings.config.root).startsWith('/')) {
const drive = fileURLToPath(container.settings.config.root).slice(0, 2);
container.viteServer.watcher.emit(eventType, drive + fs.getFullyResolvedPath(shortPath));
}
export async function createFixture(tree) {
return await _createFixture(
{
'package.json': '{}',
...tree,
},
{
tempDir: tempFixturesDir,
},
);
}
export function createRequestAndResponse(reqOptions = {}) {

View file

@ -1,6 +1,5 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createContainer } from '../../../dist/core/dev/container.js';
import { createLoader } from '../../../dist/core/module-loader/index.js';
import { createRouteManifest } from '../../../dist/core/routing/index.js';
@ -12,13 +11,13 @@ import testAdapter from '../../test-adapter.js';
import {
createAstroModule,
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
async function createDevPipeline(overrides = {}) {
const settings = overrides.settings ?? (await createBasicSettings({ root: '/' }));
async function createDevPipeline(overrides = {}, root) {
const settings = overrides.settings ?? (await createBasicSettings({ root }));
const loader = overrides.loader ?? createLoader();
const manifest = createDevelopmentManifest(settings);
@ -28,7 +27,12 @@ async function createDevPipeline(overrides = {}) {
describe('vite-plugin-astro-server', () => {
describe('request', () => {
it('renders a request', async () => {
const pipeline = await createDevPipeline({
const fixture = await createFixture({
// Note that the content doesn't matter here because we are using a custom loader.
'/src/pages/index.astro': '',
});
const pipeline = await createDevPipeline(
{
loader: createLoader({
import(id) {
if (id === '\0astro-internal:middleware') {
@ -40,19 +44,14 @@ describe('vite-plugin-astro-server', () => {
return createAstroModule(Page);
},
}),
});
},
fixture.path,
);
const controller = createController({ loader: pipeline.loader });
const { req, res, text } = createRequestAndResponse();
const fs = createFs(
{
// Note that the content doesn't matter here because we are using a custom loader.
'/src/pages/index.astro': '',
},
'/',
);
const manifestData = createRouteManifest(
{
fsMod: fs,
cwd: fixture.path,
settings: pipeline.settings,
},
defaultLogger,
@ -82,7 +81,6 @@ describe('vite-plugin-astro-server', () => {
let settings;
before(async () => {
const root = new URL('../../fixtures/api-routes/', import.meta.url);
const fileSystem = {
'/src/pages/url.astro': `{Astro.request.url}`,
'/src/pages/prerendered.astro': `---
@ -90,14 +88,13 @@ describe('vite-plugin-astro-server', () => {
---
{Astro.request.url}`,
};
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});

View file

@ -1,16 +1,14 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createContainer } from '../../../dist/core/dev/container.js';
import testAdapter from '../../test-adapter.js';
import {
createBasicSettings,
createFs,
createFixture,
createRequestAndResponse,
defaultLogger,
} from '../test-utils.js';
const root = new URL('../../fixtures/api-routes/', import.meta.url);
const fileSystem = {
'/src/pages/index.js': `export const GET = () => {
const headers = new Headers();
@ -51,14 +49,13 @@ describe('endpoints', () => {
let settings;
before(async () => {
const fs = createFs(fileSystem, root);
const fixture = await createFixture(fileSystem);
settings = await createBasicSettings({
root: fileURLToPath(root),
root: fixture.path,
output: 'server',
adapter: testAdapter(),
});
container = await createContainer({
fs,
settings,
logger: defaultLogger,
});

View file

@ -0,0 +1,24 @@
diff --git a/dist/index.d.mts b/dist/index.d.mts
index be5b88e034211892ba079c3c5c5c6f5d5f767cd4..55dcc0e99c66719d5fa68d4713b02c1919deae19 100644
--- a/dist/index.d.mts
+++ b/dist/index.d.mts
@@ -61,6 +61,10 @@ type Api = ApiBase & {
type FileTree = {
[path: string]: string | FileTree | ((api: Api) => string | Symlink);
};
-declare const createFixture: (source?: string | FileTree) => Promise<FsFixture>;
+type CreateFixtureOptions = {
+ // An absolute path to a different directory than `os.tmpdir()`
+ tempDir?: string
+}
+declare const createFixture: (source?: string | FileTree, opts?: CreateFixtureOptions) => Promise<FsFixture>;
-export { type FileTree, FsFixture, createFixture };
+export { type FileTree, FsFixture, CreateFixtureOptions, createFixture };
diff --git a/dist/index.mjs b/dist/index.mjs
index cd6cab3beebf3f38fe4f1e2a9c58aff2b87258f7..ad24d852a357fd582f9e83ac20cb73bfbcb9bfc0 100755
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -1 +1 @@
-import s from"fs/promises";import o from"path";import y from"fs";import m from"os";typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class w{path;constructor(t){this.path=t}getPath(...t){return o.join(this.path,...t)}exists(t=""){return s.access(this.getPath(t)).then(()=>!0,()=>!1)}rm(t=""){return s.rm(this.getPath(t),{recursive:!0,force:!0})}writeFile(t,r){return s.writeFile(this.getPath(t),r)}writeJson(t,r){return this.writeFile(t,JSON.stringify(r,null,2))}readFile(t,r){return s.readFile(this.getPath(t),r)}async[Symbol.asyncDispose](){await this.rm()}}const g=y.realpathSync(m.tmpdir()),b=`fs-fixture-${Date.now()}`;let l=0;const P=()=>(l+=1,l);class h{target;type;path;constructor(t,r){this.target=t,this.type=r}}const u=(i,t,r)=>{const e=[];for(const n in i){if(!Object.hasOwn(i,n))continue;const c=o.join(t,n);let a=i[n];if(typeof a=="function"){const f=Object.assign(Object.create(r),{filePath:c}),p=a(f);if(p instanceof h){p.path=c,e.push(p);continue}else a=p}typeof a=="string"?e.push({path:c,content:a}):e.push(...u(a,c,r))}return e},d=async i=>{const t=o.join(g,`${b}-${P()}/`);if(await s.mkdir(t,{recursive:!0}),i){if(typeof i=="string")await s.cp(i,t,{recursive:!0});else if(typeof i=="object"){const r={fixturePath:t,getPath:(...e)=>o.join(t,...e),symlink:(e,n)=>new h(e,n)};await Promise.all(u(i,t,r).map(async e=>{await s.mkdir(o.dirname(e.path),{recursive:!0}),e instanceof h?await s.symlink(e.target,e.path,e.type):await s.writeFile(e.path,e.content)}))}}return new w(t)};export{d as createFixture};
+import s from"fs/promises";import o from"path";import y from"fs";import m from"os";typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class w{path;constructor(t){this.path=t}getPath(...t){return o.join(this.path,...t)}exists(t=""){return s.access(this.getPath(t)).then(()=>!0,()=>!1)}rm(t=""){return s.rm(this.getPath(t),{recursive:!0,force:!0})}writeFile(t,r){return s.writeFile(this.getPath(t),r)}writeJson(t,r){return this.writeFile(t,JSON.stringify(r,null,2))}readFile(t,r){return s.readFile(this.getPath(t),r)}async[Symbol.asyncDispose](){await this.rm()}}const g=y.realpathSync(m.tmpdir()),b=`fs-fixture-${Date.now()}`;let l=0;const P=()=>(l+=1,l);class h{target;type;path;constructor(t,r){this.target=t,this.type=r}}const u=(i,t,r)=>{const e=[];for(const n in i){if(!Object.hasOwn(i,n))continue;const c=o.join(t,n);let a=i[n];if(typeof a=="function"){const f=Object.assign(Object.create(r),{filePath:c}),p=a(f);if(p instanceof h){p.path=c,e.push(p);continue}else a=p}typeof a=="string"?e.push({path:c,content:a}):e.push(...u(a,c,r))}return e},d=async (i, opts)=>{const t=o.join(opts?.tempDir ?? g,`${b}-${P()}/`);if(await s.mkdir(t,{recursive:!0}),i){if(typeof i=="string")await s.cp(i,t,{recursive:!0});else if(typeof i=="object"){const r={fixturePath:t,getPath:(...e)=>o.join(t,...e),symlink:(e,n)=>new h(e,n)};await Promise.all(u(i,t,r).map(async e=>{await s.mkdir(o.dirname(e.path),{recursive:!0}),e instanceof h?await s.symlink(e.target,e.path,e.type):await s.writeFile(e.path,e.content)}))}}return new w(t)};export{d as createFixture};

View file

@ -4,6 +4,11 @@ settings:
autoInstallPeers: false
excludeLinksFromLockfile: false
patchedDependencies:
fs-fixture@2.4.0:
hash: hvkuaks2ic76pdflr3iifgi4ku
path: patches/fs-fixture@2.4.0.patch
importers:
.:
@ -703,15 +708,15 @@ importers:
expect-type:
specifier: ^1.1.0
version: 1.1.0
fs-fixture:
specifier: ^2.4.0
version: 2.4.0(patch_hash=hvkuaks2ic76pdflr3iifgi4ku)
mdast-util-mdx:
specifier: ^3.0.0
version: 3.0.0
mdast-util-mdx-jsx:
specifier: ^3.1.3
version: 3.1.3
memfs:
specifier: ^4.14.0
version: 4.14.0
node-mocks-http:
specifier: ^1.16.1
version: 1.16.1(@types/node@18.19.50)
@ -2665,12 +2670,6 @@ importers:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/content-mixed-errors:
dependencies:
astro:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/content-ssr-integration:
dependencies:
'@astrojs/mdx':
@ -4172,6 +4171,15 @@ importers:
specifier: workspace:*
version: link:../../..
packages/astro/test/units/_temp-fixtures:
dependencies:
'@astrojs/mdx':
specifier: workspace:*
version: link:../../../../integrations/mdx
astro:
specifier: workspace:*
version: link:../../..
packages/create-astro:
dependencies:
'@astrojs/cli-kit':
@ -6528,24 +6536,6 @@ packages:
resolution: {integrity: sha512-n5JEf16Wr4mdkRMZ8wMP/wN9/sHmTjRPbouXjJH371mZ2LEGDl72t8tEsMRNFerQN/QJtivOxqK1frdGa4QK5Q==}
engines: {node: '>=10'}
'@jsonjoy.com/base64@1.1.2':
resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==}
engines: {node: '>=10.0'}
peerDependencies:
tslib: '2'
'@jsonjoy.com/json-pack@1.1.0':
resolution: {integrity: sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==}
engines: {node: '>=10.0'}
peerDependencies:
tslib: '2'
'@jsonjoy.com/util@1.3.0':
resolution: {integrity: sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==}
engines: {node: '>=10.0'}
peerDependencies:
tslib: '2'
'@libsql/client@0.14.0':
resolution: {integrity: sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q==}
@ -8274,6 +8264,10 @@ packages:
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
engines: {node: '>=6 <7 || >=8'}
fs-fixture@2.4.0:
resolution: {integrity: sha512-aFoTWGj288IEOdXBeesdcbdMvRtExbqpOp1SCjE3nRdlT7vBBCD6bf76C9FCq8/6pIPSo56P7+HeT9zT/n8rMA==}
engines: {node: '>=18.0.0'}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -8475,10 +8469,6 @@ packages:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
hyperdyperid@1.2.0:
resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==}
engines: {node: '>=10.18'}
hyperid@3.3.0:
resolution: {integrity: sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==}
@ -8924,10 +8914,6 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
memfs@4.14.0:
resolution: {integrity: sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==}
engines: {node: '>= 4.0.0'}
merge-anything@5.1.7:
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
engines: {node: '>=12.13'}
@ -10243,12 +10229,6 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
thingies@1.21.0:
resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==}
engines: {node: '>=10.18'}
peerDependencies:
tslib: ^2
timestring@6.0.0:
resolution: {integrity: sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==}
engines: {node: '>=8'}
@ -10302,12 +10282,6 @@ packages:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
tree-dump@1.0.2:
resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==}
engines: {node: '>=10.0'}
peerDependencies:
tslib: '2'
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
@ -11997,22 +11971,6 @@ snapshots:
'@jsdevtools/rehype-toc@3.0.2': {}
'@jsonjoy.com/base64@1.1.2(tslib@2.7.0)':
dependencies:
tslib: 2.7.0
'@jsonjoy.com/json-pack@1.1.0(tslib@2.7.0)':
dependencies:
'@jsonjoy.com/base64': 1.1.2(tslib@2.7.0)
'@jsonjoy.com/util': 1.3.0(tslib@2.7.0)
hyperdyperid: 1.2.0
thingies: 1.21.0(tslib@2.7.0)
tslib: 2.7.0
'@jsonjoy.com/util@1.3.0(tslib@2.7.0)':
dependencies:
tslib: 2.7.0
'@libsql/client@0.14.0':
dependencies:
'@libsql/core': 0.14.0
@ -13833,6 +13791,8 @@ snapshots:
jsonfile: 4.0.0
universalify: 0.1.2
fs-fixture@2.4.0(patch_hash=hvkuaks2ic76pdflr3iifgi4ku): {}
fsevents@2.3.2:
optional: true
@ -14145,8 +14105,6 @@ snapshots:
human-signals@5.0.0: {}
hyperdyperid@1.2.0: {}
hyperid@3.3.0:
dependencies:
buffer: 5.7.1
@ -14709,13 +14667,6 @@ snapshots:
media-typer@0.3.0: {}
memfs@4.14.0:
dependencies:
'@jsonjoy.com/json-pack': 1.1.0(tslib@2.7.0)
'@jsonjoy.com/util': 1.3.0(tslib@2.7.0)
tree-dump: 1.0.2(tslib@2.7.0)
tslib: 2.7.0
merge-anything@5.1.7:
dependencies:
is-what: 4.1.16
@ -16344,10 +16295,6 @@ snapshots:
dependencies:
any-promise: 1.3.0
thingies@1.21.0(tslib@2.7.0):
dependencies:
tslib: 2.7.0
timestring@6.0.0: {}
tinybench@2.9.0: {}
@ -16387,10 +16334,6 @@ snapshots:
dependencies:
punycode: 2.3.1
tree-dump@1.0.2(tslib@2.7.0):
dependencies:
tslib: 2.7.0
trim-lines@3.0.1: {}
trough@2.2.0: {}

View file

@ -1,9 +1,11 @@
packages:
- 'packages/**/*'
- 'packages/astro/test/units/_temp-fixtures'
- 'examples/**/*'
- 'smoke/**/*'
- 'scripts'
- 'benchmark'
- 'benchmark/packages/*'
# Below excludes are only for Turbo because it doesn't respect gitignore like pnpm does
- '!packages/astro/test/units/_temp-fixtures/*'
- '!**/.vercel/**'

View file

@ -25,6 +25,8 @@ export default async function test() {
timeout: { type: 'string', alias: 't' },
// Test setup file
setup: { type: 'string', alias: 's' },
// Test teardown file
teardown: { type: 'string' },
},
});
@ -59,6 +61,10 @@ export default async function test() {
files.push(tempTestFile);
}
const teardownModule = args.values.teardown
? await import(pathToFileURL(path.resolve(args.values.teardown)).toString())
: undefined;
// https://nodejs.org/api/test.html#runoptions
run({
files,
@ -74,6 +80,10 @@ export default async function test() {
// so we set it here manually
process.exitCode = 1;
})
.on('end', () => {
const testPassed = process.exitCode === 0 || process.exitCode === undefined;
teardownModule?.default(testPassed);
})
.pipe(new spec())
.pipe(process.stdout);
}