Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions tegg/core/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"exports": {
".": "./src/index.ts",
"./runner": "./src/runner.ts",
"./setup": "./src/setup.ts",
"./shared": "./src/shared.ts",
"./package.json": "./package.json"
},
Expand All @@ -38,6 +39,7 @@
"exports": {
".": "./dist/index.js",
"./runner": "./dist/runner.js",
"./setup": "./dist/setup.js",
"./shared": "./dist/shared.js",
"./package.json": "./package.json"
}
Expand Down
44 changes: 44 additions & 0 deletions tegg/core/vitest/src/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { pathToFileURL } from 'node:url';

const nodeRequire = createRequire(import.meta.url);
const esmDirectoryCache = new Map<string, boolean>();

function isEsmFile(filePath: string): boolean {
if (filePath.endsWith('.mjs')) return true;
if (filePath.endsWith('.cjs')) return false;
Comment thread
elrrrrrrr marked this conversation as resolved.

let directory = path.dirname(filePath);
const visited: string[] = [];
while (directory !== path.dirname(directory)) {
const cached = esmDirectoryCache.get(directory);
if (cached !== undefined) {
for (const item of visited) esmDirectoryCache.set(item, cached);
return cached;
}

visited.push(directory);
const packageFile = path.join(directory, 'package.json');
if (fs.existsSync(packageFile)) {
let isEsm = false;
try {
isEsm = JSON.parse(fs.readFileSync(packageFile, 'utf8')).type === 'module';
} catch {
// Invalid package metadata follows Node's CommonJS-compatible fallback.
}
for (const item of visited) esmDirectoryCache.set(item, isEsm);
return isEsm;
}
directory = path.dirname(directory);
}
return false;
}

// This setup file runs inside the test ViteVM. CJS files must use Node's
// require cache, while ESM files stay in Vitest's transformed import graph.
globalThis.__EGG_MODULE_IMPORTER__ = async (filePath: string) => {
if (isEsmFile(filePath)) return import(pathToFileURL(filePath).href);
return nodeRequire(filePath);
};
5 changes: 5 additions & 0 deletions tegg/core/vitest/test/fixture_app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ describe('fixture demo app', () => {
});

it('injects ctx and getEggObject', async () => {
const loadedModule = (await globalThis.__EGG_MODULE_IMPORTER__?.(
path.join(__dirname, 'fixtures/apps/demo-app/modules/demo-module/HelloService.ts'),
)) as { HelloService: typeof HelloService };
assert.strictEqual(loadedModule.HelloService, HelloService);
Comment thread
elrrrrrrr marked this conversation as resolved.
Comment thread
elrrrrrrr marked this conversation as resolved.
Comment thread
elrrrrrrr marked this conversation as resolved.

const ctx = app.ctxStorage.getStore();
assert(ctx);
const helloService = (await ctx.getEggObject(HelloService)) as any;
Expand Down
2 changes: 1 addition & 1 deletion tegg/core/vitest/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default defineConfig({
environment: 'node',
include: ['test/**/*.test.ts'],
// Register TS loader (ts-node) before tests so Egg can load .ts via Module._extensions.
setupFiles: ['test/setup.ts'],
setupFiles: ['./src/setup.ts', 'test/setup.ts'],
// Custom runner for tegg context injection via enterWith + held beginModuleScope.
runner: './src/runner.ts',
},
Expand Down
6 changes: 6 additions & 0 deletions tools/egg-bin/src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,12 @@ export default class Test<T extends typeof Test> extends BaseCommand<T> {
}
if (!runner) {
debug('skip @eggjs/tegg-vitest/runner: self-test fixture or not resolvable');
} else {
const teggSetup = importResolve('@eggjs/tegg-vitest/setup', {
paths: [flags.base, import.meta.dirname],
});
setupFiles.unshift(teggSetup);
debug('auto add @eggjs/tegg-vitest/setup: %o', teggSetup);
Comment thread
elrrrrrrr marked this conversation as resolved.
}
Comment thread
elrrrrrrr marked this conversation as resolved.

return {
Expand Down
Loading