Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/astro/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ process.env.ASTRO_TELEMETRY_DISABLED = true;
* @typedef {import('../src/cli/check/index').CheckPayload} CheckPayload
* @typedef {import('http').IncomingMessage} NodeRequest
* @typedef {import('http').ServerResponse} NodeResponse
* `RequestHandler` is defined in `@astrojs/node` so we cannot import it directly.
* See https://github.com/withastro/astro/blob/astro@6.0.0/packages/integrations/node/src/types.ts#L44-L50
* @typedef {(req: NodeRequest, res: NodeResponse, next?: (err?: unknown) => void, locals?: object) => void | Promise<void>} RequestHandler
*
* `startServer` is defined in `@astrojs/node` so we cannot import it directly.
* See https://github.com/withastro/astro/blob/astro@6.0.0/packages/integrations/node/src/server.ts#L21
* @typedef {PreviewServer & { server: import('http').Server }} AdapterServer
* @typedef {() => ({server: AdapterServer, stop: Promise<void>})} AdapterStartServer
*
* @typedef {Object} Fixture
* @property {(extraInlineConfig?: Parameters<typeof build>[0], options?: Parameters<typeof build>[1]) => Promise<void>} build
Expand All @@ -43,7 +50,8 @@ process.env.ASTRO_TELEMETRY_DISABLED = true;
* @property {() => Promise<void>} clean
* @property {(streaming?: boolean) => Promise<App>} loadTestAdapterApp
* @property {(streaming?: boolean) => Promise<App>} loadSelfAdapterApp
* @property {() => Promise<(req: NodeRequest, res: NodeResponse) => void>} loadNodeAdapterHandler
* @property {() => Promise<{ handler: RequestHandler; startServer: AdapterStartServer }>} loadAdapterEntryModule
* @property {() => Promise<RequestHandler>} loadNodeAdapterHandler
* @property {(timeout?: number) => Promise<void>} onNextDataStoreChange
* @property {typeof check} check
* @property {typeof sync} sync
Expand Down
5 changes: 3 additions & 2 deletions packages/astro/test/units/test-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EventEmitter } from 'node:events';
import { fileURLToPath } from 'node:url';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { createFixture as _createFixture, type FileTree } from 'fs-fixture';
import httpMocks from 'node-mocks-http';
import { getDefaultClientDirectives } from '../../dist/core/client-directive/index.js';
Expand Down Expand Up @@ -39,10 +40,10 @@ export async function createFixture(tree: FileTree) {
}

