Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose snapshot directory through test.meta #2444

Merged
merged 14 commits into from
Apr 10, 2020
11 changes: 8 additions & 3 deletions docs/01-writing-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
6 changes: 5 additions & 1 deletion lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
46 changes: 24 additions & 22 deletions lib/snapshot-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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));
Comment on lines +432 to +433
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: In order to not have to export both determineSnapshotDir() and resolveSourceFile() I changed the signature of determineSnapshotDir() to take the "raw" file instead of the source file (it resolves the source file on its own).

Caveat: resolveSourceFile() is called twice for load() (once more to determine the relative path). But since it's memoized, I think it should be fine, no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine.

const name = path.basename(relFile);
const reportFile = `${name}.md`;
const snapFile = `${name}.snap`;
Expand Down
33 changes: 33 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions test-tap/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

Expand Down
9 changes: 7 additions & 2 deletions test-tap/fixture/meta.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
const {default: test, meta} = require('../..');

test('meta is test.meta', t => {
t.is(meta, test.meta);
});
Comment on lines +3 to +5
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test necessary? I added it to not have to add two tests for every property as was done for file (checking both meta.property and test.meta.property).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it won't hurt 😄

I'm looking to remove this extra export in #2435.


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/);
});