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

Make test file extensions configurable #1746

Merged
merged 34 commits into from
May 30, 2018

Conversation

greym0uth
Copy link
Contributor

@greym0uth greym0uth commented Mar 19, 2018

Allows for the use of extensions like jsx, ts, etc..

Fixes #631

@greym0uth
Copy link
Contributor Author

Referencing #631

Copy link
Member

@novemberborn novemberborn left a comment

Choose a reason for hiding this comment

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

Thanks for doing the PR! It's not quite the right approach yet, see #631 (comment) for a fuller explanation.

@greym0uth
Copy link
Contributor Author

Gotcha, I'll make the updates and update this PR. Thanks for the filler!

@greym0uth
Copy link
Contributor Author

@novemberborn Looking at the code its a bit above my current knowledge with ava.

@novemberborn
Copy link
Member

@Jaden-Giordano no worries. I'm happy to give pointers if you want?

@greym0uth
Copy link
Contributor Author

greym0uth commented Mar 21, 2018

@novemberborn that would be wonderful!

I was looking through the code for awhile and I understand how the pipeline is initiated. I'm just having trouble determining which tests should have the precompile ran and which tests shouldnt.

Edit:
I just noticed your comment on the issue. I guess im just trying to think of the best way to pass the extension options to the precompiler.

Edit 2:
I was able to figure out a way to pass them along and create deterministic precompilation based on extension parameters. Ill update the pr.

@greym0uth greym0uth force-pushed the comma-delimited-filters branch from 2e68b41 to b54d2c9 Compare March 21, 2018 17:07
@greym0uth
Copy link
Contributor Author

greym0uth commented Mar 21, 2018

There are currently errors I'm attempting to resolve. I keep getting ResolveError: D:\Projects\OpenSource\ava-test\.babelrc: Couldn't find preset "@babel/core" relative to directory when testing ava in a project.

Edit:

There are other issues as you can see in the CI, I'm still trying to figure out why this is happening if you can help maybe figure that out. After this next steps would be the watcher side of things.

@greym0uth
Copy link
Contributor Author

@novemberborn You think you can help me with that?

@novemberborn
Copy link
Member

Hi @Jaden-Giordano, thanks for continuing to work on this. I've been a bit busy lately so it may take a few more days before I can get back to you on this, sorry.

@greym0uth
Copy link
Contributor Author

No worries, I assumed so, thanks for being so helpful so far.

@greym0uth
Copy link
Contributor Author

Update:

So I managed to move past those errors, but now the cli and api tests are not passing, Im assuming this is happening since I added two properties to the config (extensions and babel.extensions), I'll continue to look into what the root cause of these issues are, but if anyone is willing to help who is familiar with the api and cli files help/feedback would be greatly appreciated.

Next steps after fixes:

After this fix, we should be ready to move on to notifying the watch.

Copy link
Member

@novemberborn novemberborn left a comment

Choose a reason for hiding this comment

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

Wow nice work @Jaden-Giordano! Thanks for persisting with this. I've left some hopefully helpful comments.

api.js Outdated
@@ -43,6 +43,12 @@ class Api extends EventEmitter {
const apiOptions = this.options;
runtimeOptions = runtimeOptions || {};

const doNotCompileExtensions = apiOptions.extensions || [];
const babelExtensions = apiOptions.babelConfig.extensions || [];
Copy link
Member

Choose a reason for hiding this comment

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

FYI apiOptions.babelConfig can be null. See:

ava/lib/cli.js

Line 145 in 977cf4f

babelConfig: babelConfigHelper.validate(conf.babel),

and:

return null;

That said when it's not null it'd be good if it could have a default extensions value of ['js']. The validate() function in babel-config should take care of that.

Since these extensions won't change during test runs, perhaps compute them in the constructor?

You may still run into some CI problems since not all tests properly set up the Api instance. It'd be OK to set a default value if fixing the tests proves too much effort.

api.js Outdated
const babelExtensions = apiOptions.babelConfig.extensions || [];

// Combine all extensions possible for testing.
const allExts = [...doNotCompileExtensions, ...babelExtensions];
Copy link
Member

Choose a reason for hiding this comment

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

This will fail CI in Node.js 4, but don't worry about that. We're dropping support since it's pretty much end of life anyway. I just need to open the necessary issues.

Choose a reason for hiding this comment

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

Gotcha

lib/ava-files.js Outdated
@@ -115,9 +116,12 @@ class AvaFiles {
});