export function createRequestAndResponse(reqOptions: httpMocks.RequestOptions = {}) {
const req = httpMocks.createRequest(reqOptions);
const req: IncomingMessage = httpMocks.createRequest(reqOptions);
req.headers.host ||= 'localhost';

const res = httpMocks.createResponse({
const res: ServerResponse<IncomingMessage> = httpMocks.createResponse({
eventEmitter: EventEmitter,
req,
});
Expand Down
3 changes: 2 additions & 1 deletion packages/integrations/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"dev": "astro-scripts dev \"src/**/*.ts\"",
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"test": "astro-scripts test \"test/**/*.test.js\" \"test/**/*.test.ts\"",
"test": "astro-scripts test \"test/**/*.test.ts\"",
"typecheck:tests": "tsc --build tsconfig.test.json"
},
"dependencies": {
Expand All @@ -44,6 +44,7 @@
"devDependencies": {
"@fastify/middie": "^9.1.0",
"@fastify/static": "^9.0.0",
"@types/express": "^5.0.6",
"@types/node": "^22.10.6",
"@types/send": "^1.2.1",
"@types/server-destroy": "^1.0.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import * as assert from 'node:assert/strict';
import crypto from 'node:crypto';
import { after, before, describe, it } from 'node:test';
import type { PreviewServer } from '../../../astro/src/types/public/preview.js';
import nodejs from '../dist/index.js';
import { createRequestAndResponse, loadFixture } from './test-utils.js';
import { createRequestAndResponse, type Fixture, loadFixture } from './test-utils.ts';

describe('API routes', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
/** @type {import('../../../astro/src/types/public/preview.js').PreviewServer} */
let previewServer;
/** @type {URL} */
let baseUri;
let fixture: Fixture;
let previewServer: PreviewServer;
let baseUri: URL;

before(async () => {
fixture = await loadFixture({
Expand All @@ -26,7 +24,7 @@ describe('API routes', () => {
after(() => previewServer.stop());

it('Can get the request body', async () => {
const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, done } = createRequestAndResponse({
method: 'POST',
url: '/recipes',
Expand All @@ -48,7 +46,7 @@ describe('API routes', () => {
});

it('Can get binary data', async () => {
const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, done } = createRequestAndResponse({
method: 'POST',
Expand All @@ -67,7 +65,7 @@ describe('API routes', () => {
});

it('Can post large binary data', async () => {
const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, done } = createRequestAndResponse({
method: 'POST',
Expand All @@ -76,7 +74,7 @@ describe('API routes', () => {

handler(req, res);

let expectedDigest = null;
let expectedDigest: Buffer | null = null;
req.once('async_iterator', () => {
// Send 256MB of garbage data in 256KB chunks. This should be fast (< 1sec).
let remainingBytes = 256 * 1024 * 1024;
Expand All @@ -96,11 +94,11 @@ describe('API routes', () => {
});

const [out] = await done;
assert.deepEqual(new Uint8Array(out.buffer), new Uint8Array(expectedDigest));
assert.deepEqual(new Uint8Array(out.buffer), new Uint8Array(expectedDigest!));
});

it('Can bail on streaming', async () => {
const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, done } = createRequestAndResponse({
url: '/streaming',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import type { PreviewServer } from '../../../astro/src/types/public/preview.js';
import * as cheerio from 'cheerio';
import nodejs from '../dist/index.js';
import { loadFixture } from './test-utils.js';
import { fileURLToPath } from 'node:url';
import { type Fixture, loadFixture } from './test-utils.ts';

describe('Assets', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let devPreview;
let fixture: Fixture;
let devPreview: PreviewServer;

before(async () => {
const root = new URL('./fixtures/image/', import.meta.url);
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('Assets', () => {
const $ = cheerio.load(html);

// Fetch the asset
const fileURL = $('a').attr('href');
const fileURL = $('a').attr('href')!;
response = await fixture.fetch(fileURL);
cacheControl = response.headers.get('cache-control');
assert.equal(cacheControl, 'public, max-age=31536000, immutable');
Expand All @@ -50,7 +50,7 @@ describe('Assets', () => {
let response = await fixture.fetch('/text-file');
const html = await response.text();
const $ = cheerio.load(html);
const fileURL = $('a').attr('href');
const fileURL = $('a').attr('href')!;

// Send a request with a malformed If-Match header that won't match the ETag
response = await fixture.fetch(fileURL, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import type { PreviewServer } from '../../../astro/src/types/public/preview.js';
import nodejs from '../dist/index.js';
import { loadFixture } from './test-utils.js';
import { type Fixture, loadFixture } from './test-utils.ts';

describe('Bad URLs', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let devPreview;
let fixture: Fixture;
let devPreview: PreviewServer;

before(async () => {
fixture = await loadFixture({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import nodejs from '../dist/index.js';
import { createRequestAndResponse, loadFixture } from './test-utils.js';
import { createRequestAndResponse, type Fixture, loadFixture } from './test-utils.ts';

describe('Encoded Pathname', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let fixture: Fixture;

before(async () => {
fixture = await loadFixture({
Expand All @@ -17,7 +16,7 @@ describe('Encoded Pathname', () => {
});

it('Can get an Astro file', async () => {
const { handler } = await import('./fixtures/encoded/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, text } = createRequestAndResponse({
url: '/什么',
});
Expand All @@ -30,7 +29,7 @@ describe('Encoded Pathname', () => {
});

it('Can get a Markdown file', async () => {
const { handler } = await import('./fixtures/encoded/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, text } = createRequestAndResponse({
url: '/blog/什么',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
import nodejs from '../dist/index.js';
import { loadFixture } from './test-utils.js';
import { type Fixture, loadFixture } from './test-utils.ts';

describe('Errors', () => {
/** @type {import('./test-utils.js').Fixture} */
let fixture;
let fixture: Fixture;

before(async () => {
fixture = await loadFixture({
Expand All @@ -16,15 +15,6 @@ describe('Errors', () => {
});
await fixture.build();
});
let devPreview;

// The two tests that need the server to run are skipped
// before(async () => {
// devPreview = await fixture.preview();
// });
after(async () => {
await devPreview?.stop();
});

it('rejected promise in template', {
skip: true,
Expand All @@ -43,9 +33,8 @@ describe('Errors', () => {
}, async () => {
const result = ['<!DOCTYPE html><h1>Astro</h1> 1', 'Internal server error'];

/** @type {Response} */
const res = await fixture.fetch('/generator');
const reader = res.body.getReader();
const res: Response = await fixture.fetch('/generator');
const reader = res.body!.getReader();
const decoder = new TextDecoder();
const chunk1 = await reader.read();
const chunk2 = await reader.read();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import nodejs from '../dist/index.js';
import { createRequestAndResponse, loadFixture } from './test-utils.js';
import { createRequestAndResponse, type Fixture, loadFixture } from './test-utils.ts';

describe('Node Adapter Headers', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let fixture: Fixture;

describe('Node Adapter Headers', () => {
describe('streaming', () => {
before(async () => {
fixture = await loadFixture({
Expand Down Expand Up @@ -131,7 +130,7 @@ describe('Node Adapter Headers', () => {

// TODO: needs e2e tests to check real headers
it('sends several chunks', async () => {
const { handler } = await import('./fixtures/headers/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, done } = createRequestAndResponse({
method: 'GET',
Expand Down Expand Up @@ -159,7 +158,7 @@ describe('Node Adapter Headers', () => {

// TODO: needs e2e tests to check real headers
it('sends a single chunk', async () => {
const { handler } = await import('./fixtures/headers/dist/server/entry.mjs?cachebust=0');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, done } = createRequestAndResponse({
method: 'GET',
Expand All @@ -176,8 +175,8 @@ describe('Node Adapter Headers', () => {
});
});

async function runTest(url, expectedHeaders) {
const { handler } = await import('./fixtures/headers/dist/server/entry.mjs');
async function runTest(url: string, expectedHeaders: Record<string, string | string[]>) {
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, done } = createRequestAndResponse({
method: 'GET',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import * as assert from 'node:assert/strict';
import { cp, rm } from 'node:fs/promises';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import type { PreviewServer } from '../../../astro/src/types/public/preview.js';
import { inferRemoteSize } from 'astro/assets/utils/inferRemoteSize.js';
import * as cheerio from 'cheerio';
import nodejs from '../dist/index.js';
import { loadFixture } from './test-utils.js';
import { fileURLToPath } from 'node:url';
import { type Fixture, loadFixture } from './test-utils.ts';

describe('Image endpoint', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let devPreview;
let fixture: Fixture;
let devPreview: PreviewServer;

before(async () => {
const root = new URL('./fixtures/image/', import.meta.url);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import nodejs from '../dist/index.js';
import { createRequestAndResponse, loadFixture } from './test-utils.js';
import { createRequestAndResponse, type Fixture, loadFixture } from './test-utils.ts';

describe('API routes', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let fixture: Fixture;

before(async () => {
fixture = await loadFixture({
Expand All @@ -17,7 +16,7 @@ describe('API routes', () => {
});

it('Can use locals added by node middleware', async () => {
const { handler } = await import('./fixtures/locals/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, text } = createRequestAndResponse({
url: '/from-node-middleware',
});
Expand All @@ -33,11 +32,12 @@ describe('API routes', () => {
});

it('Throws an error when provided non-objects as locals', async () => {
const { handler } = await import('./fixtures/locals/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, done } = createRequestAndResponse({
url: '/from-node-middleware',
});

// @ts-expect-error - intentionally passing a non-object to test error handling
handler(req, res, undefined, 'locals');
req.send();

Expand All @@ -46,7 +46,7 @@ describe('API routes', () => {
});

it('Can use locals added by astro middleware', async () => {
const { handler } = await import('./fixtures/locals/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();

const { req, res, text } = createRequestAndResponse({
url: '/from-astro-middleware',
Expand All @@ -61,7 +61,7 @@ describe('API routes', () => {
});

it('Can access locals in API', async () => {
const { handler } = await import('./fixtures/locals/dist/server/entry.mjs');
const handler = await fixture.loadNodeAdapterHandler();
const { req, res, done } = createRequestAndResponse({
method: 'POST',
url: '/api',
Expand Down
Loading
Loading