diff --git a/docs/01-writing-tests.md b/docs/01-writing-tests.md index 430e02276..f21c44546 100644 --- a/docs/01-writing-tests.md +++ b/docs/01-writing-tests.md @@ -267,14 +267,19 @@ test('context is unicorn', t => { }); ``` -## Retrieving test meta data +## Retrieving test metadata -Helper files can determine the filename of the test being run by reading `test.meta.file`. This eliminates the need to pass `__filename` from the test to helpers. +Access data about the currently loaded test file run by reading `test.meta`. + +Available properties: + +* `file`: path to the test file +* `snapshotDirectory`: directory where snapshots are stored ```js const test = require('ava'); -console.log('Test currently being run: ', test.meta.file); +console.log('Test file currently being run:', test.meta.file); ``` ## Reusing test logic through macros diff --git a/index.d.ts b/index.d.ts index 1fa3568a9..7dab001c0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -760,6 +760,9 @@ export interface TodoDeclaration { export interface MetaInterface { /** Path to the test file being executed. */ file: string; + + /** Directory where snapshots are stored. */ + snapshotDirectory: string; } /** Call to declare a test, or chain to declare hooks or test modifiers */ diff --git a/lib/runner.js b/lib/runner.js index 80f76791e..f1a221a36 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -54,7 +54,11 @@ class Runner extends Emittery { let hasStarted = false; let scheduledStart = false; const meta = Object.freeze({ - file: options.file + file: options.file, + get snapshotDirectory() { + const {file, snapshotDir: fixedLocation, projectDir} = options; + return snapshotManager.determineSnapshotDir({file, fixedLocation, projectDir}); + } }); this.chain = createChain((metadata, testArgs) => { // eslint-disable-line complexity if (hasStarted) { diff --git a/lib/snapshot-manager.js b/lib/snapshot-manager.js index e5fc9f95a..1871977c9 100644 --- a/lib/snapshot-manager.js +++ b/lib/snapshot-manager.js @@ -11,6 +11,7 @@ const md5Hex = require('md5-hex'); const convertSourceMap = require('convert-source-map'); const slash = require('slash'); const writeFileAtomic = require('write-file-atomic'); +const mem = require('mem'); const concordanceOptions = require('./concordance-options').snapshotManager; @@ -388,8 +389,26 @@ class Manager { } } -function determineSnapshotDir({file, fixedLocation, projectDir}) { +const resolveSourceFile = mem(file => { const testDir = path.dirname(file); + const buffer = tryRead(file); + if (!buffer) { + return file; // Assume the file is stubbed in our test suite. + } + + const source = buffer.toString(); + const converter = convertSourceMap.fromSource(source) || convertSourceMap.fromMapFileSource(source, testDir); + if (converter) { + const map = converter.toObject(); + const firstSource = `${map.sourceRoot || ''}${map.sources[0]}`; + return path.resolve(testDir, firstSource); + } + + return file; +}); + +const determineSnapshotDir = mem(({file, fixedLocation, projectDir}) => { + const testDir = path.dirname(resolveSourceFile(file)); if (fixedLocation) { const relativeTestLocation = path.relative(projectDir, testDir); return path.join(fixedLocation, relativeTestLocation); @@ -405,30 +424,13 @@ function determineSnapshotDir({file, fixedLocation, projectDir}) { } return testDir; -} +}, {cacheKey: ([{file}]) => file}); -function resolveSourceFile(file) { - const testDir = path.dirname(file); - const buffer = tryRead(file); - if (!buffer) { - return file; // Assume the file is stubbed in our test suite. - } - - const source = buffer.toString(); - const converter = convertSourceMap.fromSource(source) || convertSourceMap.fromMapFileSource(source, testDir); - if (converter) { - const map = converter.toObject(); - const firstSource = `${map.sourceRoot || ''}${map.sources[0]}`; - return path.resolve(testDir, firstSource); - } - - return file; -} +exports.determineSnapshotDir = determineSnapshotDir; function load({file, fixedLocation, projectDir, recordNewSnapshots, updating}) { - const sourceFile = resolveSourceFile(file); - const dir = determineSnapshotDir({file: sourceFile, fixedLocation, projectDir}); - const relFile = path.relative(projectDir, sourceFile); + const dir = determineSnapshotDir({file, fixedLocation, projectDir}); + const relFile = path.relative(projectDir, resolveSourceFile(file)); const name = path.basename(relFile); const reportFile = `${name}.md`; const snapFile = `${name}.snap`; diff --git a/package-lock.json b/package-lock.json index f43ebc78a..043485f18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5105,6 +5105,15 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5165,6 +5174,24 @@ "safe-buffer": "^5.1.2" } }, + "mem": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-6.0.1.tgz", + "integrity": "sha512-uIRYASflIsXqvKe+7aXbLrydaRzz4qiK6amqZDQI++eRtW3UoKtnDcGeCAOREgll7YMxO5E4VB9+3B0LFmy96g==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.0.0.tgz", + "integrity": "sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==", + "dev": true + } + } + }, "memory-fs": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", @@ -5938,6 +5965,12 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-event": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz", diff --git a/package.json b/package.json index 6a92e44ec..04c561f74 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "esm": "^3.2.25", "execa": "^4.0.0", "get-stream": "^5.1.0", + "mem": "^6.0.1", "nyc": "^15.0.1", "p-event": "^4.1.0", "proxyquire": "^2.1.3", diff --git a/test-tap/api.js b/test-tap/api.js index 1bcf5972e..8b025912e 100644 --- a/test-tap/api.js +++ b/test-tap/api.js @@ -30,12 +30,12 @@ function apiCreator(options = {}) { return instance; } -test('test.meta.file', t => { - const api = apiCreator(); +test('test.meta', t => { + const api = apiCreator({snapshotDir: 'snapshot-fixture'}); - return api.run({files: [path.join(__dirname, 'fixture/meta.js')]}) + return api.run({files: [path.join(__dirname, 'fixture', 'meta.js')]}) .then(runStatus => { - t.is(runStatus.stats.passedTests, 2); + t.is(runStatus.stats.passedTests, 3); }); }); diff --git a/test-tap/fixture/meta.js b/test-tap/fixture/meta.js index bf7f23247..3fee5b701 100644 --- a/test-tap/fixture/meta.js +++ b/test-tap/fixture/meta.js @@ -1,9 +1,14 @@ const {default: test, meta} = require('../..'); +test('meta is test.meta', t => { + t.is(meta, test.meta); +}); + test('meta.file', t => { t.is(meta.file, __filename); }); -test('test.meta.file', t => { - t.is(test.meta.file, __filename); +test('meta.snapshotDirectory', t => { + t.regex(meta.snapshotDirectory, /snapshot-fixture/); }); +