if (files.length === 0) {
files = defaultIncludePatterns();
files = defaultIncludePatterns(options.extensions || ['js']);
Copy link
Member

Choose a reason for hiding this comment

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

You've changed defaultIncludePatterns to take a single pattern, but here you're calling it with an array. That's probably why CI is failing. Looks like you need the this.extensionPattern value from below.

Choose a reason for hiding this comment

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

Thanks for this catch!

lib/ava-files.js Outdated

if (process.platform === 'win32') {
// Always use `/` in patterns, harmonizing matching across platforms
pattern = slash(pattern);
}

return handlePaths([pattern], excludePatterns, globOptions);
return handlePaths([pattern], extensions, excludePatterns, globOptions, true);
Copy link
Member

Choose a reason for hiding this comment

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

That last true parameter seems unused?

Choose a reason for hiding this comment

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

Alright I removed that.

@@ -22,7 +22,7 @@ function validate(conf) {
return {testOptions: {}};
}

if (!conf || typeof conf !== 'object' || !conf.testOptions || typeof conf.testOptions !== 'object' || Array.isArray(conf.testOptions) || Object.keys(conf).length > 1) {
if (!conf || typeof conf !== 'object' || !conf.testOptions || typeof conf.testOptions !== 'object' || Array.isArray(conf.testOptions)) {
Copy link
Member

Choose a reason for hiding this comment

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

The Object.keys() bit was there to ensure only known keys were used. So we still need that, but it's gotten a bit trickier since more than 1 key is allowed.

It should be possible to provide extensions and not testOptions.

Choose a reason for hiding this comment

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

I'll add another function that checks the keys and return a boolean for whether the keys are valid or not and place it in that checks place.

@@ -30,7 +30,11 @@ class CachingPrecompiler {
this.transform = this._createTransform();
}

precompileFile(filePath) {
precompileFile(filePath, excludeExtensions) {
if (excludeExtensions.includes(path.extname(filePath))) {
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 this should be guarded against in the calling code.

Choose a reason for hiding this comment

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

Good idea moving this to be checked here https://github.com/avajs/ava/blob/master/api.js#L134

@greym0uth
Copy link
Contributor Author

@novemberborn Thank you for the insight it helped immensely! That fixed most of the issues. It is working if I install from my git repo with this current branch and run it with babel.extensions = ['jsx'] too. Maybe you have some idea what is going on in the CI? With that being said I'm going to go ahead and move on to informing the watcher.

@novemberborn
Copy link
Member

Maybe you have some idea what is going on in the CI?

A newer Babel 7 is being picked up in one of the test jobs, and it's causing test failures. I'm a little surprised at that, to be honest! Feel free to ignore the Travis run with FRESH_DEPS=true, and AppVeyor as well since it doesn't run any other tests after the fresh-deps one fails.

It's going to be a few days before I'll have a chance to look at this. Thanks for your hard work @Jaden-Giordano!

@greym0uth
Copy link
Contributor Author

Sounds good, take you're time.

@novemberborn novemberborn changed the title Fixes #631 Allowing ambiguous extensions in test filenames Make test file extensions configurable Apr 7, 2018
Copy link
Member

@novemberborn novemberborn left a comment

Choose a reason for hiding this comment

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

This is shaping up nicely @Jaden-Giordano!

I'm sure it's on your to-do list but we'll need some tests for the new behavior, too.

api.js Outdated
@@ -43,6 +43,12 @@ class Api extends EventEmitter {
const apiOptions = this.options;
runtimeOptions = runtimeOptions || {};

const doNotCompileExtensions = apiOptions.extensions || [];
const babelExtensions = apiOptions.babelConfig ? apiOptions.babelConfig.extensions || ['js'] : ['js'];
Copy link
Member

Choose a reason for hiding this comment

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

Oh! Since we're no longer targeting Node.js 4, this could be:

const {extensions: doNotCompileExtensions = []} = apiOptions;

I wouldn't set the default 'js' value here, since it depends on whether apiOptions.babelConfig === null. Instead:

const {extensions: babelExtensions = []} = apiOptions.babelConfig || {};

if (!apiOptions.babelConfig && doNotCompileExtensions.length === 0) {
  doNotCompileExtensions.push('js');
}

if (apiOptions.babelConfig && babelExtensions.length === 0) {
  babelExtensions.push('js');
}

Some of this could be done through destructuring but it gets rather difficult to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh alright I see.

api.js Outdated
const babelExtensions = apiOptions.babelConfig ? apiOptions.babelConfig.extensions || ['js'] : ['js'];

// Combine all extensions possible for testing.
const allExts = [...doNotCompileExtensions, ...babelExtensions];
Copy link
Member

Choose a reason for hiding this comment

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

This needs a check to ensure any extension in doNotCompileExtensions is not also listed in babelExtensions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For this im just using concat then filter to filter out any duplicate entries:

const allExtensions = doNotCompileExtensions
    .concat(babelExtensions)
    .filter((ext, i, self) => self.indexOf(ext) === i);

return false;
}
}
return true;
Copy link
Member

Choose a reason for hiding this comment

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

How about:

return Object.keys(conf).every(key => key === 'extensions' || key === 'testOptions')`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So elegant!

throw new Error(`${colors.error(figures.cross)} Unexpected Babel configuration for AVA. See ${chalk.underline('https://github.com/avajs/ava/blob/master/docs/recipes/babel.md')} for allowed values.`);
}

if (conf.extensions === undefined) {
conf.extensions = ['js'];
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 it might be easier not to set a default here, but always ensure extensions is an array. Then the code in api.js could be simplified further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm just going to strip away this code, this shouldn't even be needed with the recommendations you gave for api.js.

}

if (!conf || typeof conf !== 'object' || !conf.testOptions || typeof conf.testOptions !== 'object' || Array.isArray(conf.testOptions) || Object.keys(conf).length > 1) {
if (!conf || typeof conf !== 'object' || !conf.testOptions || typeof conf.testOptions !== 'object' || Array.isArray(conf.testOptions) || !validateKeys(conf)) {
Copy link
Member

Choose a reason for hiding this comment

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

This needs to check whether extensions, if present, is an array.

It might be worthwhile creating a let valid = true variable and then splitting the checks up into multiple if conditions, each of which could assign false, before throwing the error only if !valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of multiple if conditions we could use invalid instead then split them up by doing:

let invalid = false;

invalid = !conf || typeof conf !== 'object' || ...;
invalid = ...

if (invalid) {
    throw ...
}

This should be a bit shorter than a bunch of if statements.

test/api.js Outdated
const Api = require('../api');

const test = tap.test;
Copy link
Member

Choose a reason for hiding this comment

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

Why this change? (Same for the other files.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was an oops, I was running tap.only to test specific files and forgot to revert this line.

@novemberborn
Copy link
Member

@Jaden-Giordano if you could merge in (or rebase on) master, that should fix some of the CI issues.

@greym0uth
Copy link
Contributor Author

Yeah will do!

This is an update of a previous commit removing a change that was just being used for testing and shouldn't have been pushed
@greym0uth greym0uth force-pushed the comma-delimited-filters branch from 98c978c to 90de180 Compare April 8, 2018 16:24
@greym0uth greym0uth force-pushed the comma-delimited-filters branch from ffd673b to 226b42b Compare April 16, 2018 21:13
@greym0uth
Copy link
Contributor Author

greym0uth commented Apr 16, 2018

So the test would just end up precompiling and checking if the test passed but thats what the test I wrote already does so I just left that the same but added a test to ava files to look for files with a specified extension. Also what is the transformSpy issue in the lint, can I fix this or just leave it? It seems to be the only issue.

Copy link
Member

@novemberborn novemberborn left a comment

Choose a reason for hiding this comment

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

You actually need to use transformSpy I think, see the inline comment. We also need to exit with an error if the same extension is defined multiple times.

Now something I feel really bad about — we need a separate compilation path for the "do not compile" extensions. Really what that setting means is "don't apply AVA's Babel presets", except that we still want to compile some other enhancements (the compileEnhancements setting), and currently we do that through Babel. I've left a bit more detail inline, below. Please let me know if you want me to push a commit for that, I realize this PR is starting to drag on a bit.

Finally, would you want to add some documentation for this new feature?

Thanks for your hard work on this @Jaden-Giordano. Again apologies for missing some of this issues before. Happy to push some commits if you'd like.

api.js Outdated
}

// Combine all extensions possible for testing. Removing duplicate extensions.
const allExtensions = [...new Set([...doNotCompileExtensions, ...babelExtensions])];
Copy link
Member

Choose a reason for hiding this comment

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

This just needs to ensure there is no overlap between both arrays. You could compare the length of allExtensions to the combined length of doNotCompileExtensions and babelExtensions: it should be equal.

We should probably include the ambiguous extensions in the error message though. Some applications of lodash.difference should do the trick.

Given that we're now throwing an error, could you move this logic into the constructor? Feel free to save allExtensions on the instance.

precompiler.precompileFile(fixture('es2015-source-maps.js'));
const options = transformSpy.lastCall.args[1];
precompiler.precompileFile(fixture('es2015-source-maps.js'), []);
const options = babel.transform.lastCall.args[1];
Copy link
Member

Choose a reason for hiding this comment

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

I suspect you may have had a merge conflict here? Use transformSpy rather than babel.transform in this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh alright gotcha

api.js Outdated
try {
const realpath = fs.realpathSync(file);
let hash = realpath;
if (!doNotCompileExtensions.includes(path.extname(realpath))) {
Copy link
Member

Choose a reason for hiding this comment

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

OK so the tricky thing is we still want to compile enhancements, unless that's also disabled. This currently does use Babel, but it won't apply AVA's regular presets. I think this means instantiating a second precompilation object, and doing a second build of Babel configuration.

I feel bad for not spotting this earlier, and it's a bit tricky to line all this up correctly. Let me know if you want me to push a commit for this instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@novemberborn If you could on this one I can handle the rest, work is a bit busy so I don't have alot of time to work on this. Thanks for all the help!

Copy link
Member

@novemberborn novemberborn left a comment

Choose a reason for hiding this comment

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

If you could on this one I can handle the rest, work is a bit busy so I don't have a lot of time to work on this. Thanks for all the help

@Jaden-Giordano I'm a bit time constrained as well but will tackle the separate "compile enhancements" code path for the regular extensions 👍

I've left some other comments below.

api.js Outdated
this.allExtensions = [...new Set([...doNotCompileExtensions, ...babelExtensions])];

if (this.allExtensions.length !== doNotCompileExtensions.length + babelExtensions.length) {
const duplicates = difference([...doNotCompileExtensions, ...babelExtensions]);
Copy link
Member

Choose a reason for hiding this comment

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

You need to compare two arrays for difference to return anything. But then it actually gives the extensions that are not repeated, so:

const notRepeated = difference(doNotCompileExtensions, babelExtensions);
const duplicates = this.allExtensions.filter(ext => !notRepeated.includes(ext));

I suppose this still needs a test?

readme.md Outdated
@@ -262,6 +262,7 @@ To ignore a file or directory, prefix the pattern with an `!` (exclamation mark)
"@babel/register"
],
"babel": {
"extensions": ["jsx"]
Copy link
Member

Choose a reason for hiding this comment

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

Missing comma.

readme.md Outdated
@@ -282,8 +283,10 @@ Arguments passed to the CLI will always take precedence over the configuration i
- `tap`: if `true`, enables the [TAP reporter](#tap-reporter)
- `snapshotDir`: specifies a fixed location for storing snapshot files. Use this if your snapshots are ending up in the wrong location
- `compileEnhancements`: if `false`, disables [power-assert](https://github.com/power-assert-js/power-assert) — which otherwise helps provide more descriptive error messages — and detection of improper use of the `t.throws()` assertion
- `extensions`: Extensions to opt of of precompilation.
Copy link
Member

Choose a reason for hiding this comment

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

This needs some elaboration. How about:

Extensions of test files that are not precompiled using AVA's Babel presets. Note that files are still compiled to enable power-assert and other features, so you may also need to set compileEnhancements to false if your files are not valid JavaScript.

readme.md Outdated
- `require`: extra modules to require before tests are run. Modules are required in the [worker processes](#process-isolation)
- `babel`: test file specific Babel options. See our [Babel recipe] for more details
- `babel.extensions`: Extensions to include in precompilation.
Copy link
Member

Choose a reason for hiding this comment

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

How about:

Extensions of test files that will be precompiled using AVA's Babel presets.

@novemberborn
Copy link
Member

@Jaden-Giordano I haven't had a chance yet to look into this. I just realized we need some changes with Babel (#1789) so I might try and land that before doing my work here.

Anyway this is super close so once that unblocks we should be good to go.

@novemberborn
Copy link
Member

@Jaden-Giordano just wanted to say I haven't forgotten about this 😄 #1789 is pretty much done so I'm hoping to land that next weekend. I'll then try and get this in as well before cutting a release.

@novemberborn
Copy link
Member

Phew! This is ready now, @Jaden-Giordano please have a look at the commits I just pushed.

I've updated the Babel and ES module recipes. I've fixed the top-level extensions / enhancements-only code path, and we can now handle extensions that have periods in them.

@novemberborn
Copy link
Member

I don't think profile.js script supports custom extensions, but I'll file a follow-up issue for that.

@novemberborn novemberborn force-pushed the comma-delimited-filters branch from e04c3c7 to fd477f1 Compare May 28, 2018 16:16
@novemberborn novemberborn merged commit 3533aba into avajs:master May 30, 2018
@greym0uth
Copy link
Contributor Author

Whoo! This is exciting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants