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
5 changes: 5 additions & 0 deletions .changeset/migrate-ava-to-vitest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@drupal-kit/core": patch
---

Migrate test runner from AVA to Vitest
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ The update script installs Node 20 via nvm and prepends it to PATH in `~/.bashrc

Turborepo handles dependency ordering: `@drupal-kit/core` must build before downstream plugins. Always use `pnpm build` (via turbo) rather than building individual packages manually, unless you know the dependency graph.

### Tests use MSW mocks
### Tests use Vitest and MSW mocks

All tests mock HTTP via MSW (Mock Service Worker). No real Drupal instance or network access is required for the test suite.
Packages run tests with Vitest (`vitest run`). Shared config lives in `@drupal-kit/config-vitest`. All HTTP is mocked via MSW (Mock Service Worker). No real Drupal instance or network access is required for the test suite.

### Shared skills

Expand Down
16 changes: 16 additions & 0 deletions packages/config/vitest/create-vitest-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "vitest/config";

export function createVitestConfig() {
return defineConfig({
test: {
environment: "node",
include: ["tests/**/*.test.ts"],
sequence: {
concurrent: false,
},
snapshotFormat: {
printBasicPrototype: false,
},
},
});
}
14 changes: 14 additions & 0 deletions packages/config/vitest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@drupal-kit/config-vitest",
"version": "0.13.4",
"type": "module",
"private": true,
"devDependencies": {
"@drupal-kit/config-typescript": "workspace:0.13.4",
"typescript": "5.4.5",
"vitest": "^3.2.4"
},
"exports": {
"./vitest": "./create-vitest-config.ts"
}
}
7 changes: 7 additions & 0 deletions packages/config/vitest/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../typescript/base.json",
"compilerOptions": {
"noEmit": true
},
"include": ["*.ts"]
}
20 changes: 5 additions & 15 deletions packages/consumers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,29 @@
},
"devDependencies": {
"@drupal-kit/config-typescript": "workspace:0.13.4",
"@drupal-kit/config-vitest": "workspace:0.13.4",
"@drupal-kit/eslint-config": "workspace:0.13.4",
"@drupal-kit/types": "workspace:0.13.4",
"@rollup/plugin-typescript": "^11.1.1",
"@swc/core": "^1.6.5",
"ava": "^6.1.3",
"esbuild": "^0.27.3",
"msw": "^2.14.6",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.1.1",
"ts-node": "^10.9.2",
"typescript": "5.4.5"
"typescript": "5.4.5",
"vitest": "^3.2.4"
},
"scripts": {
"build": "rollup -c",
"lint": "eslint --ext .ts,.tsx src --max-warnings 0",
"test": "NODE_NO_WARNINGS=1 ava",
"test": "NODE_NO_WARNINGS=1 vitest run",
"test:watch": "NODE_NO_WARNINGS=1 vitest",
"typecheck": "pnpm run '/(typecheck:.*)/'",
"typecheck:src": "tsc --project ./tsconfig.json",
"typecheck:tests": "tsc --project ./tsconfig.tests.json"
},
"ava": {
"files": [
"tests/**/*.test.ts"
],
"extensions": {
"ts": "module"
},
"nodeArguments": [
"--loader",
"ts-node/esm"
]
},
"exports": {
".": {
"types": "./dist/index.d.ts",
Expand Down
149 changes: 70 additions & 79 deletions packages/consumers/tests/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test from "ava";
import { afterAll, afterEach, beforeAll, expect, test } from "vitest";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { Drupalkit, DrupalkitOptions } from "@drupal-kit/core";
Expand All @@ -7,15 +7,15 @@ import { DrupalkitConsumers } from "../src/index.js";

const server = setupServer();

test.before(() => {
beforeAll(() => {
server.listen();
});

test.afterEach(() => {
afterEach(() => {
server.resetHandlers();
});

test.after(() => {
afterAll(() => {
server.close();
});

Expand All @@ -36,12 +36,12 @@ const createDrupalkit = (
});
};

test.serial("Add consumer id to request", async (t) => {
t.plan(2);
test("Add consumer id to request", async () => {
expect.assertions(2);

server.use(
http.get("*", ({ request }) => {
t.is(request.headers.get("X-Consumer-ID"), CONSUMER_ID);
expect(request.headers.get("X-Consumer-ID")).toBe(CONSUMER_ID);

return HttpResponse.text();
}),
Expand All @@ -56,38 +56,35 @@ test.serial("Add consumer id to request", async (t) => {
method: "GET",
});

t.assert(result.ok);
expect(result.ok).toBeTruthy();
});

test.serial(
"Do not add consumer id header if no value is supplied",
async (t) => {
t.plan(1);
test("Do not add consumer id header if no value is supplied", async () => {
expect.assertions(1);

server.use(
http.get("*", ({ request }) => {
t.is(request.headers.get("X-Consumer-ID"), null);
server.use(
http.get("*", ({ request }) => {
expect(request.headers.get("X-Consumer-ID")).toBe(null);

return HttpResponse.text();
}),
);
return HttpResponse.text();
}),
);

const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
});
const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
});

await drupalkit.request("/", {
method: "GET",
});
},
);
await drupalkit.request("/", {
method: "GET",
});
});

test.serial("Add consumer id with custom header name", async (t) => {
t.plan(1);
test("Add consumer id with custom header name", async () => {
expect.assertions(1);

server.use(
http.get("*", ({ request }) => {
t.is(request.headers.get("X-Custom-Consumer-ID"), CONSUMER_ID);
expect(request.headers.get("X-Custom-Consumer-ID")).toBe(CONSUMER_ID);

return HttpResponse.text();
}),
Expand All @@ -104,58 +101,52 @@ test.serial("Add consumer id with custom header name", async (t) => {
});
});

test.serial(
"Add consumer id to request - via deprecated consumerUUID",
async (t) => {
t.plan(2);
test("Add consumer id to request - via deprecated consumerUUID", async () => {
expect.assertions(2);

server.use(
http.get("*", ({ request }) => {
t.is(request.headers.get("X-Consumer-ID"), CONSUMER_ID);
server.use(
http.get("*", ({ request }) => {
expect(request.headers.get("X-Consumer-ID")).toBe(CONSUMER_ID);

return HttpResponse.text();
}),
);
return HttpResponse.text();
}),
);

const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
consumerUUID: CONSUMER_ID,
});
const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
consumerUUID: CONSUMER_ID,
});

const result = await drupalkit.request("/", {
method: "GET",
});
const result = await drupalkit.request("/", {
method: "GET",
});

t.assert(result.ok);
},
);

test.serial(
"Do not overwrite already existing consumer id header",
async (t) => {
const otherId = "other";
t.plan(2);

server.use(
http.get("*", ({ request }) => {
t.is(request.headers.get("X-Consumer-ID"), otherId);

return HttpResponse.text();
}),
);

const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
consumerUUID: CONSUMER_ID,
});

const result = await drupalkit.request("/", {
method: "GET",
headers: {
"X-Consumer-ID": otherId,
},
});

t.assert(result.ok);
},
);
expect(result.ok).toBeTruthy();
});

test("Do not overwrite already existing consumer id header", async () => {
const otherId = "other";
expect.assertions(2);

server.use(
http.get("*", ({ request }) => {
expect(request.headers.get("X-Consumer-ID")).toBe(otherId);

return HttpResponse.text();
}),
);

const drupalkit = createDrupalkit({
baseUrl: BASE_URL,
consumerUUID: CONSUMER_ID,
});

const result = await drupalkit.request("/", {
method: "GET",
headers: {
"X-Consumer-ID": otherId,
},
});

expect(result.ok).toBeTruthy();
});
3 changes: 3 additions & 0 deletions packages/consumers/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createVitestConfig } from "@drupal-kit/config-vitest/vitest";

export default createVitestConfig();
20 changes: 5 additions & 15 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,32 @@
},
"devDependencies": {
"@drupal-kit/config-typescript": "workspace:0.13.4",
"@drupal-kit/config-vitest": "workspace:0.13.4",
"@drupal-kit/eslint-config": "workspace:0.13.4",
"@drupal-kit/types": "workspace:0.13.4",
"@rollup/plugin-typescript": "^11.1.1",
"@swc/core": "^1.6.5",
"@types/qs": "~6.9.15",
"@wunderwerk/node-pkg-version-export": "1.0.0-beta.1",
"ava": "^6.1.3",
"esbuild": "^0.27.3",
"msw": "^2.14.6",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.1.1",
"ts-node": "^10.9.2",
"typescript": "5.4.5"
"typescript": "5.4.5",
"vitest": "^3.2.4"
},
"scripts": {
"build": "rollup -c",
"export-version": "node-pkg-version-export src/version.ts",
"lint": "eslint --ext .ts,.tsx src --max-warnings 0",
"test": "NODE_NO_WARNINGS=1 ava",
"test": "NODE_NO_WARNINGS=1 vitest run",
"test:watch": "NODE_NO_WARNINGS=1 vitest watch",
"typecheck": "pnpm run '/(typecheck:.*)/'",
"typecheck:src": "tsc --project ./tsconfig.json",
"typecheck:tests": "tsc --project ./tsconfig.tests.json"
},
"ava": {
"files": [
"tests/**/*.test.ts"
],
"extensions": {
"ts": "module"
},
"nodeArguments": [
"--loader",
"ts-node/esm"
]
},
"engines": {
"node": ">=18"
},
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/DrupalkitError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,17 @@ export class DrupalkitError<T = unknown> extends Error {
super(message);

// Maintains proper stack trace (only available on V8)
// @ts-expect-error - Error.captureStackTrace is not available in all browsers.
if (Error.captureStackTrace) {
// @ts-expect-error - Error.captureStackTrace is not available in all browsers.
Error.captureStackTrace(this, this.constructor);
const ErrorWithCapture = Error as ErrorConstructor & {
captureStackTrace?: (
error: Error,
constructor: new (...args: unknown[]) => object,
) => void;
};
if (ErrorWithCapture.captureStackTrace) {
ErrorWithCapture.captureStackTrace(
this,
this.constructor as new (...args: unknown[]) => object,
);
}

this.name = "HttpError";
Expand Down
Loading
Loading