'use strict'; function reqFromCwd(id) { // Resolve as if we required from the project root (where mocha is installed) return require(require.resolve(id, { paths: [process.cwd()] })); } const Mocha = reqFromCwd('mocha'); const bdd = reqFromCwd('mocha/lib/interfaces/bdd'); function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function captureLocationForFile(file, stackStartFn) { const err = new Error(); if (Error.captureStackTrace) Error.captureStackTrace(err, stackStartFn); const lines = String(err.stack || '').split('\n').slice(1); // Prefer exact match for current test file (absolute/normalized path as passed by mocha) const fileRe = new RegExp(`\\(?(${escapeRegExp(file)}):(\\d+):(\\d+)\\)?$`); for (const line of lines) { const m = line.match(fileRe); if (m) return { file: m[1], line: Number(m[2]), column: Number(m[3]) }; } // Fallback: any frame containing this file path const anyRe = /\(?(.+?):(\d+):(\d+)\)?$/; for (const line of lines) { const m = line.match(anyRe); if (m && m[1] && m[1].includes(file)) { return { file: m[1], line: Number(m[2]), column: Number(m[3]) }; } } return { file, line: null, column: null }; } function markTest(test, loc, pendingType) { if (!test) return; test._akLocation = loc; if (pendingType) test._akPendingType = pendingType; // 'todo' | 'skip' } function markSuite(suite, loc, pendingType) { if (!suite) return; suite._akLocation = loc; if (pendingType) suite._akPendingType = pendingType; // 'skip' } // Register custom interface name Mocha.interfaces['bdd-with-location'] = function (suite) { // First install standard BDD globals bdd(suite); // Then wrap globals on pre-require (Mocha passes "file" for each loaded test file) suite.on('pre-require', function (context, file) { if (!context || !file) return; function loc(stackStartFn) { return captureLocationForFile(file, stackStartFn); } // ------------------------- // Wrap `it` // ------------------------- const origIt = context.it; function itWrapped(title, fn) { const test = origIt.call(this, title, fn); // "todo" detection: it('x') without a function const pendingType = typeof fn !== 'function' ? 'todo' : null; markTest(test, loc(itWrapped), pendingType); return test; } // Copy properties like `.only`/`.skip` added by Mocha Object.assign(itWrapped, origIt); if (typeof origIt.only === 'function') { itWrapped.only = function (title, fn) { const test = origIt.only.call(this, title, fn); const pendingType = typeof fn !== 'function' ? 'todo' : null; markTest(test, loc(itWrapped.only), pendingType); return test; }; } if (typeof origIt.skip === 'function') { itWrapped.skip = function (title, fn) { const test = origIt.skip.call(this, title, fn); // Explicit skip markTest(test, loc(itWrapped.skip), 'skip'); return test; }; } context.it = itWrapped; if (context.specify) context.specify = itWrapped; // ------------------------- // Wrap `describe` // ------------------------- const origDescribe = context.describe; function describeWrapped(title, fn) { const s = origDescribe.call(this, title, fn); markSuite(s, loc(describeWrapped), null); return s; } Object.assign(describeWrapped, origDescribe); if (typeof origDescribe.only === 'function') { describeWrapped.only = function (title, fn) { const s = origDescribe.only.call(this, title, fn); markSuite(s, loc(describeWrapped.only), null); return s; }; } if (typeof origDescribe.skip === 'function') { describeWrapped.skip = function (title, fn) { const s = origDescribe.skip.call(this, title, fn); // Suite skip markSuite(s, loc(describeWrapped.skip), 'skip'); return s; }; } context.describe = describeWrapped; if (context.context) context.context = describeWrapped; }); }; module.exports = Mocha.interfaces['bdd-with-location'];